跨项目的 EF 核心数据上下文注入

EF Core Data Context injection across Projects

我有项目 A,它是一个 class 库和使用 A 的项目 B。项目 A 是一个通用的帮助程序库,可以跨项目(例如 B)使用。

EF Core 数据上下文和数据实体需要在项目 B 中定义(因为它们可能因项目而异)但我需要将数据上下文注入到项目 A 中服务 classes 的构造函数中(它以通用方式处理所有事情)。 在项目 B 中,我有 datacontext

public class MyDataContext : DbContext
    {
        public MyDataContext(DbContextOptions<MyDataContext> options): base(options)
        {
        }

        public DbSet<Test> Tests { get; set; }

    }

在项目 A 中,我有 class 实现 IUnitOfWork 的 UnitOfWork。在它的构造函数中,我需要注入数据上下文。但是,由于项目 A 不能引用项目 B(项目 A 是通用的),我不能在参数列表中使用数据上下文的实际名称。由于datacontext继承自DbContext,我试过

public UnitOfWork(DbContext dc){...}

在项目B的启动中,我有

services.AddDbContext<MyDataContext>(options =>
     {
          options.UseSqlServer("...<the connection string> ...");
     });
services.AddScoped<IUnitOfWork, UnitOfWork>();

一切都可以编译,但在运行时需要创建 UnitOfWork 时,出现错误

System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: IUnitOfWork Lifetime: Scoped ImplementationType: UnitOfWork': Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContext' while attempting to activate 'UnitOfWork'.)

内部异常是

InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContext' while attempting to activate 'UnitOfWork'.

非常感谢任何帮助。

编辑 我被要求在评论中提供 UnitOfWork class 的详细信息。所以这里是

public class UnitOfWork : IUnitOfWork
    {
        private readonly DbContext dc;
        private readonly IServiceProvider serviceProvider;

        public UnitOfWork(DbContext dc, IServiceProvider serviceProvider)
        {
            this.dc = dc;
            this.serviceProvider = serviceProvider;
        }
        public void BeginTransaction()
        {
            dc.Database.BeginTransaction();
        }

        public void BeginTransaction(IsolationLevel isolationLevel)
        {
            dc.Database.BeginTransaction(isolationLevel);
        }

        public void CommitTransaction()
        {
            dc.Database.CommitTransaction();
        }

        public void RollbackTransaction()
        {
            dc.Database.RollbackTransaction();
        }

        public bool IsTransactionActive()
        {
            return dc.Database.CurrentTransaction != null;
        }

        public async Task<bool> SaveAsync()
        {
            return await dc.SaveChangesAsync() > 0;
        }

        public bool Save()
        {
            return dc.SaveChanges() > 0;
        }

    }

您的 UnitOfWork 服务依赖于 DbContext 类型,而不是注册到 DI 的派生 MyDataContext 类型。 所以你有两个选择:

您可以像这样修改 UnitOfWork 注册(告诉 IoC 容器用 MyDataContext 实例化 UnitOfWork):

services.AddScoped<IUnitOfWork>(srp => new UnitOfWork(srp.GetRequiredService<MyDataContext>(), srp));

或者您也可以将 DbContext 注册到 DI 中,因此 DI 容器知道当有人请求 DbContext 时它应该 return MyDbContext:

services.AddScoped<DbContext, MyDataContext>();

请注意,ServiceProvider 字段在您的 UnitOfWork class 中似乎未使用。

解决方案是进行两项更改。首先是按照@fbede

的建议显式注册服务
services.AddScoped<DbContext, MyDataContext>();

现在当我们这样做时,我们失去了通过 AddDbContext 扩展方法设置 DbContextOptionsBuilder 选项的便利性。

所以我们需要覆盖datacontext中的OnConfiguring方法来设置我们需要的配置选项。例如:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(configuration.GetConnectionString(<key>));
    }

当然是在MyDataContext构造函数中注入了IConfiguration