Entity Framework - Using Containers Simultaneously

In the business logic layer of an Entity Framework-based application, all methods acting on the database should (as I heard) be included in:

using(FunkyContainer fc = new FunkyContainer()) { // do the thing fc.SaveChanges(); } 

Of course, for my convenience, these methods often use each other, so as not to be repeated. The risk that I see here is as follows:

 public void MainMethod() { using(FunkyContainer fc = new FunkyContainer()) { // perform some operations on fc // modify a few objects downloaded from DB int x = HelperMethod(); // act on fc again fc.SaveChanges(); } } public int HelperMethod() { using(FunkyContainer fc2 = new FunkyContainer()) { // act on fc2 an then: fc2.SaveChanges(); return 42; } } 

I don't like it when the fc2 container is created and fc is still open and not yet saved. So this leads to my number one question:

  • Do several containers open at the same time and carelessly apply acceptable practices?

I came to the conclusion that I could write a simple object protected in this way:

 public sealed class FunkyContainerAccessGuard : IDisposable { private static FunkyContainer GlobalContainer { get; private set; } public FunkyContainer Container // simply a non-static adapter for syntactic convenience { get { return GlobalContainer; } } private bool IsRootOfHierarchy { get; set; } public FunkyContainerAccessGuard() { IsRootOfHierarchy = (GlobalContainer == null); if (IsRootOfHierarchy) GlobalContainer = new FunkyContainer(); } public void Dispose() { if (IsRootOfHierarchy) { GlobalContainer.Dispose(); GlobalContainer = null; } } } 

Now use will be as follows:

 public void MainMethod() { using(FunkyContainerAccessGuard guard = new FunkyContainerAccessGuard()) { FunkyContainer fc = guard.Container; // do anything with fc int x = HelperMethod(); fc.SaveChanges(); } } public int HelperMethod() { using(FunkyContainerAccessGuard guard = new FunkyContainerAccessGuard()) { FunkyContainer fc2 = guard.Container; // do anything with fc2 fc2.SaveChanges(); } } 

When HelperMethod is called MainMethod , the GlobalContainer already created and both methods use it, so there is no conflict. In addition, HelperMethod can also be used separately, and then it creates its own container.

However, this seems to me a tremendous excess; So:

  1. Was this problem solved in the form of some class (IoC?), Or at least some good design pattern?

Thanks.

+5
source share
2 answers
  • Do several containers open at the same time and carelessly apply acceptable practices?

As a rule, this is perfectly acceptable, sometimes even necessary, but you must be careful with this. Having several containers at the same time is especially convenient when performing multi-threaded operations. Due to the fact that db works in general, each thread must have its own DbContext, which should not be shared with other threads. The disadvantage of using multiple DbContexts at the same time is that each of them will use a separate db connection, and sometimes a limited one, which can lead to the fact that the application sometimes cannot connect to the database. Another disadvantage is the fact that an entity generated by one DbContext cannot be used with an entity generated by another DbContext. In your example, HelperMethod returns a primitive type, so this is completely safe, but if it returns some object of the object that you would like to assign to MainMethod, for example, to some navigation property of the object created by MainMethod DbContext, then you will get an exception. To overcome this in MainMethod, you will need to use the Id of the object returned by HelperMethod to get this object again, this time using the fc context. On the other hand, there is the advantage of using several contexts: if one context has some problems, for example, he tried to save something that violated the index constant, then all subsequent tests of saving changes will lead to the same exception that the erroneous change is still not expected. If you use several DbContexts, then if someone fails, then the second will work independently - that’s why DbContexts should not live long. Therefore, usually I would say that the best rule of use would be:

  • Each thread must use a separate DbContext
  • All methods executed in one thread must have the same DbContext

Of course, the above applies if the work performed is short. DbContext should not live long. The best example is web applications: each request to the server is processed by a separate thread, and response generation operations usually do not take much time. In this case, all methods that are executed to generate a single response should be shared for convenience by the same DbContext. But each request must be served by a separate DbContext.

  1. Was this problem solved in the form of some class (IoC?), Or at least some good design pattern?

What you need to do is that your DbContext class is a single point stream, but each stream has its own instance of this class. In my opinion, the best way to assure this is in IoC. For example, in Autofac in web applications, I register my DbContext with the following rule:

 builder .RegisterType<MyDbContext>() .InstancePerHttpRequest(); 

Thus, autofac IoC generates one DbContext for each request and transfers the existing instance to the request service flow. You do not need to worry about disposing of your DbContext. Your IoC will do this when your flow is complete.

+3
source

Working in multiple connections at the same time is not the right method most of the time, because:

  • You can get distributed locks that SQL Server cannot solve.
  • You may not see data that was previously written but not yet completed.
  • You cannot share entities across context boundaries (here: methods).
  • More use of resources.
  • It is not possible to complete a transaction across context boundaries (here: methods).

These are very serious flaws. Typically, the best model is to have one context, connection, and transaction for the request processed by the application (HTTP or WCF request). It is very simple to configure and avoid many problems.

It is assumed that EF will be used as a model of living objects. Do not reduce it by reducing it to CRUD.

 static FunkyContainer GlobalContainer 

This does not work. You should not share context between requests. Super dangerous. Consider storing context in HttpContext.Items or something else in your application.

+2
source

All Articles