跨项目的 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
我有项目 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