We are updating an application to ASPNET .NET 6 and EF Core 6. The application started without DI. Currently, we are creating a new DBContext () for each query. We want to switch to inject the DBContext, however for all current code we want to use PooledDbContextFactory to avoid creating the context on every query.
We would like to know how this implementation should be in an ASPNET application. We are testing by saving the pool to a ThreadLocal <PooledDbContextFactory <DBContext>> object to reuse the pool. Also we have test with a private static readonly Lazy<PooledDbContextFactory<DBEntities>> object.
This is a correct way? Should or can it be done in another way? Is this use correct?
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I have .net core web api project that is using Entity Framework Core to add/edit/delete records from a DB which is working as intended.
It is configured with Dependency Injection with data access and service layers with the web api controllers interacting via the injected service layer.
This part of the solution is working absolutely fine.
However I also have a separate .net core console app that shares the same service layer and consists of a long running task that periodically polls the same data, however if a change is made to the data via the website this is not reflected in the data retrieved on each iteration in the console app. From my research I understand this is due to the data being changed outside the DbContext.
I'm still pretty new to EF so am trying to broaden my understanding. The web api is using a new DB context per web request which is why I am not running into the same issue as the console app and there are lots of articles and discussion on the web app side, but not so much in the context of a console app.
I believe with my 'stale' data that I'm not doing something quite right with respect to DbContext lifespan and I am hanging on to a context longer than is ideal.
Wrapping the operations around a 'using' statement I think would do what I need outside of a DI scenario, but I'm trying to understand how I can achieve the equivalent when using DI? I have managed to get round the problem to a degree by using '.AsNoTracking' in the places where I need it, but was wondering if there is a better way?
Also, any pointers/documentation on using EF Core in a scenario where multiple systems may be accessing the same DB records would be really helpful.
Many thanks in advance.
The better solution could be to separate your projects, you are going to have two different contexts, one created for the Web app and other for the console part.
The problem is the life cycles, while transient and singleton are natural for almost any app, the problem comes with the Scoped type, since it needs something to limit their lifetime, you have two options that I know:
One is to add the EF context as transient, but be aware, since attached objects will not be able to have the same context, each service class will become a unit of work where the context cannot be shared in other calls
The other is leave the context as scoped, and define the lifecycle wherever you need with the help of the IScopedProvider interface, this way you can create you own Scopes, this way you can limit how the context cache will be shared (everything inside that context will share the same context, taking advantage of the cache and management of attached objects. It looks something like this:
public class Bar()
{
public Bar(IServiceScopeFactory scopeFactory)
{
using(var scope = scopeFactory.CreateScope())
{
// Use the scope to ask for services, they will
// share the same scope
scope.ServiceProvider.GetServices<>();
}
}
}
To keep things clear I like to use the scopeFactory to ask for a service that will do all the job, this way this service can inject the context and other services and everyone will be sharing the same context, as if it were the same request in the web app.
I like to use like this:
public class MainClassInConsoleNoContext()
{
private readonly IServiceScopeFactory _scopeFactory;
public MainClassInConsoleNoContext(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public void MyMainAction() {
using(var scope = scopeFactory.CreateScope())
{
scope.ServiceProvider.GetService<MyServiceWithContextAndScope>().MyScopedAction();
}
}
}
public class MyServiceWithContextAndScope()
{
private readonly CoreContext _context;
public MainClassInConsoleNoContext(CoreContext context, OtherService other)
{
...
}
public void MyScopedAction() {
// Here I can use other service, if they both use CoreContext,
// will be the same in all services, since they share the same
// scope
}
}
This question already exists:
"A second operation started on this context" EF core 3.1 concurrency breaking change [duplicate]
Closed 2 years ago.
I'm migrating from netcoreapp 2.1 to 3.1, and I've found a breaking change for EF core 3.1 that I can't resolve. The following worked in 2.1 so simply saying db context isn't thread safe by design and pointing to other questions that don't deal with this nuance does not address the issue at hand.
Previously in 2.1, this worked:
taskList.Add(MethodOne(myRequestObject));
taskList.Add(MethodTwo(myRequestObject));
await Task.WhenAll(taskList);
where both methods only read(never changed) from the db context and look something like this:
private async Task MethodOne(RequestObject myRequestObject)
{
var entity = await DbContext
.MyDbSet
.OrderByDescending(x => x.SomeProperty)
.FirstOrDefaultAsync(x => x.Id == myRequestObject.Id);
if (entity != null)
myRequestObject.SomeRequestProperty = entity.AnotherProperty;
}
}
In 3.1, even when I'm just reading and not changing the entities, the DB context ConcurrencyDetector thinks it has EnterCriticalSection which causes an exception when the second method tries to await on the DbContext:
InvalidOperationException: A second operation started on this context
before a previous operation completed
For baseline sanity, I also tried the following which does work(but is not an ideal solution for the real code of my app):
await MethodOne(myRequestObject);
await MethodTwo(myRequestObject);
So here is my questions:
Is there a way to continue to tell EF core 3.1 to allow concurrency when I know it is safe. this worked in 2.1 so clearly this cannot be dismissed by simply saying db context has never allowed this by design and closing the question as a duplicate. My app has been running happily in production for a long time. Only after migrating to 3.1 has this become an issue. What has changed? Is this change truly impossible to work around or are there exceptions to the claim that the context doesn't allow this? If this worked before when this 'wasn't allowed by design', is it possible that it is similarly not true now as well?
Is there a way to continue to tell EF core 3.1 to allow concurrency when I know it is safe. this worked in 2.1 so clearly this cannot be dismissed by simply saying db context has never allowed this by design and closing the question as a duplicate.
The DB context has never allowed that. I've never seen this kind of code work on any version of .NET Core. If it happened to work in 2.x, it was only "working" in the sense that the code was winning its race conditions and thus just getting lucky. Any change in performance - reduced memory, antivirus software, alternate network routing - could have caused this to fail. It's very important to recognize that the code is wrong and always has been.
My app has been running happily in production for a long time. Only after migrating to 3.1 has this become an issue. What has changed?
Probably some timing in the framework.
If this worked before when this 'wasn't allowed by design', is it possible that it is similarly not true now as well?
The race condition still exists. If your code happens to win the race condition, you won't see that exception.
Is this change truly impossible to work around or are there exceptions to the claim that the context doesn't allow this?
There's no workaround to force a dbcontext to work with multiple simultaneous requests. The normal pattern for doing simultaneous requests is to use multiple dbcontexts, one for each simultaneous request.
A DbContext is cheap to create. By default, if you're injecting them into a scoped service, a new one is created for every request. But you can also create them yourself, while still leveraging .NET Core's DI framework.
Declare your DbContext in Startup.cs as you normally would. But the service (you are injecting services into your controllers, right?) that uses your DbContext can be a singleton instead of scoped, since it doesn't need to be injected with a new DbContext per-request. The background task started by the service can then use a ScopeFactory to create contexts as needed:
using (var scope = ScopeFactory.CreateScope())
using (var db = scope.ServiceProvider.GetRequiredService<MyDbContext>())
{
// do stuff with db
}
so I am currently working on an ASP.NET MVC web application that uses Entity Framework, I'm also using Ninject for Dependency Injection.
So basically, at the moment, this is how I register my DbContext and Services with Ninject.
kernel.Bind<DbContext>().To<MyApplicationContext>().InSingletonScope();
kernel.Bind<IAccountService>().To<AccountService>().InSingletonScope();
kernel.Bind<IRegionService>().To<RegionService>().InSingletonScope();
kernel.Bind<IRoleService>().To<RoleService>().InSingletonScope();
I register them with InSingletonScope, which means that they will only be created once and used throughout the lifetime of the application (at least how I understand it).
Controllers:
private IAccountService _accountService;
public MemberController(IAccountService accountService)
{
_accountService = accountService;
}
However, I have a deep feeling that this singleton scope will cause problem in my web application especially for the Entity Framework's context, due to it being singleton.
I am already facing a minor issue due to this, if I manually update the database using SQL Management Studio, the data in my web application's Entity Framework wouldn't update until I restart the application (seems to be some caching mechanism in EF).
--
However, if I remove the InSingletonScope, I will randomly get errors from EF saying that:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
I understand why this happens because the DbContext initialized by AccountService could be different from say, RegionService. But I have no idea how I can resolve this.
My understanding of Dependency Injection is still very limited, so can anybody please advice?
--
EDIT: I've tried changing to InRequestScope for all the injections, but I'm still getting
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
When trying to insert a new entity with related object (foreign key) from another service in my application. That means they are still using a different DbContext, what is happening?!
FINAL EDIT: Ok I've found the problem, it was my caching mechanism that was caching a previous request, causing the relationship issue on all subsequent request.
The lifetime of some services including DbContext can be configured this way:
services.AddDbContext<ApplicationDbContext>(
options => { options.UseSqlServer("YourConnectionString"); },
ServiceLifetime.Singleton);
REF
Singleton-scope is a very bad idea for your context. Request-scope is what you should be using, as it's essentially a singleton for the life of the request.
As to why you're getting errors when using request-scope, I can't say for sure. Assuming that the entities you're utilizing all originate from the same context type, and that you're properly injecting the context everywhere it's needed, there should never be multiple context instances in play.
EDIT
After re-reading your question, it sounds as if your services are actually initializing the context in their constructors or something. If that's the case, that's your problem. You context should be injected into your services, i.e.:
public class AccountService : IAccountService
{
protected readonly DbContext context;
public AccountService(DbContext context)
{
this.context = context;
}
...
}
Then, Ninject will properly inject the request-scoped instance of MyApplicationContext when newing up any of the services.
Dan, you are creating a bottleneck when you scope a single DBContext for the entire application. Underneath the hood, Entity Framework will handle how many objects you need rather efficiently. If you go deeper into internals, the actual objects contacting the database do the same thing. So your attempt to optimize by making a singleton may actually be creating a very big problem.
I've finally managed to resolve this issue by using InRequestScope instead of InSingletonScope.
Initially, I was still facing the same problem after changing to InRequestScope because of my existing caching mechanism on my services layer.
Thus, all subsequent requests were using the initially-cached entity object, that was why I was getting multiple instances error from EF.
--
If you are still facing the
An entity object cannot be referenced by multiple instances of IEntityChangeTracker
error after changing to InRequestScope, make sure your entities are not somehow cached or stored for subsequent HTTP requests uses.
Generally the work with EF for an looks like this:
Some Web controller calls for a method that has something like this:
using (var ctx = new EntityContext())
{
...
return something;
}
But I guess that in highly loaded application, that has several thousand requests per minute, it might create the problems.
So my question is: maybe it makes sense to manually open connection and keep it alive?
If yes, can anybody share the proper piece of code for such the task?
Thanks in advance
No, don't try to keep opened connections alive. It just won't work, see https://stackoverflow.com/a/9416275/870604.
You're building an ASP.Net MVC application, so the pattern to follow to work with Entity Framework is quite simple in this use case: instantiate a new context for each new controller instance (for example using dependency injection). As a new controller instance is created for each user request, you'll have one new fresh EF context for each request, which is highly desirable.
Don't worry about creating a lot of context instances, it won't create a new DB connection each time thanks to connection pooling.
If you want your application to be scalable, your best option is to also use the async pattern if your version of Entity Framework supports it. See http://blogs.msdn.com/b/webdev/archive/2013/09/18/scaffolding-asynchronous-mvc-and-web-api-controllers-for-entity-framework-6.aspx
EDIT:
Have a look to this overview of the unit of work pattern with ASP.Net MVC and EF.
After closing tag of using element, it automatically close the connection. So you don't want to worry about it. But if you really want to open connection manually, try following (I couldn't test it. May be you need to give connection settings).
ctx.Database.Connection.Open();
I am working on asp.net mvc using code first. I noticed that once i create a new controller, the controller template shows dispose overridden method that just has one job; dispose db variable created at the top of this controller.
I am thinking of changing this to use singleton pattern with my DBContext class.
I tried it and it worked fine. except that i needed sometimes to access database from global.asax. (sometimes) is throws an exception.
Have anyone thought to do the same? Any ideas?
Thank you
personally I would follow a completely different approach, see my answer here: https://stackoverflow.com/a/7474357/559144 I would not use Singleton and would not hardlink MVC which is a UI framework with the DAL (EF in your case).
about not using singleton, let the database handle concurrency; it's one of the things Database servers do the best ;-)
We use EF context as a singleton per http context. I also would not hard link EF with MVC, but you can still be sure that each http context deals with a single EF context instance by using dependency injection (we use Unity).
We also access the context in global asax to do db initialization and seeding for development. Again, you can use a DI container to get an instance of the EF context.
public interface IUnitOfWork : IDisposable
{
int SaveChanges();
}
public class MyEfContext : DbContext, IUnitOfWork
{
// your custom context code
}
Using a singleton-per-http-context lifetime for the IUnitOfWork dependency injection isn't an approach to help deal with concurrency in our case. We do it because when dealing with EF entities, we need to make sure all of the selects / inserts / updates / deletes always happen with the same context instance. EF does not let you attach entities to multiple contexts, and we use singleton per http context for this reason.