【问题标题】:How to solve "there is already an open datareader associated with this connection"如何解决“已经有一个打开的数据读取器与此连接关联”
【发布时间】:2025-12-21 08:10:06
【问题描述】:

主要问题是当网络应用程序启动到互联网时,当负载很高时会引发异常,告知已经有一个打开的数据读取器。

以下是我们使用的规格:

  • 实体框架 5.0.0
  • MySQL 数据库

有没有办法在没有using(){} 块的情况下解决这个问题?这种方法的主要问题是,当关闭 using 块时,我无法在 html 视图中扩展实体框架对象的外键关系。

我还附上了一些源代码,展示了我们如何在整个应用程序中保持单个数据库上下文

public abstract class AbstractService
{
    public Entities db_model
    {
        get
        {
            return DbContext.Instance.db_model;
        }
    }
}

public class DbContext
{
    public Entities db_model = new Entities();
    private static DbContext _dbContext;

    public static DbContext Instance
    {
        get
        {
            if(_dbContext == null)
            {
                _dbContext = new DbContext();
            }
            return _dbContext;
        }
    }
}

【问题讨论】:

  • 不要使用静态上下文!
  • @GertArnold Okey 那么我如何从应用程序的不同部分获取相同的上下文??
  • 别这样。每个请求的上下文是 Web 应用程序的推荐模式。
  • 无法在 html 视图中扩展实体框架对象的外键关系 -- 我强烈建议您不要在 ASP.NET 视图中使用实体框架对象。相反,请尝试将所有单独的字段数据复制到一个新对象(视图模型)中。这将通过使用单独的模型在“数据”层和“视图”层之间保持清晰的分离。这样您就可以在 using 块中正确使用 DbContext。将所有视图数据复制到完全独立的类中是很常见的。

标签: c# mysql asp.net entity-framework


【解决方案1】:

此答案与在 ASP.NET 视图中使用已加载实体的问题中提到的问题特别相关。该问题询问如何在没有using 块或处置DbContext 的情况下解决此问题,但是我建议这样做。

原因是通常不希望在 ASP.NET 视图中使用实体框架对象,因为这些对象不仅仅是普通的 POCO 对象;它们隐藏了允许它们充当底层数据库代理的逻辑,因此它们对创建它们的DbContext 的状态具有隐藏的依赖关系。

这是一个人为的示例,使用 EmployeeDepartment 的 EF 模型和 DbContext

public class CompanyDbContext : DbContext
{
    public DbSet<Department> Departments { get; set; }
    public DbSet<Employee> Employees { get; set; }
}

public class Department
{
    public long Id { get; set; }
    public virtual ICollection<Employee> Employees { get; set; }
}

public class Employee
{
    public long Id { get; set; }
    public long DepartmentId { get; set; }
    public virtual Department Department { get; set; }
}

如果在 ASP.NET 应用程序中使用这些,我将创建一些不绑定到实体框架的单独模型,供 ASP.NET 使用。例如:

public class DepartmentModel
{
    public long Id { get; set; }
    public List<EmployeeModel> Employees { get; set; }
}

public class EmployeeModel
{
    public long Id { get; set; }
    public long DepartmentId { get; set; }
}

一些注意事项:

  1. 根据 MSDN 文档,“A DbContext 表示 UnitOfWork 和 Repository 模式的组合” - https://docs.microsoft.com/en-us/dotnet/api/system.data.entity.dbcontext?redirectedfrom=MSDN&view=entity-framework-6.2.0 - 因此,DbContext 应该是短暂的尽可能。

  2. 从上下文加载数据时,可以使用DbSet&lt;&gt;.Include() - https://docs.microsoft.com/en-us/ef/ef6/querying/related-data检索相关实体

  3. 一般来说,将“数据”层与“视图”层解耦是有意义的——出于各种原因,其中一些在此处列出:https://docs.microsoft.com/en-us/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5——这涉及到EF 对象和 POCO 模型。

用于查询DbContext 的逻辑将使用 EF 查询数据,并使用 POCO 模型返回该数据,这样只有直接处理DbContext 的逻辑才与 EF 对象有任何关系。例如:

    public List<DepartmentModel> GetAllDepartments()
    {
        using (var ctx = new CompanyDbContext())
        {
            // Ensure that related data is loaded
            var departments = ctx.Departments
                .Include(d => d.Employees);

            // Manual mapping by converting into a new set of models to be used by the Views
            var models = departments
                .Select(d => new DepartmentModel
                {
                    Id = d.Id,
                    Employees = d.Employees
                             .Select(e => new EmployeeModel
                             {
                                 Id = e.Id,
                                 DepartmentId = e.DepartmentId
                             })
                             .ToList(),
                })
                .ToList();

            return models;
        }
    }

能够使用这些 POCO 模型,同时需要一些额外的样板代码,提供了 DbContext 和 ASP.NET 之间的完全分离,允许使用数据而无需 ASP.NET 视图/控制器与生命周期或DbContext 的状态。

有时这可能看起来好像这种方法违反了“DRY”原则,但是我要指出,EF 对象和 ViewModel 对象的存在是为了解决不同的问题,并且 ViewModel 对象采用不同的形状并不少见,或者甚至需要不适合添加到 EF 类的其他字段/属性。

最后,上面使用“手动”映射,但如果映射非常简单明了,那么使用 AutoMapper 会更有意义:Cleanest Way To Map Entity To DTO With Linq Select?

【讨论】:

    最近更新 更多