Reputation: 231
I have project A which is a class library and project B that uses A. Project A is meant to be a generic helper library that can be used across projects (such as B).
The EF Core datacontext and the data entities need to be defined in project B (as they can vary by project) but I need to inject the datacontext in to constructors of service classes in Project A (which handle everything in a generic way). In Project B I have the datacontext
public class MyDataContext : DbContext
{
public MyDataContext(DbContextOptions<MyDataContext> options): base(options)
{
}
public DbSet<Test> Tests { get; set; }
}
In project A I have class UnitOfWork that implements IUnitOfWork. In it's constructor I need to inject the datacontext. However since project A cannot reference project B (project A is meant to be generic), I cannot use the actual name of the datacontext in the parameter list. Since the datacontext inherits from DbContext, I tried
public UnitOfWork(DbContext dc){...}
In the startup of Project B, I have
services.AddDbContext<MyDataContext>(options =>
{
options.UseSqlServer("...<the connection string> ...");
});
services.AddScoped<IUnitOfWork, UnitOfWork>();
Everything compiles but in runtime when the UnitOfWork needs to be created, I get the error
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'.)
The Inner Exception is
InvalidOperationException: Unable to resolve service for type 'Microsoft.EntityFrameworkCore.DbContext' while attempting to activate 'UnitOfWork'.
Any help is deeply appreciated.
EDIT I was asked for the UnitOfWork class details in the comments. So here it is
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;
}
}
Upvotes: 0
Views: 1545
Reputation: 901
Your UnitOfWork
service depends on a DbContext
type, not the derived MyDataContext
type which is registered into DI.
So you have two options:
You can modify the UnitOfWork registration like this (tell the IoC container to instantiate UnitOfWork with MyDataContext):
services.AddScoped<IUnitOfWork>(srp => new UnitOfWork(srp.GetRequiredService<MyDataContext>(), srp));
Or you can register DbContext
into DI as well, so the DI container knows that when someone asks for a DbContext it should return MyDbContext:
services.AddScoped<DbContext, MyDataContext>();
Note that the ServiceProvider
field seems to be unused in your UnitOfWork
class.
Upvotes: 1
Reputation: 231
The solution was to make two changes. First was to explicitly register the service as suggested by @fbede
services.AddScoped<DbContext, MyDataContext>();
Now when we do this, we lose the convenience of setting the DbContextOptionsBuilder
options via the AddDbContext
Extension method.
So we need to override the OnConfiguring
method in the datacontext to set the configurations options we need. For example:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(configuration.GetConnectionString(<key>));
}
Of course, IConfiguration
is injected in the MyDataContext
constructor
Upvotes: 0