【问题标题】:How to inject a service in my DbContext class and have host.MigrateDatabase() still working如何在我的 DbContext 类中注入服务并让 host.MigrateDatabase() 仍然工作
【发布时间】:2021-05-11 13:42:52
【问题描述】:

我有一个有效的 EFCore、.NET5、Blazor WASM 应用程序。 我在Program.Main() 中调用await host.MigrateDatabase(); 以使我的数据库始终保持最新状态。

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

在我的AppDbContext 中,我已覆盖SaveChangesAsync() 以添加和更新CreatedOnUpdatedOn。 我之前在DbContext.SaveChanges overrides behaves unexpected 中提到过这一点。 我还想用 userId 填写 CreatedByUpdatedBy

我有一个IdentityOptions 类来保存用户数据:

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

我已经像这样在 StartUp 中注册了这个类:

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

我将这个IdentityOptions 类注入到其他几个服务中,没有任何问题。
但是当我将它注入我的AppDbContext:

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

MigrateDatabase() 出现错误:
"Cannot resolve scoped service 'IdentityOptions' from root provider."

我尝试了很多在谷歌上搜索到的选项,但找不到适合我的解决方案。 请指教。

更新

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

【问题讨论】:

  • 你是如何注册数据库上下文工厂的? AddDbContextFactory?寿命? (默认为单例)。
  • 感谢伊万的回复。我已经更新了我的问题。我用AddDbContextFactory
  • 您是否尝试过传递不同的生命周期,例如ScopedAddDbContextFactory 的最后一个可选参数)?我不是 DI 专家,但将作用域注入单例听起来有问题(逻辑上)。让我知道这是否有帮助,否则您可能需要自己的 IDbContextFactory&lt;&gt; 实现。但让我们先看看这个。
  • 是的,伊万。添加lifetime: ServiceLifetime.Scoped 解决了我的问题。您可以将其发布为答案吗?我会接受的。
  • 很高兴它帮助了交配。但正如我所说,我离 DI 还很远,所以请随时发布并接受一个自我回答(供未来的读者使用),我很乐意对其进行 UV 处理。

标签: asp.net-core dependency-injection entity-framework-core dbcontext


【解决方案1】:

感谢@IvanStoev(再次)的大力帮助,我找到了答案。
Startup 中添加lifetime: ServiceLifetime.ScopedAddDbContextFactory 解决了我的问题。
现在我可以在SaveChanges 中使用我的 IdentityOptions 类并自动更新我的Created*Updated* 属性。

【讨论】:

    猜你喜欢
    • 2020-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-01
    • 1970-01-01
    相关资源
    最近更新 更多