操作无法完成,因为 DbContext 已被释放。System.InvalidOperationException

Posted

技术标签:

【中文标题】操作无法完成,因为 DbContext 已被释放。System.InvalidOperationException【英文标题】:The operation cannot be completed because the DbContext has been disposed.System.InvalidOperationException 【发布时间】:2021-03-01 23:54:27 【问题描述】:

我使用 N-Layer Architech 和依赖注入。 我还想在员工列表中列出部门列表。

public List<Employee> ListEmployee()
    
        using (var context = new DatabaseContext())
        
            return context.Set<Employee>().Include("Department").ToList();
        
    

或者我尝试了两种方法但得到了同样的错误。

 public IQueryable<Employee> GetQueryableEmployee()
    
        using (var context = new DatabaseContext())
        
            return context.Set<Employee>().Include("Department").AsQueryable<Employee>();
        
    

我像这样创建了我的表。 部门表

 public int DepartmentID  get; set; 
 public virtual Department Department  get; set; 

和员工表

 public ICollection<Employee> Employees  get; set; 

我使用 Fluent Api 创建的一对多关系

modelBuilder.Entity<Employee>()
              .HasRequired<Department>(e => e.Department)
              .WithMany(c => c.Employees)
             .HasForeignKey<int>(e => e.DepartmentID);

员工控制器

public class EmployeeController : Controller

    private readonly IEmployeeService _employeeService;
    public EmployeeController(IEmployeeService employeeService)
    
        _employeeService = employeeService;
    
    
    public ActionResult Index()
    
        var employee = _employeeService.ListEmployee();
        return View(employee);
    

和查看

@model IEnumerable<Test.Entities.Concreate.Employee>
@foreach (var item in Model) 
<tr>
    <td>
        @html.DisplayFor(modelItem => item.Department.DepartmentName)
    </td>

如何在同一个列表中显示两个表? 我认为需要平衡 DepartmentID 的地方

【问题讨论】:

Fior GetQueryableEmployee 你会有这个例外。因为您已经处理了已生成 IQueryable 的上下文 var employee=_employeeService.GetQueryableEmployee 是这样吗? 是的,这种用法是错误的。您不必每次都创建上下文,而是配置 DI,以便为每个请求创建实例。 builder.RegisterType().As(); builder.RegisterType().As(); 我是这样做的。如何配置 DI? 【参考方案1】:

要利用IQueryable,至少必须将 DbContext 限定在将引用实体的最外层点。这可以通过依赖注入来管理,前提是 DbContext 的生命周期范围为请求,或者由工作单元模式管理。 (using 块在包含 DbContext 的控制器级别限定了一个工作单元)

根据您的 IoC 容器,上下文可以限定为 PerRequest 之类的内容。以 Autofac 为例:

builder.RegisterType<DatabaseContext>().AsSelf().InstancePerRequest();

使用 Dbcontexts,您可能需要考虑将容器配置为使用特定的 Constructor /w 参数作为连接字符串等。

当 DbContext 本身被提供给您的控制器时,您的存储库 (EmployeeService) 将为要注入的 DbContext 声明一个依赖项。

public class EmployeeService

    private readonly DatabaseContext _context = null;

    public EmployeeService(DatabaseContext context)
    
        _context = context ?? throw new ArgumentNullException("context");
    

     public IQueryable<Employee> GetEmployees()
    
        return _context.Employees.AsQueryable();
    

这种存储库方法的优点是您的服务方法只是用于访问数据的包装器。请注意,我没有将.Include() 用于部门,呼叫者可以这样做或根据他们的需要投射员工。

所以在控制器中你可以去:

 var employeesWithDepartments = EmployeeService.Employees
    .Include(x => x.Department)
    .ToList();

或者更好的是,只需将数据投影到您需要的内容上,这将构成一个更简化的查询:

var employeeViewModels = EmployeeService.Employees
    .Select(x => new EmployeeViewModel
    
        Name = x.Name,
        Department = x.Department.Name,
        // ...
    ).ToList();

使用此模式,您可以将“核心”业务逻辑集中在您的存储库/服务中,例如,如果员工具有您应该始终默认考虑的 IsActive 状态:

 public IQueryable<Employee> GetEmployees(bool includeInactive = false)

    var query = context.Employees.AsQueryable();
    if(!includeInactive)
        query = query.Where(x => x.IsActive);
    return query;

调用代码可以使用额外的Where 子句进一步过滤数据,使用Select 执行预测,或者只需进行Count 或进行Any 检查,而无需在存储库/服务上使用其他方法。

【讨论】:

解决了我的 Configuration.LazyLoadingEnabled = false;获取列表的问题,但找不到 IQueryable 的解决方案。

以上是关于操作无法完成,因为 DbContext 已被释放。System.InvalidOperationException的主要内容,如果未能解决你的问题,请参考以下文章

操作无法完成,因为 DbContext 已使用 MVC 4 处置

防止 DbContext 被释放,直到异步任务完成

注入 DbContext 时无法访问 ASP.NET Core 中已释放的对象

azurew网站持续部署 - Web Deploy无法修改目标上的文件“XXX”,因为它已被外部进程锁定

Android:弹出窗口:尝试完成输入事件,但输入事件接收器已被释放

错误“此消息无法支持该操作,因为它已被读取”