Search code examples
c#asp.net-coreentity-framework-coreentity-framework-5simple-injector

Update-Database using Simple Injector, without services.AddDbContext<>()


.NET Core 3.1, EF Core 5.0 preview 6.

I am using Simple Injector 5.0 to register IMyContextFactory (I'm not using services.AddDbContext<>()).

My MyContext have all constructors set to private because I use ContextFactory without tenantId and sometimes with few different tenantIds at one request, so I make sure that everyone will use IMyContextFactory injection instead of MyContext and to prevent creating new MyContext() in controllers etc.

But how to do Update-Database?

  1. While I do Update-Database I always have error Login failed for user ''., but I can run and connect to database from my WebAPI project without any problem (my ConnectionString is good).
  2. I was able to apply migration at runtime, but I have to add services.AddDbContext<MyContext>(), make constructors for MyContext as public and get this context from scope.ServiceProvider.GetRequiredService<MyContext>() in Program.cs, because my IMyContextFactory was not inside ServiceProvider. (via https://learn.microsoft.com/pl-pl/ef/core/managing-schemas/migrations/applying?tabs=vs#apply-migrations-at-runtime)

How to achieve that only with Simple Injector?


Solution

  • The solutions is to add ConnectionString to your IDesignTimeDbContextFactory implementation:

    // Fix for "Unable to create an object of type 'MyContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728" error while creating migration
    public class MyContextDesignFactory : IDesignTimeDbContextFactory<MyContext>
    {
        private string _appsettingsPath = "./../Path/To/ProjectWithAppsettingsJSON";
    
        public MyContext CreateDbContext(string[] args)
        {
            // In 90% we don't have ASPNETCORE_ENVIRONMENT here, so set fallback to Development
            var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Development";
    
            var config = new ConfigurationBuilder()
                .SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), _appsettingsPath))
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddJsonFile($"appsettings.{environment}.json", optional: true)
                .Build();
    
            Console.WriteLine($"Default ConnectionString comes from appsettings file.{Environment.NewLine}You can override it by using `dotnet ef database update --connection MyConnectionString`");
    
            // Use ConnectionString from appsettings inside WebAPI to fix `Update-Database` command from Package Manager Console
            // We still can override ConnectionString by `dotnet ef database update --connection MyConnectionString`
            var optionsBuilder = new DbContextOptionsBuilder<MyContext>()
                .UseSqlServer(config["ConnectionString"]);
    
            return MyContext.MyContextFactory.GetMyContext(optionsBuilder.Options);
        }
    }