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.
First, DbContext is a lightweight object; it is designed to be used once per business transaction. Making your DbContext a Singleton and reusing it throughout the application can cause other problems, like concurrency and memory leak issues.
In order to avoid that you should always use "scoped" on the DBContext. Correct. Maybe there are cases in which you need a transient EF-Context - but usually you should stick to scoped.
To be able to use scoped services within a singleton, you must create a scope manually. A new scope can be created by injecting an IServiceScopeFactory into your singleton service (the IServiceScopeFactory is itself a singleton, which is why this works).
This is usually caused by different threads using the same instance of DbContext, however instance members are not guaranteed to be thread safe. When concurrent access goes undetected, it can result in undefined behavior, application crashes and data corruption.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With