6

Summary

This question is for a methodology. The answer should be a link to the holy grail in working with contexts for the described scenario.

We have been experiencing different problems in our MVC web application project, related to the use of dbContext.

After reading many question-answer blogs, articles ... including proposals with repositories and injection patterns, Owin, Entity Framework, Ninject, we are still not clear about the right way to work with dbContext’s.

Is there any article, demo, with “The Way” to do it in a more complex application than just “CRUD” operations using separation between MVVC-presentation / Domain Entities / Logic / DataAccess layers, including Identity security handling users and roles permissions?

Description

Previously, our approach was to create dbContext objects when needed in each repository. Soon we discovered errors like “dbContext is disposed” since the connection dies together with the repository function. This makes the retrieved objects “partially available” to the upper layers in the app (using the trick .ToList(), limited because we can access collections and attributes but not later navigation into the object child tables, and so on). Also using 2 contexts from different repositories, we got an exception telling that 2 contexts are trying to register changes to the same object.

Due to timed commitments to deliver prototypes, we created a single static dbContext shared for the whole application, which is called from everywhere when needed (Controllers, Models, Logic, DataAccess, database initializers). We are aware that is a very dirty workaround but it has been working better than the previous approach.

Still with problems: dbContext can handle only 1 async method call at a time, and we can have many calls (eg. userManager.FindByNameAsync - there are only async methods). Exception: “A second operation started on this context before a previous asynchronous operation completed”.

We were thinking about creating the context as the very first step when an action is called in the controller, then to carry this object as “relay race” to every other layer or function called. In this way the connection will live from the “click in the browser” until the response is loaded back on it. But we don’t like the idea that every single function must have an extra parameter “context” just to share the connection through the layers for the entire operation route.

We are sure that we are not the first ones wondering about what is the right way to use contexts.

Application layers

We have these (logical) layers, differents workspaces, but same webapp MVC project, top to down:

  • Views: HTML + Razor + JQuery + CSS. Code here is restricted to the layout, but some HTML might depend on the Role. Method calls are to controllers only, plus utils (like formatting).

  • ViewModels: The data container to be exchanged between Controllers and Views. Classes only define attributes, plus functions to convert to and from Domain entities only (Translators).

  • Controllers: Actions called from the browser result in calls to functions in the Logic layers. Authentication here restricts access to actions or limits inside an action. Controllers avoid using Domain entities but ViewModels, so that to communicate with Logic layer ViewModels translation functions are called.

  • Domain Entities: Used for the logic layer, and used to create database tables by Entity Framework.

  • Logic Classes: A Domain entity has an EntityLogic class with all the operations. These are the core where all the rules that are common and abstracted from specific consumer clients (ViewModels are unknown).

  • Repositories: To access the database. Not sure if we do need this since Domain entities are already mapped to objects in database by Entity Framework.

Typical scenario

  1. The browser calls an action (POST) in the Products controller to edit a product. The ProductViewModel is used as container of the data.

  2. The controller action is restricted to a collection of roles. Inside the action, depending of the role, a different Logic function is called and ProductViewModel is translated to ProductDomainEntity and passed as parameter.

  3. The logic EditProduct function calls others functions in different logic classes and also use localization and security to restrict or filter. The logic may or may not call a Repository to access the data, or to use a global context for all, and deliver the resulting domain entity collections to the Logic.

  4. Based on the results, the logic may or may not try to navigate the results’ children collections. The results are given back to the controller action as domain entity (or collection of), and depending of this results, the controller may call more Logic, or redirect to another action or respond with a View translating the results to the right ViewModel.

Where, when and how to create the dbContext to support the whole operation in the best way?

dbContext handling to make objects always navigable through layers?

UPDATE: All classes within the Logic layer are static. The methods are called from controllers simply like this:

UserLogic.GetCompanyUserRoles(user)

, or

user.GetCompanyRoles()

where GetCompanyRoles() is an extension method for User implemented in UserLogic. Thus, no instances for Logic classes means no constructors to receive a dbContext to use inside its methods.

I want a static method inside a static class to know where to get the instance of the dbContext active to the current HttpRequest.

Could NInject and OnePerRequestHttpModule help with this? Someone who tried?

renzol
  • 183
  • 2
  • 8
  • 2
    Just a few loose comments: if you get disposal errors, that means you're implicitly using lazy loading. **Don't**. Materialize all entries you have to get during the execution of the query. If you need to have the option to make parallel calls to the database, you have to create multiple contexts - `DbContext` is not thread-safe. Global, static context is a bad idea. – Patryk Ćwiek Jul 01 '15 at 12:20
  • I would definitely not make anyone else aware of the `dbContext`, outside of the repository. Honestly, have you considered scrapping EF? In my experience, it doesn't scale well with projects of this size. You have a good abstraction, why not try Dapper or something more friendly – Jonesopolis Jul 01 '15 at 12:24
  • 1
    If you feel the need to "pass around the context to every method", then chances are your code needs a thorough restructuring anyway. You also may want to read about dependency injection. – CodeCaster Jul 01 '15 at 12:24
  • Fast reading. My opinion: multiple contexts are a bad idea. You will always finish having same entity in multiple contexts which is sad and bad. Or you'll stay 100 years in the design phase. http://stackoverflow.com/questions/11197754/entity-framework-one-database-multiple-dbcontexts-is-this-a-bad-idea – Razvan Dumitru Jul 01 '15 at 12:24
  • And two, i have a strange feeling about passing the context to every method. I've seen that once and it was bad. – Razvan Dumitru Jul 01 '15 at 12:25
  • see http://blogs.msdn.com/b/cesardelatorre/archive/2010/03/26/our-brand-new-ddd-n-layer-net-4-0-architecture-guide-book-and-sample-app-in-codeplex.aspx . But beware, it is outdated and full of anti patterns. – daryal Jul 01 '15 at 12:27

2 Answers2

3

I don't believe there is a "Holy Grail" or magic-bullet answer to this or any other problem with EF / DbContexts. Because of that, I also don't believe that there is one definitive answer to your question, and that any answers will be primarily opinion-based. However I have found personally that using a CQRS pattern rather than a repository pattern allows for more control and fewer problems when dealing with EF semantics and quirks. Here are a few links that you may (or may not) find helpful:

https://stackoverflow.com/a/21352268/304832

https://stackoverflow.com/a/21584605/304832

https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91

https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92

http://github.com/danludwig/tripod

Some more direct answers:

...This makes the retrieved objects “partially available” to the upper layers in the app (using the trick .ToList(), limited because we can access collections and attributes but not later navigation into the object child tables, and so on). Also using 2 contexts from different repositories, we got an exception telling that 2 contexts are trying to register changes to the same object.

The solutions to these problems are to 1) eager load all of the child and navigation properties that you will need when initially executing the query instead of lazy loading, and 2) only work with 1 DbContext instance per HTTP request (inversion of control containers can help with this).

Due to timed commitments to deliver prototypes, we created a single static dbContext shared for the whole application, which is called from everywhere when needed (Controllers, Models, Logic, DataAccess, database initializers). We are aware that is a very dirty workaround but it has been working better than the previous approach.

This is actually much worse than a "dirty workaround", as you will start to see very strange and hard to debug errors when you have a static DbContext instance. I am very surprised to hear that this is working better than your previous approach, but it only points out that there are more problems with your previous approach if this one works better.

We were thinking about creating the context as the very first step when an action is called in the controller, then to carry this object as “relay race” to every other layer or function called. In this way the connection will live from the “click in the browser” until the response is loaded back on it. But we don’t like the idea that every single function must have an extra parameter “context” just to share the connection through the layers for the entire operation route

This is what an Inversion of Control container can do for you, so that you don't have to keep passing around instances. If you register your DbContext instance one per HTTP request, you can use the container (and constructor injection) to get at that instance without having to pass it around in method arguments (or worse).

ViewModels: The data container to be exchanged between Controllers and Views. Classes only define attributes, plus functions to convert to and from Domain entities only (Translators).

Little piece of advice: Don't declare functions like this on your ViewModels. ViewModels should be dumb data containers, void of behavior, even translation behavior. Do the translation in your controllers, or in another layer (like a Query layer). ViewModels can have functions to expose derived data properties that are based on other data properties, but without behavior.

Logic Classes: A Domain entity has an EntityLogic class with all the operations. These are the core where all the rules that are common and abstracted from specific consumer clients (ViewModels are unknown).

This could be the fault in your current design. Boiling all of your business rule and logic into entity-specific classes can get messy, especially when dealing with repositories. What about business rules and logic that span entities or even aggregates? Which entity logic class would they belong to?

A CQRS approach pushes you out of this mode of thinking about rules and logic, and more into a paradigm of thinking about use cases. Each "browser click" is probably going to boil down to some use case that the user wants to invoke or consume. You can find out what the parameters of that use case are (for example, which child / navigation data to eager load) and then write 1 (one) query handler or command handler to wrap the entire use case. When you find common subroutines that are part of more than one query or command, you can factor those out into extension methods, internal methods, or even other command and query handlers.

If you are looking for a good place to start, I think that you will get the most bang for your buck by first learning how to properly use a good Inversion of Control container (like Ninject or SimpleInjector) to register your EF DbContext so that only 1 instance gets created for each HTTP request. This should help you avoid your disposal and multi-context exceptions at the very least.

Community
  • 1
  • 1
danludwig
  • 46,965
  • 25
  • 159
  • 237
  • +1 EF is already a repository and UoW in one, abstracting over it is wrapping one heavy abstraction into another - turtles all the way down. Even basic CQS is pretty simple with EF and adds a lot of clarity, you can attach extension methods to `IQueryable` and `DbContext` for queries and commands. – Patryk Ćwiek Jul 01 '15 at 12:30
  • 2
    @Patryk don't discourage anyone from wrapping DbContext in yet another Repository pattern if you don't know the reason. It _can_ be useful. – CodeCaster Jul 01 '15 at 12:32
  • Appreciating what @danludwig said. Now i'm working with CQRS too. I think it's a good approach to abstract everywhere you can. I've adapt this solution to my needs. Sorry to post it like a comment, but i don't really have time to elaborate. https://westcountrydeveloper.wordpress.com/2012/11/01/creating-a-mvc-application-using-unit-of-work-repository-pattern-and-ninject-2/ – Razvan Dumitru Jul 01 '15 at 12:33
  • @CodeCaster Anecdotally, it's usually not *that* useful - and if you want your own abstraction over the data source, you *probably* should wrap something more lightweight. Of course, YMMV and all, I agree there, but I don't like to complicate things without a *really good* reason. – Patryk Ćwiek Jul 01 '15 at 12:34
  • @danludwig, I have been diving deep into your links and discovered interesting new things. Still I want to believe that there is a simpler way to share 1 dbContext per HTTP request without adding lots of extra patterns and complexity. dbContext is for me enough abstraction from the database, so I got rid of the "DataAccess layer" at all. end of story. No extra layers, no extra components .. They all should be able to know THE instance which in use. Let's say, a shared object different per user (session?) accessible from everywhere, living as long as the HTTP request begins and ends. – renzol Jul 02 '15 at 12:17
  • @renzol If your main problem is how to share 1 DbContext across all code within 1 HTTP request, you only need to implement the inversion of control pattern. However in order to do it in a sane and testable way, all of your code components which use the DbContext should constructor-inject it rather than passing it around in method arguments. Meaning, your business logic classes should be dependencies of your controllers, and that the DbContext class should be a dependency of your logic classes. – danludwig Jul 02 '15 at 14:11
0

I always use a BaseController that contains a dbContext and passes it to the logic functions (Extensions i call). That way you only use one context per call and if something fails it will do a rollback.

Example:

Controller1 that inherits BaseController

Controller1 now have access to the property db that is a context

Controller1 contains an action "Action1"

Action1 will call the function "LogicFunctionX(db, value1, Membership.CurrentUserId, true)"

In Action1 you can call other logic functions or even call them inside "LogicFunctionX". Always passing the property db through functions.

To save the context i do it inside the controller (mostly) after calling all the logic functions.

Note: the argument true that i pass in LogicFunctionX is to save the context inside or not. Like:

if(Save)
   db.SaveChanges();

I had several problems before doing this.

Leandro Soares
  • 2,902
  • 2
  • 27
  • 39
  • Interesting technique, Leandro. It would be more or less what I described in:"We were thinking about creating the context as the very first step when an action is called in the controller, then to carry this object as “relay race” to every other layer or function called." But having the dbContext in the base class avoids the "relay race" at least among controllers. Thx for your collaboration. Still I think that it is better to call SaveChanges() in the Logic functions, this can be made after every change and it is not disposing the dbContext until the controller itself is disposed. – renzol Jul 02 '15 at 06:03
  • What if you have the need to call several logic functions? Do you save the context in each one or do you create a single logic function to aggregate the other ones and SaveChanges() after all them? I'm asking because if you don't keep them inside the same context, if an error occurs you will have only some things saved. I know that the application is not supposed to have errors, but... bug happens :) – Leandro Soares Jul 02 '15 at 08:35
  • I do call several logic functions to do different tasks per HTTP request, but usually I rely on 1 to save (update) the very manipulated object and its children to the database. In more complex scenarios, transactions would be a must, I guess. – renzol Jul 02 '15 at 11:43
  • I guess transactions do resolve the problem... But i still prefer using one single context. Well, it should be the developer's choice, everyone has a preference :) – Leandro Soares Jul 02 '15 at 12:50
  • About "dbContext is disposed", probably you are not calling .ToList() on that specific query or using Include() – Leandro Soares Jul 02 '15 at 12:51
  • I do. But consider this case: Controller1 calls to CommonLogic.GetCurrentUser() <-- 1 context is used to retrieve the user data.- Controller1 calls to Class1Logic.Operation1() <- Op1 goes to the db with another context.- Controller1 redirects to Controller2.- Controller2 calls to Class2Logic.Operation2() <- another context is needed.- Controller2 returns View1.- View1 has minor razor code blocks depending on UserLogic.IsInRole(roleX) <- another context.- How .ToList() solves the problem of sharing 1 single dbContext instance? – renzol Jul 02 '15 at 13:41
  • There is 1 major problem to this approach that you may be overlooking: When you call these business logic methods from within controller action methods, it makes those action methods exceedingly difficult to unit test. Decomposing the application responsibilities into interface dependencies and using and IoC container to resolve them makes the application 1) more testable, and 2) easier to reason about. – danludwig Jul 02 '15 at 14:25
  • @danludwig, i can surpass that problem duplicating the logic methods where the second one receives the same parameters but not the context (so you can create a new context inside the second method). That way you can still do unit test to a single method, of course that it's not the most practical way to do it... I'm working with a big ass project for at least one year and i haven't started doing unit tests, when the application is "stable" (near the end) i will look at it and i will try to figure the best way to do unit tests. – Leandro Soares Jul 02 '15 at 14:33
  • @danludwig, I don't understand why using static logic that doesn't own properties, only methods (sharing 1 single dbContext per HttpRequest) makes less testable the application. Responsibilities are not messed up but clearly defined: controllers actions don't make operations over domain entities or database, only minor ops over ViewModels after their translators and returning the right View. If I achieve to make NInject with OnePerRequestHttpModule to work, it is all what I needed. I am searching some article that explains the whole thing, I've found only partial example codes and explanations – renzol Jul 03 '15 at 06:39