I’m using IHost to create a service provider in a console app. I have set up user secrets that get loaded when running the project. However when I use the Package Manager Console with commands like Add-Migration user secrets don’t get loaded.
public static async Task Main()
{
IHost host = Host.CreateDefaultBuilder()
.ConfigureServices((context, serviceCollection) =>
{
serviceCollection
.AddDbContext<ApplicationDbContext>();
})
.Build();
// Other code
}
public class ApplicationDbContext(IConfiguration configuration) : DbContext
{
public DbSet<TestRequest> TestRequests { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// This foreach loop doesn't log anything stored in user secrets at design-time while it does at runtime
foreach (var item in configuration.AsEnumerable())
{
Console.WriteLine(item);
}
string connStr = configuration.GetConnectionString("MySql") ??
throw new NullReferenceException("MySQL conncetion string was null"); // Throws at design time
optionsBuilder
.UseMySql(connStr, ServerVersion.AutoDetect(connStr));
}
}
My user secrets ID is set in the .csproj file.
secrets.json
{
"ConnectionStrings:MySql": "Server=localhost;Database=db;Uid=root;Pwd=mypwd;"
}
>Solution :
This issue occurs because configuration data (especially user secrets) isn’t loaded at design-time. When you run your application, Host.CreateDefaultBuilder() loads the configuration and user secrets, but when running commands like Add-Migration, this doesn’t happen.
To fix this, you can create a class that implements the IDesignTimeDbContextFactory<T> interface. This class tells Entity Framework how to create your DbContext at design time.
Here’s how you can do it:
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json") // If needed
.AddUserSecrets<ApplicationDbContextFactory>()
.Build();
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
string connStr = configuration.GetConnectionString("MySql") ??
throw new NullReferenceException("MySQL connection string was null");
optionsBuilder.UseMySql(connStr, ServerVersion.AutoDetect(connStr));
return new ApplicationDbContext(configuration, optionsBuilder.Options);
}
}
Then, update your ApplicationDbContext class like this:
public class ApplicationDbContext : DbContext
{
private readonly IConfiguration _configuration;
public ApplicationDbContext(IConfiguration configuration, DbContextOptions<ApplicationDbContext> options)
: base(options)
{
_configuration = configuration;
}
public DbSet<TestRequest> TestRequests { get; set; }
// You can remove the OnConfiguring method if you don't need it anymore
}
By doing this, the configuration and user secrets will be loaded at design-time as well, so the Add-Migration command will find the connection string.