How to inject a service in my DbContext class and have host.MigrateDatabase() still working

Viewed 293

I've got a working EFCore, .NET5, Blazor WASM application. I call await host.MigrateDatabase(); in my Program.Main() to have my database always up-to-date.

public static async Task<IHost> MigrateDatabase(this IHost host)
{
    using var scope = host.Services.CreateScope();
    try
    {
        // Get the needed context factory using DI:
        var contextFactory = scope.ServiceProvider.GetRequiredService<IDbContextFactory<AppDbContext>>();
        // Create the context from the factory:
        await using var context = contextFactory.CreateDbContext();
        // Migrate the database:
        await context.Database.MigrateAsync();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw;
    }
    return host;
}

In my AppDbContext I've overridden SaveChangesAsync() to add and update CreatedOn en UpdatedOn. I mentioned this in DbContext.SaveChanges overrides behaves unexpected before. I also want to fill CreatedBy and UpdatedBy with the userId.

I have an IdentityOptions class to hold the user data:

public class IdentityOptions
{
    public string UserId => User.FindFirst(ClaimTypes.NameIdentifier)?.Value; 
    public ClaimsPrincipal User { get; set; }
}

I've registered this class in StartUp like this:

services.AddScoped(sp =>
{
    var context = sp.GetService<IHttpContextAccessor>()?.HttpContext;
    var identityOptions = new IdentityOptions();

    if (context?.User.Identity != null && context.User.Identity.IsAuthenticated)
    {
        identityOptions.User = context.User;
    }
    return identityOptions;
});

I inject this IdentityOptions class into several other services, without any problem.
But when I inject it in my AppDbContext:

public AppDbContext(DbContextOptions<AppDbContext> options, IdentityOptions identityOptions)
    : base(options)
{
    ...
}

I get an error in MigrateDatabase():
"Cannot resolve scoped service 'IdentityOptions' from root provider."

I've been trying numerous options I found googling but can't find a solution that works for me. Please advice.

Update:

services.AddDbContextFactory<AppDbContext>(
    options => options.UseSqlServer(Configuration.GetConnectionString("DbConnection"),
            b => b.MigrationsAssembly("DataAccess"))
#if DEBUG
        .LogTo(Console.WriteLine, new [] {RelationalEventId.CommandExecuted})
        .EnableSensitiveDataLogging()
#endif
);
1 Answers

Thanks to the great help of @IvanStoev (again), I found the answer.
Adding lifetime: ServiceLifetime.Scoped to AddDbContextFactory in Startup solved my problem.
Now I can use my IdentityOptions class in SaveChanges and automatically update my Created* and Updated* properties.

Related