18

Question

What is the proper way to share an EF DbContext across multiple repositories in an MVC web app? Is it prudent/necessary to do so, what are the pitfalls of doing or not doing this?

Background

Assume:

  • App.MvcSite (Several dozen controllers, multiple areas, etc)
  • App.Services (service layer, many services)
  • App.Data (many repositories, etc)
  • ... etc excluded for simplicity (EF Code First latest)

Research To Date

I seem to find at least two/three schools of thought on SO and the interwebs.

  1. Share/scope your DbContext to the Request so that a single request has a single DbContext shared by all repositories.
  2. Share/scope your DbContext at the service layer -- the service maintains a single DbContext and passes it to each repository as needed.
  3. Do not share a DbContext, since they are cheap let each Repo have its own.

In a small website this is a non-issue which is why most MS and community examples simply don't even address this.

In my experience thus far I have not used finite repositories. I have always had services use a DbContext and directly change it so I didn't need to worry about this. I'm told there is a great benefit to finite repositories from a unit testing perspective... we'll see if it makes the rest of this worthwhile.

My Thoughts

(1) Share/scope your DbContext to the Request

This is interesting as it smartly avoids the pitfall of a singleton context which some developers think is the answer but find DbContext doesn't work that way. But it seems to have a downside in that it assumes all repositories, services, etc are going to be in coordination across an entire request... this is often not the case, right? What if changes are saved by one repo before another completes its work. (outer(inner(inner)))

(2) Share/scope your DbContext at the service layer

This makes more sense to me because each service should be coordinating a specific unit of work (lower case intentional). So if multiple services were used in one request it would be proper (if not required) that each had its own context to the database.

(3) Do not share a DbContext, since they are cheap

This is the way I've always done it... well actually I almost always only had one DbContext per request because only one service was being called. Sometimes it might be two because two services were called by a controller who was coordinating the work. But given my current application, with many finite repositories, each repository having its own context would mean a given request might have 3-10 instances of DbContext. I assume (perhaps incorrectly) that this is problematic.


Repeating the question:

What is the proper way to share an EF DbContext across multiple repositories in an MVC web app? Is it prudent/necessary to do so, what are the pitfalls of doing or not doing this?

kingdango
  • 3,979
  • 2
  • 26
  • 43
  • Use a Dependency Injection container. Ninject is very easy to use in MVC. Install the NuGet package Ninject.MVC - it will create all the bootstrapper code for you. – Dismissile Feb 27 '13 at 14:33
  • We are already using Castle Windsor throughout. I don't think my question is really about dependency injection... it's about Entity Framework and the best way to share a context across repositories. Unless I am missing some key point of your response. – kingdango Feb 27 '13 at 17:21

2 Answers2

5
  • DbContext are cheap, but distributed transactions are not.
  • Objects attached to one context can't be used in another context (if you have object relations)

The easiest way to share a context is to start using an inversion of control container: http://www.codeproject.com/Articles/386164/Get-injected-into-the-world-of-inverted-dependenci

jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • 1
    Thank you, this is a great article on IoC in general -- a topic of which I am familiar. It does have a couple good points about not using long lifetimes to increase performance, which makes sense, but it doesn't provide much specific guidance on DbContext sharing in a web app and definitely nothing concrete for my question. Perhaps you can elaborate in your answer to apply your very good article to my specific question. (Also, we are using Windsor throughout the app so we're beyond simply needed dependency injection) – kingdango Feb 27 '13 at 17:16
  • I don't follow. Since you already are using a container, why don't you just register it as a scoped service? – jgauffin Feb 27 '13 at 17:38
  • I'm not clear what I would be registering. For example I have a SomeService which will use two repositories Repo1 and Repo2, each repo needs an instance of MyDbContext. My question asks what is the best way to handle this, a shared context, separate contexts, and how to best share them if sharing is the best way. – kingdango Feb 27 '13 at 17:47
  • 1
    Just take MyDbContext in the constructor. Check this answer: http://stackoverflow.com/a/9765329/70386. Do exactly the same registration as they do for their UoW, but do it for the dbContext – jgauffin Feb 27 '13 at 18:15
  • That definitely clarifies your point -- use Castle Windsor container to host one instance of the DbContext per request. That may be what I do BUT (BUT BUT BUT) is that really the right way to do it? That will enforce one and only one DbContext per the entire request... do I really want that, is that the best practice? – kingdango Feb 27 '13 at 19:13
  • Regarding you "DbContext are cheap" claim, i have recently asked a question about the lifetime of DbContexts in fat client WPF applications (even using SQL Server CE), "Should DbContexts live for the duration of the life of the process (application) that creates it ?", i would love to hear you taking on this. – Ibrahim Najjar Feb 27 '13 at 23:27
  • What I said was that DbContects are cheap compared to distributed transactions which you are forced to use to have transaction support between several contexts. One context per request is what most use, yes. – jgauffin Feb 28 '13 at 05:23
  • FYI, I am working with the person primarily responsible for Castle Windsor and he advises not to use the PerRequest lifecycle option and we instead manually store and retrieve the context in Http.Items. Otherwise I'm marking this as the answer. – kingdango Feb 28 '13 at 21:34
  • I don't know how he implemented Per Request lifestyle, but that would be the recommendation in any other IoC container. It would also be interesting to hear WHY he recommended against it. – jgauffin Mar 01 '13 at 05:52
1

I would go for a combination between the first two options and regarding your take on the first option, don't let repositories save any changes (that's not the recommended way of doing things, Martin Fowler says that him self) and for that he introduced the Unit of Work pattern.

Ibrahim Najjar
  • 19,178
  • 4
  • 69
  • 95