Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Right way to work with dbContext

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?

like image 457
renzol Avatar asked Jul 01 '15 12:07

renzol


1 Answers

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.

like image 198
danludwig Avatar answered Oct 25 '22 15:10

danludwig