简体   繁体   中英

How to solve "there is already an open datareader associated with this connection"

The main problem is that when the web app is launched to the internet, when the load is high an exception is raised telling that there is already an opened data reader.

The following are the specs we use:

  • Entityframework 5.0.0
  • MySQL database

Is there a way of solving this problem without the using(){} block? Main problem of this approach is that when closed the using block I can't expand foreign key relations of entityframework objects inside the html view.

I also attach some source code, showing how we keep a single database context through the whole application

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;
        }
    }
}

This answer is specifically related to the issue mentioned in the question about using the loaded entities in an ASP.NET View. The question asks about a way of solving this problem without a using block or disposing of the DbContext , however I am suggesting doing exactly this.

The reason being that it's generally desirable not to use Entity Framework objects in the ASP.NET Views because those objects are a lot more more than just plain POCO objects; they hide logic which allows them to act as a proxy to the underlying database, so they have a hidden dependency on the state of the DbContext which created them.

Here's a contrived example using EF models for Employee and Department with a 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; }
}

If these were used in an ASP.NET application, I would create some separate models which aren't tied to Entity Framework, to be used by ASP.NET. For example:

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; }
}

A few considerations:

  1. According to the MSDN docs, "A DbContext represents a combination of the UnitOfWork and Repository patterns" - https://docs.microsoft.com/en-us/dotnet/api/system.data.entity.dbcontext?redirectedfrom=MSDN&view=entity-framework-6.2.0 - Therefore the DbContext should be short lived as far as possible.

  2. When loading data from the context, related entities can be retrieved using DbSet<>.Include() - https://docs.microsoft.com/en-us/ef/ef6/querying/related-data

  3. Generally speaking, it makes sense to de-couple the 'data' layer from the 'view' layer - for all kinds of reasons, some of which are listed here: https://docs.microsoft.com/en-us/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5 -- this involves mapping between the EF objects and the POCO Models.

The logic which is used to query the DbContext would query the data using EF, and return that data using POCO models so that only logic which deals directly with DbContext has any involvement with the EF objects. For example:

    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;
        }
    }

Being able to use those POCO models, while requiring some extra boilerplate code, provides complete separation between the DbContext and ASP.NET, allowing the data to be used without ASP.NET Views/Controllers being concerned by the lifetime or state of the DbContext .

Sometimes this may look as if this approach violates the 'DRY' principle, however I would point out that EF objects and ViewModel objects exist to solve different problems, and it's not uncommon for the ViewModel objects to take a different shape, or even to require additional fields/attributes which wouldn't be suitable to add to the EF classes.

Lastly, the above uses 'manual' mapping, but if the mappings are really simple and straightforward, then it could make more sense to use AutoMapper instead: Cleanest Way To Map Entity To DTO With Linq Select?

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