Search code examples
c#dependency-injection.net-coredbcontextef-core-3.0

Resolve DbContext by Interface in EFCore 3


What's the correct way to register/resolve a DbContext by an Interface?


Context:

I have multiple web services with a different dbcontext each.

Since I need to write some common functionalities in a shared project, I wrote an interface which is implemented by each dbcontext, so I can have the set of shared functionalities "dbcontext-implementation-agnostic" just injecting the dbcontext interface.

In each project, each dbcontext is already used and injected by its implementation, but I would like to use just its interface in the common features project.

So, in each webservice I would have something like:

services.AddDbContext<IMyDbcontext, MyDbcontext>(options =>
{
    options.UseSqlServer(configuration.GetConnectionString("MyDbContext"));
});
services.AddTransient<ISharedService, SharedService>();

and in the shared project

public class SharedService : ISharedService
{
    public SharedService(IMydbcontext context)
    {
        [...]
    }

    [...]
}

having IMyDbcontext just like

public interface IMyDbcontext
{
    DbSet<SharedSettings> Settings { get; set; }

    int SaveChanges(bool acceptAllChangesOnSuccess);
    int SaveChanges();
    Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default);
    Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}

and I require ISharedService within Startup.cs - Configure() with

app.ApplicationServices.GetService<ISharedService>();

I've been googling a lot and tried different approches but I couldn't find one which worked...

I've tried to use a forwarder, like this: (as suggested here)

services.AddDbContext<MyDbContext>();
services.AddScoped<IMyDbContext>(provider => provider.GetRequiredService<MyDbContext>());

But, while I've no issues resolving the concrete MyDbContext, I get this error whenever I try to resolve IMyDbContext

System.InvalidOperationException: 'Cannot resolve 'Blah.IMyService' from root provider because it requires scoped service 'Blah.IMydbcontext'.'

where IMyService is registered as transient and it's implementation constructor is

public MyService(IMydbcontext context)

I also tried to register the dbcontext like this

services.AddDbContext<IMyDbContext, MyDbContext>();

but then, when I try to resolve MyDbContext i get null, and I can't understand why


Solution

  • As pointed out by /u/AngularBeginner:

    The IMyService requires a scoped IMyDbContext, but within your startups Configure() method you don't have a scope.

    You can make your IDbContext transient (if that's acceptable to you).

    Alternatively you can try to create a temporary scope using the CreateScope() method, then get your IMyService from that new scoped provider.

    Source

    So, I created a scope with IServiceScopeFactory and it did the trick

    var scopeFactory = app.ApplicationServices.GetService<IServiceScopeFactory>();   
    using var scope = scopeFactory.CreateScope();
    var myService = scope.ServiceProvider.GetService<IMyService>();