Entity Framework, DBContext and using() + async?

19,087

Solution 1

Your code:

public Task<tblLanguage> Post(tblLanguage language)
{
    using (var langRepo = new TblLanguageRepository(new Entities()))
    {
        return langRepo.Add(RequestOrganizationTypeEnum, language);
    }
}

is disposing the repository before returning a Task. If you make the code async:

public async Task<tblLanguage> Post(tblLanguage language)
{
    using (var langRepo = new TblLanguageRepository(new Entities()))
    {
        return await langRepo.Add(RequestOrganizationTypeEnum, language);
    }
}

then it will dispose the repository just before the Task completes. What actually happens is when you hit the await, the method returns an incomplete Task (note that the using block is still "active" at this point). Then, when the langRepo.Add task completes, the Post method resumes executing and disposes the langRepo. When the Post method completes, the returned Task is completed.

For more information, see my async intro.

Solution 2

I would go for the 'one DbContext per request' way, and reuse the DbContext within the request. As all tasks should be completed at the end of the request anyway, you can safely dispose it again.

See i.e.: One DbContext per request in ASP.NET MVC (without IOC container)

Some other advantages:

  • some entities might already be materialized in the DbContext from previous queries, saving some extra queries.
  • you don't have all those extra using statements cluttering your code.

Solution 3

If you are using proper n-tiered programming patters, your controller should never even know that a database request is being made. That should all happen in your service layer.

There are a couple of ways to do this. One is to create 2 constructors per class, one that creates a context and one that accepts an already existing context. This way, you can pass the context around if you're already in the service layer, or create a new one if it's the controller/model calling the service layer.

The other is to create an internal overload of each method and accept the context there.

But, yes, you should be wrapping these in a using.

In theory, the garbage collection SHOULD clean these up without wrapping them, but I don't entirely trust the GC.

Solution 4

If you want to keep your method synchronous but you want to save to DB asynchronously, don't use the using statement. Like @danludwig said, it is just a syntactic sugar. You can call the SaveChangesAsync() method and then dispose the context after the task is completed. One way to do it is this:

//Save asynchronously then dispose the context after
context.SaveChangesAsync().ContinueWith(c => context.Dispose());

Take note that the lambda you pass to ContinueWith() will also be executed asynchronously.

Solution 5

I agree with @Dirk Boer that the best way to manage DbContext lifetime is with an IoC container that disposes of the context when the http request completes. However if that is not an option, you could also do something like this:

var dbContext = new MyDbContext();
var results = await dbContext.Set<MyEntity>.ToArrayAsync();
dbContext.Dispose();

The using statement is just syntactic sugar for disposing of an object at the end of a code block. You can achieve the same effect without a using block by simply calling .Dispose yourself.

Come to think of it, you shouldn't get object disposed exceptions if you use the await keyword within the using block:

public async Task<tblLanguage> Post(tblLanguage language)
{
    using (var langRepo = new TblLanguageRepository(new Entities()))
    {
        var returnValue = langRepo.Add(RequestOrganizationTypeEnum, language);
        await langRepo.SaveChangesAsync();
        return returnValue;
    }
}
Share:
19,087

Related videos on Youtube

Objective Coder
Author by

Objective Coder

trying to learn this

Updated on September 15, 2022

Comments

  • Objective Coder
    Objective Coder over 1 year

    There is a thing that's been bugging me for a long time about Entity Framework.

    Last year I wrote a big application for a client using EF. And during the development everything worked great.

    We shipped the system in august. But after some weeks I started to see weird memory leaks on the production-server. My ASP.NET MVC 4 process was taking up all the resources of the machine after a couple of days running (8 GB). This was not good. I search around on the net and saw that you should surround all your EF queries and operations in a using() block so that the context can be disposed.

    In a day I refactored all my code to use using() and this solved my problems, since then the process sits on a steady memory usage.

    The reason I didn't surround my queries in the first place however is that I started my first controllers and repositories from Microsofts own scaffolds included in Visual Studio, these did not surround it's queries with using, instead it had the DbContext as an instance variable of the controller itself.

    First of all: if it's really important to dispose of the context (something that wouldn't be weird, the dbconnection needs to be closed and so on), Microsoft maybe should have this in all their examples!

    Now, I have started working on a new big project with all my learnings in the back of my head and I've been trying out the new features of .NET 4.5 and EF 6 async and await. EF 6.0 has all these async methods (e.g SaveChangesAsync, ToListAsync, and so on).

    public Task<tblLanguage> Post(tblLanguage language)
    {
        using (var langRepo = new TblLanguageRepository(new Entities()))
        {
            return langRepo.Add(RequestOrganizationTypeEnum, language);
        }
    }
    

    In class TblLanguageRepo:

    public async Task<tblLanguage> Add(OrganizationTypeEnum requestOrganizationTypeEnum, tblLanguage language)
    {
        ...
        await Context.SaveChangesAsync();
        return langaugeDb;
    }
    

    However, when I now surround my statements in a using() block I get the exception, DbContext was disposed, before the query has been able to return. This is expected behaviour. The query runs async and the using block is finished ahead of the query. But how should I dispose of my context in a proper way while using the async and await functions of ef 6??

    Please point me in the right direction.

    Is using() needed in EF 6? Why do Microsoft's own examples never feature that? How do you use async features and dispose of your context properly?

  • danludwig
    danludwig about 10 years
    @Steven, is Add awaitable? I thought it just returned a TEntity (assuming langRepo is a DbSet)...
  • Stephen Cleary
    Stephen Cleary about 10 years
    @danludwig: The Add in this case is given in the op's question. It is awaitable (and should really be called AddAsync).