1

I'm wanting to use the async/await pattern in my ASP.NET MVC controller. I'm getting the error

The ObjectContext instance has been disposed and can no longer be used

Ultimately, I'm wanting to cache my result but for now I think the first step is to just figure out how to work with EF and return with async/await

    public class SpeakerController : Controller
    {
        public async Task<ActionResult> Index()
        {
            using (var context = new MultiTenantContext())
            {
                var speakersAll = await context.Speakers.ToListAsync();
                return View("Index", speakersAll);
            }
        }

Solution Found:

private MultiTenantContext context = new MultiTenantContext();

    public async Task<ActionResult> Index()
    {
            var speakersAll = await context.Speakers.ToListAsync();
            return View("Index", speakersAll);
    }

As I said in the comment below, pulling context out of the using causes it not to be disposed when the ToListAsync() actually executes.

Erik Philips
  • 53,428
  • 11
  • 128
  • 150
Peter Kellner
  • 14,748
  • 25
  • 102
  • 188
  • Which line is throws the error? – Carl Jan 15 '16 at 19:00
  • I just found an example that show that I can move the context into the controller like: private MultiTenantContext context = new MultiTenantContext(); and that solved the issue here. Now, I'm moving on to my cache problem – Peter Kellner Jan 15 '16 at 19:02
  • 2
    You should post how you solved the problem as an answer for anyone who may come across your post in the future. – drneel Jan 15 '16 at 19:52
  • I wonder if you solved the problem or hid it under the mat. – Paulo Morgado Jan 15 '16 at 21:37
  • Are you lazy-loading anything in Speaker? – sellotape Jan 15 '16 at 22:53
  • Yes, lazy loading speakers.sessions by having the "virtual" keyword in the list definition. that is, public virtual list<... – Peter Kellner Jan 16 '16 at 00:32
  • @paulomorgado I'm not sure but this URL seems authoritative. http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/async-and-stored-procedures-with-the-entity-framework-in-an-asp-net-mvc-application – Peter Kellner Jan 16 '16 at 02:36
  • The correct way to cache results in Web is to not to. You should concentrate purely on hints to the Http Proxy where the real caching will occur. – Aron Jan 16 '16 at 18:20
  • @Aron please elaborate. Proxies? – Peter Kellner Jan 16 '16 at 22:01

2 Answers2

3

You should avoid using Lazy Loading within web applications, because the goal is to minimize the response time of the HTTP request, and this is best achieved with fewer database requests. Especially with MVC and/or Web API, where you have very granular action methods, it should be possible in most cases to determine exactly what your data requirements are and include these in one or very few requests/queries. This puts less load on your database server and results in better performance for your web requests.

As for using statements and dbcontexts, you shouldn't be instantiating dbcontexts in your controller if you can help it. It's much better to inject them, and let your container determine how to manage their lifetime. Typically you will want to have exactly one dbcontext instance per request, which will be available for all (however few these may be) queries you need EF to make during the life of the request. If you're using StructureMap (my preference), you would achieve this with:

For<DbContext>.HybridHttpOrThreadLocalScoped().Use<MyContext>();

(Note that this syntax is updated with StructureMap 4)

If you're not sure whether you trust my recommendation, consider that EF7 is taking this recommendation further and making it difficult to work with EF7 and MVC 6 without using dependency injection, in an effort to drive developers toward doing the right thing (falling into "the pit of success" as it were). See this EF7/MVC6 tutorial in the official asp.net docs for an example (http://docs.asp.net/projects/mvc/en/latest/tutorials/mvc-with-entity-framework.html).

Excerpt:

Notice that we don’t set any value for Logger and BookContext. The dependency injection (DI) subsystem automatically sets these properties at runtime. DI also handles the object lifetimes, so you don’t need to call Dispose. For more information, see Dependency Injection.

ssmith
  • 8,092
  • 6
  • 52
  • 93
  • already +1'd this but wanted to chime in with this +1 as well. – Julie Lerman Jan 16 '16 at 18:26
  • Thanks @ssmith Any opinion on async or cache? – Peter Kellner Jan 16 '16 at 23:01
  • I'm a fan of both. async I think you have. caching you can do with the [OutputCache] attribute (for the full output) or via a CachedRepository (http://ardalis.com/building-a-cachedrepository-via-strategy-pattern) for the data. – ssmith Jan 17 '16 at 22:56
1

If you're lazy-loading Speaker.Sessions, your Context is long gone by the time it's used, hence the issue.

Try this instead:

await context.Speakers.Include(x => x.Sessions).ToListAsync();

to ensure Sessions is loaded before your Context goes out of scope.

You can use a different pattern; e.g. allowing the Context to live longer, but personally I think very-short-lived Contexts within using() blocks, as you've done, is best, if you don't need some sort of Unit of Work pattern.

While this isn't the issue here - you also shouldn't mix lazy-loading with async-await in EF: Why Does Await Not Appear to Prevent Second Operation on EF Context

Community
  • 1
  • 1
sellotape
  • 8,034
  • 2
  • 26
  • 30
  • Does adding the "virtual" keyword to lists do the same thing as the .Include does? That is, Speakers is defined as a virtual List (not just List) – Peter Kellner Jan 22 '16 at 18:53
  • Not really; they're more complimentary. As a general rule, virtual allows EF to lazy-load the property, and Include asks it to rather eager-load the otherwise-lazy-loaded properties in your Include definition. http://stackoverflow.com/questions/15247614/understanding-code-first-virtual-properties explains the virtual part quite well. – sellotape Jan 22 '16 at 23:02