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.