14

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.

Dan
  • 971
  • 1
  • 8
  • 22
  • Related: https://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why/10588594#10588594 – Steven Mar 19 '15 at 21:22
  • Related: https://stackoverflow.com/questions/3266295/net-entity-framework-and-transactions/3266481#3266481 – Steven Mar 19 '15 at 21:22
  • I think you are experiencing [Captive Dependencies](http://blog.ploeh.dk/2014/06/02/captive-dependency/). You probably inject your DbContext into a consumer that is registered as singleton, causing the DbContext to become a singleton as well. – Steven Mar 19 '15 at 21:25
  • @Steven are you referring to the null navigational property problem? I've changed every consumer to RequestScope already, so why is it still becoming a singleton? – Dan Mar 19 '15 at 21:35
  • I'm unfamiliar with the navigational property problem, so I can't comment on that, and I can't tell why and if your DbContext is being kept alive by a consumer, since you haven't posted any code that reproduces the problem. Ninject lacks the tools to find this for you; you will have to debug this through. – Steven Mar 19 '15 at 21:38
  • I guess the navigational properties are not initialized because lazy loading is disabled and the data are sometimes loaded through a path featuring an "include" or "load" call, and sometimes through another path. You should post your code in another question – jbl Mar 19 '15 at 22:04
  • Ok, I've fixed the navigational properties, but I'm still getting `An entity object cannot be referenced by multiple instances of IEntityChangeTracker` when trying to insert a new entity with relationship from another service. – Dan Mar 19 '15 at 22:52

4 Answers4

8

The lifetime of some services including DbContext can be configured this way:

services.AddDbContext<ApplicationDbContext>(
    options => { options.UseSqlServer("YourConnectionString"); },
    ServiceLifetime.Singleton);

REF

Arash.Zandi
  • 1,010
  • 2
  • 13
  • 24
Dr. Strangelove
  • 2,725
  • 3
  • 34
  • 61
  • 1
    This will work, but wouldn't be a good idea, especially not in ASP.NET apps. – Shimmy Weitzhandler Aug 02 '20 at 03:40
  • 1
    I know DBContext as Singleton is very bad idea, but in my case, i need to keep my service run even after the request is end due to long processing.. and since dbcontext will disposed after my request end, i tried to change its lifetime to singleton, and it works just what i expected. The question is, why it is so bad? and how do i access dbcontext after request end if not supposed to be singleton? – hphp Nov 24 '20 at 13:53
4

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.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Yes, that's what I do with my services, injecting the DbContext. So are you saying that I should use `InRequestScope()` for all the service registration with Ninject, including EF's DbContext? What other problem will that cause? Will it be OK with the different ObjectContext problem I stated in my original question? – Dan Mar 19 '15 at 20:18
  • For the purposes of a web application, virtually everything should be request-scoped. Since things like controllers are newed up and disposed with each request, you'll always get a fresh copy of your service or whatever that will survive any passing around you do between other areas of your application in order to fulfill the request. – Chris Pratt Mar 19 '15 at 20:25
  • I've tried it, look at my question edit.. Really weird problem where entities' relationships are sometimes not populated (especially first run) and becomes null.. – Dan Mar 19 '15 at 20:56
4

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.

d219
  • 2,707
  • 5
  • 31
  • 36
Gregory A Beamer
  • 16,870
  • 3
  • 25
  • 32
2

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.

Dan
  • 971
  • 1
  • 8
  • 22