Use DbContext in ASP.Net Singleton Injected Class

I need to access my database in the Singleton class created in my Startup class. It seems that the injection directly leads to the placement of the DbContext.

I get the following error:

Unable to access the remote object. Object Name: "MyDbContext".

My question is twofold: why does this not work and how can I access my database in an instance of the singleton class?

Here is my ConfigureServices method in my Startup class:

public void ConfigureServices(IServiceCollection services) { // code removed for brevity services.AddEntityFramework().AddSqlServer().AddDbContext<MyDbContext>( options => { var config = Configuration["Data:DefaultConnection:ConnectionString"]; options.UseSqlServer(config); }); // code removed for brevity services.AddSingleton<FunClass>(); } 

Here is my controller class:

 public class TestController : Controller { private FunClass _fun; public TestController(FunClass fun) { _fun = fun; } public List<string> Index() { return _fun.GetUsers(); } } 

Here is my FunClass:

 public class FunClass { private MyDbContext db; public FunClass(MyDbContext ctx) { db = ctx; } public List<string> GetUsers() { var lst = db.Users.Select(c=>c.UserName).ToList(); return lst; } } 
+23
c # dependency-injection asp.net-core
source share
5 answers

The reason it doesn't work is because the .AddDbContext extension adds it as the scope for each request. The copied to the request is usually what you want, and usually saving changes will be called once per request, and then dbcontext will be placed at the end of the request.

If you really need to use dbcontext inside a singleton , then your FunClass class should probably depend on IServiceProvider and DbContextOptions instead of directly depending on dbcontext , so you can create it yourself.

 public class FunClass { private GMBaseContext db; public FunClass(IServiceProvider services, DbContextOptions dbOptions) { db = new GMBaseContext(services, dbOptions); } public List<string> GetUsers() { var lst = db.Users.Select(c=>c.UserName).ToList(); return lst; } } 

However, my advice would be to carefully consider whether you really need your FunClass for the single, I would avoid it if you have no good reason to make it single.

+17
source share

Refresh

I am fully aware that this decision is not the right way to do this. Please do not do what I did here all these years ago. In fact, do not enter a singleton DbContext at all.

Old answer

The solution was to call AddSingleton with an instance of my class in the method parameter in my launch class:

 services.AddSingleton(s => new FunClass(new MyContext(null, Configuration["Data:DefaultConnection:ConnectionString"]))); 

The solution was to change my DbContext class:

 public class MyContext : IdentityDbContext<ApplicationUser> { private string connectionString; public MyContext() { } public MyContext(DbContextOptions options, string connectionString) { this.connectionString = connectionString; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { // Used when instantiating db context outside IoC if (connectionString != null) { var config = connectionString; optionsBuilder.UseSqlServer(config); } base.OnConfiguring(optionsBuilder); } } 

However, as several people have warned, using a DbContext in a singleton class can be a very bad idea. My use is very limited in real code (not in the FunClass example), but I think that if you do this, it would be better to find other ways.

+2
source share

As already mentioned .AddDbContext extension .AddDbContext adds it as a scope for each request. Therefore, DI cannot instantiate a Scoped object to create a Singleton object.

You must create and dispose of the MyDbContext instance yourself, this is even better, because the DbContext should be disposed of after use as soon as possible. To pass the connection string, you can take Configuration from the Startup class:

 public class FunClass { private DbContextOptions<MyDbContext> _dbContextOptions; public FunClass(DbContextOptions<MyDbContext> dbContextOptions) { _dbContextOptions = dbContextOptions; } public List<string> GetUsers() { using (var db = new MyDbContext(_dbContextOptions)) { return db.Users.Select(c=>c.UserName).ToList(); } } } 

In Startup.cs configure DbContextOptionBuilder and register singleton:

 var optionsBuilder = new DbContextOptionsBuilder<MyDbContext>(); optionsBuilder.UseSqlServer(_configuration.GetConnectionString("DefaultConnection")); services.AddSingleton(new FunClass(optionsBuilder.Options)); 

It's a bit dirty, but it works very well.

0
source share

If you want to use dbContext in the service and you do not need Singleton, do not use Singleton, but only AddTransient!

In the ConfigureServices method in my startup class:

 services.AddTransient<FunClass>(); 

In my FunClass:

 public class FunClass { private IServiceProvider services; public FunClass(IServiceProvider services) { this.services = services; } public List<string> GetUsers() { using (var scope = services.CreateScope()) { var dbContext = scope.ServiceProvider.GetService<MyDbContext>(); var lst = dbContext.Users.Select(c=>c.UserName).ToList(); return lst; } } } 
0
source share

There is no need to overload ctor MyDbContext.

 services.AddSingleton(s=>new FunClass(new MyDbContext(new DbContextOptionsBuilder<MyDbContext>().UseSqlServer(configuration.GetConnectionString("DefaultConnection")).Options))); 
-one
source share

All Articles