Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework - concurrent use of containers

In the Business Logic Layer of an Entity Framework-based application, all methods acting on DB should (as I've heard) be included within:

using(FunkyContainer fc = new FunkyContainer())
{
    // do the thing

    fc.SaveChanges();
}

Of course, for my own convenience often times those methods use each other, for the sake of not repeating myself. The risk I see here is the following:

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 doesn't look good to me, when the container fc2 is created, while fc is still open and has not been saved yet. So this leads to my question number one:

  1. Is having multiple containers open at the same time and acting on them carelessly an acceptable practice?

I came to a conclusion, that I could write a simple guard-styled object like this:

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 the usage would be as following:

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 the HelperMethod is called by MainMethod, the GlobalContainer is already created, and its used by both methods, so there is no conflict. Moreover, HelperMethod can be also used separately, and then it creates its own container.

However, this seems like a massive overkill to me; so:

  1. Has this problem been already solved in form of some class (IoC?) or at least some nice design pattern?

Thank you.

like image 656
Wojciech Kozaczewski Avatar asked Jul 17 '15 13:07

Wojciech Kozaczewski


People also ask

Does DbContext need to be disposed?

Don't dispose DbContext objects. Although the DbContext implements IDisposable , you shouldn't manually dispose it, nor should you wrap it in a using statement. DbContext manages its own lifetime; when your data access request is completed, DbContext will automatically close the database connection for you.

What is DbContext and DbSet in Entity Framework?

DbContext generally represents a database connection and a set of tables. DbSet is used to represent a table.

What is OnModelCreating in Entity Framework?

The DbContext class has a method called OnModelCreating that takes an instance of ModelBuilder as a parameter. This method is called by the framework when your context is first created to build the model and its mappings in memory.

Is EF core thread safe?

Entity Framework DB contexts are not thread safe. If an ASP.NET Core app wishes to process requests in a multi-threaded way while using Entity Framework Core, it's important to carefully manage DbContexts so that a single context isn't used on multiple threads simultaneously.


1 Answers

  1. Is having multiple containers open at the same time and acting on them carelessly an acceptable practice?

Generally this is perfectly acceptable, sometimes even necessary, but you have to be caucious with that. To have multiple containers at the same time is especially handy when doing multithreading operations. Because of how db works generally each thread should have its own DbContext that should not be shared with other threads. Downside to using multiple DbContext at the same time is that each of them will use separate db connection, and sometimes they are limited, what may lead to application occasionally being unable to connect to database. Other downside is the fact that entity generated by one DbContext may not be used with entity generated by other DbContext. In your example HelperMethod returns primitive type, so this is perfectly safe, but if it would return some entity object that in MainMethod you would like to assign for instance to some navigation property of entity created by MainMethod DbContext then you will receive an exception. To overcome this in MainMethod you would have to use Id of entity returned by HelperMethod to retrieve that entity once more, this time with fc context. On the other hand there is an advantage of using multiple contexts - if one context have some troubles, for instance it tried to save something that violated index constaint, then all next trials of saving changes will result in the same exception as the faulty change will still be pending. If you use multiple DbContexts then if one would fail, then second will operate independently - this is why DbContexts should not live long. So generally I would say the best usage rule would be:

  • Each thread should use a separate DbContext
  • All methods that executes on the same thread should share the same DbContext

Of course the above applies if the job to be done is short. DbContext should not live long. The best example would be web applications - there each server request is handled by separate thread and the operations to generate response generally do not take long. In such case all methods executed to generate one response should share for convenience the same DbContext. But each request should be served by separate DbContext.

  1. Has this problem been already solved in form of some class (IoC?) or at least some nice design pattern?

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

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

This way autofac IoC generates one DbContext per request and share existing instance within the request serving thread. You do not need to care here for disposing your DbContext. Your IoC will do this when your thread is over.

like image 176
mr100 Avatar answered Oct 13 '22 19:10

mr100