Is there a way to use AddDbContext<TContext>, to inject a particular implementation of DbContext without actually forcing other layers to depend on context's implementation? Registering DbContext implementation using AddDbContext<Context>, doesn't allow using DbContext as a Service constructor parameter.
``` C#
public class Statup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework().AddSqlServer().AddDbContext
}
}
public class Context : DbContext
{
public DbSet
}
public class Service
{
public Service(DbContext context)
{
// context doesn't get injected, and I don't know what type of context it will be
// it may be Context, it may be TestingContext, DI should take care of that
var items = context.Set
}
}
I tried a lot of DI configurations and couldn't manage this to work.
I would want to wire it up by hand like this, but it doesn't work now.
``` C#
public class Statup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<DbContextOptions>(provider => ...);
services.AddTransient<DbContext, Context>();
}
}
Hey,
Hopefully this may help you in some way. What I'd do in this scenario is:
1) Interface out your Context
public interface IContext
{
DbSet<T> dbSet<T>() where T : class; //This will be how you go about calling your models.
}
2) Implement your interface
public Context : DbContext, IContext
{
public DbSet<T> dbSet<T>() where T : class
{
return this.Set<T>();
}
}
3) Within Startup.cs create an implementation factory which will add your context to DI. Also note we are adding the concrete implementation to DI also using the AddDbContext helper.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<Context>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"])
);
//Other code removed for simplicity
//Add our Interface to DI
services.AddScoped(repoFactory);
}
private Func<IServiceProvider, IContext> repoFactory = x =>
{
//Getting our context out of the container.
var context = x.GetService<Context>();
return context;
};
4) We can now inject our Interface into our controllers
public class HomeController
{
private readonly IContext _context;
public HomeController(IContext context)
{
_context = context;
}
public IActionResult Index()
{
var myModel = _context.dbSet<MODEL>();
}
}
Please let me know if you have any problems with this code, or if it isn't quite what you're looking for.
Cheers :)
Emm, you can't interface DbContext, because it's a class? I'm actually using unit of work pattern, so your approach seems to be on the right track, I will try this out tomorrow. Thanks.
Sorry, that was my stupid mistake, I put DbContext in the wrong place and it should be on the Context class. I've changed it accordingly. Good luck :)
I actually ran into #4441 problem, of contexts being scoped, which results in "object's already disposed" errors. I still baffled on why replacing AddDbContext scoped creation with your own transient AddDbContext extension doesn't work at all.
@ajcvickers could you comment on this one. We do something with scoping on DbContext right?
Closing this as the relevant changes in #4668 have now been merged. AddDbContext still registers the context as scoped if the context type is not yet registered, but a registration with a different lifetime can now be made either before or after with AddTransient or AddSingleton.
Your context class will now need to take a DbContextOptions<TContext> object in its constructor. This is registered as a singleton in the app container by AddDbContext, or it can be created explicitly using DbContextOptions builder.
I think I stumbled on this, I managed to make it work like this:
Assume following classes
```C#
public class AppDbContext : DbContext {...}
public class SpecificDbContext : AppDbContext {...}
Then startup like this:
```C#
var services = new ServiceCollection();
services.AddEntityFramework().AddDbContext<SpecificDbContext>(...));
The EntityFramework uses SpecificDbContext as the injected class. This means that controllers that assumes AppDbContext being there ceases functioning.
In order to use the AppDbContext or even DbContext as a injected class (but still have SpecificDbContext instance there). One has to register it in startup like this:
```C#
services.AddScoped
{
return f.GetService
Or for DbContext injection, use this:
```C#
services.AddScoped<DbContext, SpecificDbContext>(f =>
{
return f.GetService<SpecificDbContext >();
});
This way the injection works with right instance. It won't work correctly with services.AddScoped<AppDbContext, SpecificDbContext>() alone.
Thanks @Ciantic , nice solution.
For:
It won't work correctly with services.AddScoped
() alone.
Why? Because registering AppDbContext requires the EF DbContext service?
Most helpful comment
I think I stumbled on this, I managed to make it work like this:
Assume following classes
```C#
public class AppDbContext : DbContext {...}
public class SpecificDbContext : AppDbContext {...}
The EntityFramework uses
SpecificDbContextas the injected class. This means that controllers that assumesAppDbContextbeing there ceases functioning.In order to use the
AppDbContextor evenDbContextas a injected class (but still have SpecificDbContext instance there). One has to register it in startup like this:```C#(f =>
});
services.AddScoped
{
return f.GetService
This way the injection works with right instance. It won't work correctly with
services.AddScoped<AppDbContext, SpecificDbContext>()alone.