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

Viewed 476

.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://docs.microsoft.com/pl-pl/ef/core/managing-schemas/migrations/applying?tabs=vs#apply-migrations-at-runtime)

How to achieve that only with Simple Injector?

2 Answers

Workaround for this is to install Entity Framework Core .NET Command-line Tools and to update database from it.

Note: Disable all private NuGet repositories in Visual Studio and install tool in Package Manager Console:

dotnet tool install --global dotnet-ef --version 5.0.0-preview.6.20312.4

Now you can enable your private NuGet feeds in Visual Studio.

Then, go to folder, where your context and configuration is and:

dotnet ef database update --connection "Your=Connection;String"

You can also change Connection String using Update-Database:

Update-Database -connection "Your=Connection;String"

But this is workaround. I'm still asking you for solution "inside" Visual Studio IDE, without installing additional CMD tools

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);
    }
}
Related