简体   繁体   English

多次通话'DbContext已被处理错误'

[英]'DbContext has been disposed error' on Multiple Calls

I've setup an API that has a simple getCustomers() method. 我已经设置了一个具有简单getCustomers()方法的API。 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 错误:无法完成操作,因为DbContext已被释放

The error is caused within my CustomerService on the return db.Customers... return db.Customers...上的CustomerService导致错误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 GitHub Repo可以在这里找到: 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 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 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块时:

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.) using基本上是try/finally语法简写,其中finally块在对象上调用.Dispose() 。)

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. 这个值是static ,所以每次调用时它都是MyBookStoreEntity的相同实例。 But when you call it the first time, you .Dispose() it. 但是当你第一次打电话给你时,你.Dispose()它。 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. 基本上,您已经发现了static数据库上下文非常糟糕的原因之一。 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. (并在不同的操作之间共享它们充满了危险。)一个好的经验法则是离散地定义您需要为给定的应用程序操作执行的数据库操作,并以紧密的代码创建/使用/配置您的数据库连接尽可能阻止这些操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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