简体   繁体   中英

'DbContext has been disposed error' on Multiple Calls

I've setup an API that has a simple getCustomers() method. The endpoint returns data on the first call, but returns an error on a second call.

Error: The operation cannot be completed because the DbContext has been disposed

The error is caused within my CustomerService on the return db.Customers...

Question: Why does this work on the first call, but fail on the second call. How can this be resolved?

GitHub Repo can be found here: https://github.com/ChaseHardin/MyBookStore

Here's a walkthrough of the code:

Controller:

[RoutePrefix("api/customers")]
public class CustomerController : ApiController
{
    private readonly CustomerService _service = new CustomerService();

    [HttpGet, Route("")]
    public virtual IHttpActionResult Get()
    {
        var customers = _service.GetCustomers();
        return Ok(new {customers});
    }
}

Customer Service:

public class CustomerService : BaseService
{
    public List<CustomerViewModel> GetCustomers()
    {
        using (var db = Application.GetDatabaseInstance())
        {
            return db.Customers.Select(AutoMapper.Mapper.Map<CustomerViewModel>).ToList();
        }
    }
}

BaseService

public class BaseService
{
    public BaseService()
    {
        AutoMapperConfiguration();
    }

    public void AutoMapperConfiguration()
    {
        Assembly.GetExecutingAssembly()
                 .GetTypes()
                 .Where(x => x.IsClass && x.Namespace == "MyBookStore.Business.ViewModels")
                 .ForEach(x => System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(x.TypeHandle));

        AutoMapper.Mapper.CreateMap<bool, short?>().ConvertUsing(x => x ? (short)1 : (short)0);
        AutoMapper.Mapper.CreateMap<short, bool>().ConvertUsing(x => x == 1);

        AutoMapper.Mapper.CreateMap<bool, int?>().ConvertUsing(x => x ? 1 : 0);
        AutoMapper.Mapper.CreateMap<int?, bool>().ConvertUsing(x => x.HasValue && x.Value == 1);

        AutoMapper.Mapper.CreateMap<short, int>().ConvertUsing(x => (int)x);
        AutoMapper.Mapper.CreateMap<int, int?>().ConvertUsing(x => x);
    }
}

CustomerViewModel

public class CustomerViewModel
{
    static CustomerViewModel()
    {
        AutoMapper.Mapper.CreateMap<Customer, CustomerViewModel>().ReverseMap();
    }

    public Guid CustomerId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Context Setup:

public class Application
{
    private static readonly MyBookStoreEntity Context = new MyBookStoreEntity();

    public static MyBookStoreEntity GetDatabaseInstance()
    {
        return Context;
    }
}

When you use a using block:

using (var db = Application.GetDatabaseInstance())

The object being "used" will be disposed at the end of the block. ( using is basically syntactic shorthand for a try/finally where the finally block calls .Dispose() on the object.)

And what you're "using" is this value:

private static readonly MyBookStoreEntity Context = new MyBookStoreEntity();

This value is static , so it's the same instance of MyBookStoreEntity every time it's called. But when you call it the first time, you .Dispose() it. So any subsequent calls will be on a disposed object.

Basically, you've discovered one of the reasons why a static database context is a very bad idea. You can still encapsulate your database context into a method like you have, but make the method return a new instance each time:

public static MyBookStoreEntity GetDatabaseInstance()
{
    return new MyBookStoreEntity();
}

Or, if that method isn't really providing any benefit at this point, then just create the context where you need it:

using (var db = new MyBookStoreEntity())

Creating a database context isn't a particularly heavy operation. But keeping them around when you're not using them is. (And sharing them among different operations is fraught with peril.) A good rule of thumb is to discretely define the database operations you need to perform for a given application operation, and to create/use/dispose your database connection in as tight a code block around those operations as possible.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM