实体框架上下文和结构图配置

发布于 2024-12-03 20:38:40 字数 4392 浏览 4 评论 0原文

我在 ASP.NET MVC 应用程序中处理实体框架连接时遇到奇怪的问题。

我有简单的结构,例如:

实体:

public class EmployeeReport
{
    public int EmployeeReportId { get; set; }

    public DateTime Created { get; set; }

    public Decimal Hours { get; set; }

    public string Comment { get; set; }

    public int EmployeeId { get; set; }

    public int ContractId { get; set; }

    public int ServiceId { get; set; }

    public virtual ReportContract Contract { get; set; }

    public virtual ReportService Service { get; set; }

    public virtual Employee Employee { get; set; }

}

实体映射器:

public class EmployeeReportMapper : EntityTypeConfiguration<EmployeeReport>
{
    public EmployeeReportMapper()
    {
        ToTable("intranet_employee_reports");
        HasKey(x => x.EmployeeReportId);

        Property(x => x.Created).HasColumnName("Created").IsRequired();
        Property(x => x.Comment).HasColumnName("Comment").IsOptional();
        Property(x => x.Hours).HasColumnName("Hours").IsRequired();

        HasRequired(x => x.Employee).WithMany().HasForeignKey(x => x.EmployeeId);
        HasRequired(x => x.Service).WithMany().HasForeignKey(x => x.ServiceId);
        HasRequired(x => x.Contract).WithMany().HasForeignKey(x => x.ServiceId);
    }
}

DbContext - 接口

public interface IDbContext : IDisposable
{
    IDbSet<EmployeeReport> EmployeeReports { get; }
}

DbContext - 实现

public class IntranetDbContext : DbContext,IDbContext
{
    public IDbSet<EmployeeReport> EmployeeReports { get; set; }
    ...


    public IntranetDbContext() : base("IntranetDb")
    {
        Database.SetInitializer<IntranetDbContext>(null); 
    }

    public void Commit()
    {
        SaveChanges();
    }

    public void ChangeEntityState(object entity, EntityState entityState)
    {
        Entry(entity).State = entityState;
    }

    public void ExecuteSql(string query, SqlParameterCollection parameterCollection)
    {
        Database.ExecuteSqlCommand(query, parameterCollection);
    }


    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        /* Register custom mapping class */
        modelBuilder.Configurations.Add(new EmployeeReportMapper());
         ....
        base.OnModelCreating(modelBuilder);
    }

}

最后我的结构映射配置:

public class CoreRegistry : Registry
{
    public CoreRegistry()
    {
        For<IDbContext>().HttpContextScoped().Use<IntranetDbContext>();
        ...
    }
}

和 Global.asax:

    protected void Application_EndRequest(object sender, EventArgs e)
    {
        ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
    }

好的,现在的问题是,在我的应用程序中我使用标准构造函数依赖注入 或调用 ObjectFactory.GetInstance()。

在我的控制器之一中,我调用服务类,它可以访问 dbcontext 并获取一些实体。

不幸的是我得到了经典的异常:

ObjectContext 实例已被释放,无法再使用 用于需要连接的操作。

这很奇怪,因为在请求期间调用服务,并且所有数据都强制发送到控制器中的客户端...

知道吗,我在哪里出错了?

编辑:

服务代码:

public class EmployeeService : IEmployeeService
{
    /// <summary>
    /// IDbContext reference
    /// </summary>
    private readonly IDbContext _dbContext;

    public EmployeeService(IDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public List<Employee> GetSubordinateEmployees(Employee employee)
    {
        List<Employee> employees = new List<Employee>();

        foreach (var unit in employee.OrganizationUnits.ToList()) /* throw exception*/
        {
            foreach (var childrenUnit in unit.ChildrenUnits)
            {
                employees.AddRange(childrenUnit.Employees);
            }
        }
        return employees.Distinct().ToList();
    }

控制器:

    private readonly IEmployeeService _employeeService;

    public EmployeeReportController(IEmployeeService employeeService)
    {
        _employeeService = employeeService;
    }

    [HttpGet]
    public ActionResult SearchReports()
    {
        List<Employee> employees = _employeeService.GetSubordinateEmployee(IntranetSession.Current.LoggedAccount.Employee).ToList(); // Exception!
       ...

        return View();
    }


}   

I have strange problem with disposing entity framework connection in asp.net mvc application.

I have simple structure for example :

Entity :

public class EmployeeReport
{
    public int EmployeeReportId { get; set; }

    public DateTime Created { get; set; }

    public Decimal Hours { get; set; }

    public string Comment { get; set; }

    public int EmployeeId { get; set; }

    public int ContractId { get; set; }

    public int ServiceId { get; set; }

    public virtual ReportContract Contract { get; set; }

    public virtual ReportService Service { get; set; }

    public virtual Employee Employee { get; set; }

}

Entity mapper :

public class EmployeeReportMapper : EntityTypeConfiguration<EmployeeReport>
{
    public EmployeeReportMapper()
    {
        ToTable("intranet_employee_reports");
        HasKey(x => x.EmployeeReportId);

        Property(x => x.Created).HasColumnName("Created").IsRequired();
        Property(x => x.Comment).HasColumnName("Comment").IsOptional();
        Property(x => x.Hours).HasColumnName("Hours").IsRequired();

        HasRequired(x => x.Employee).WithMany().HasForeignKey(x => x.EmployeeId);
        HasRequired(x => x.Service).WithMany().HasForeignKey(x => x.ServiceId);
        HasRequired(x => x.Contract).WithMany().HasForeignKey(x => x.ServiceId);
    }
}

DbContext - interface

public interface IDbContext : IDisposable
{
    IDbSet<EmployeeReport> EmployeeReports { get; }
}

DbContext - implementation

public class IntranetDbContext : DbContext,IDbContext
{
    public IDbSet<EmployeeReport> EmployeeReports { get; set; }
    ...


    public IntranetDbContext() : base("IntranetDb")
    {
        Database.SetInitializer<IntranetDbContext>(null); 
    }

    public void Commit()
    {
        SaveChanges();
    }

    public void ChangeEntityState(object entity, EntityState entityState)
    {
        Entry(entity).State = entityState;
    }

    public void ExecuteSql(string query, SqlParameterCollection parameterCollection)
    {
        Database.ExecuteSqlCommand(query, parameterCollection);
    }


    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        /* Register custom mapping class */
        modelBuilder.Configurations.Add(new EmployeeReportMapper());
         ....
        base.OnModelCreating(modelBuilder);
    }

}

Finally my structure map configuration :

public class CoreRegistry : Registry
{
    public CoreRegistry()
    {
        For<IDbContext>().HttpContextScoped().Use<IntranetDbContext>();
        ...
    }
}

and Global.asax :

    protected void Application_EndRequest(object sender, EventArgs e)
    {
        ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
    }

Ok, now the problem, in my application i using standard constructor dependency injection
or call ObjectFactory.GetInstance().

In one of my controller i call service class, which has access to dbcontext and fetch some entites.

Unfortunately i get classic exception :

The ObjectContext instance has been disposed and can no longer be used
for operations that require a connection.

This is strange, because service is called during request and all data are forced to client in controller...

Any idea, where I do mistake?

EDIT :

Service code :

public class EmployeeService : IEmployeeService
{
    /// <summary>
    /// IDbContext reference
    /// </summary>
    private readonly IDbContext _dbContext;

    public EmployeeService(IDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public List<Employee> GetSubordinateEmployees(Employee employee)
    {
        List<Employee> employees = new List<Employee>();

        foreach (var unit in employee.OrganizationUnits.ToList()) /* throw exception*/
        {
            foreach (var childrenUnit in unit.ChildrenUnits)
            {
                employees.AddRange(childrenUnit.Employees);
            }
        }
        return employees.Distinct().ToList();
    }

Controller :

    private readonly IEmployeeService _employeeService;

    public EmployeeReportController(IEmployeeService employeeService)
    {
        _employeeService = employeeService;
    }

    [HttpGet]
    public ActionResult SearchReports()
    {
        List<Employee> employees = _employeeService.GetSubordinateEmployee(IntranetSession.Current.LoggedAccount.Employee).ToList(); // Exception!
       ...

        return View();
    }


}   

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

卷耳 2024-12-10 20:38:40

您的代码根本不使用当前的DbContext。问题是:

IntranetSession.Current.LoggedAccount.Employee

其次:

employee.OrganizationUnits.ToList()

存储在会话中的员工加载了已经处理的上下文,但它仍然保留对该上下文的引用。当您加载该员工时,您并没有急于加载他的组织,因此一旦您访问她的 OrganizationUnits ,它将尝试在已处置的上下文上触发延迟加载。

有两种方法可以避免此问题:

  • 立即加载会话中存储的员工需要使用的所有信息。这意味着像 context.Employees.Include(e => e.OrganizationUnits).Single(...) 一样检索员工
  • 仅在会话中存储员工的 ID 并加载您需要的按需数据

如果您愿意要在会话中缓存整个员工,请确保您将通过调用禁用会话中存储的对象的代理创建:

 context.Configuration.ProxyCreationEnabled = false;

它将确保缓存的数据不会保留对已处置上下文的引用(顺便说一句,阻止垃圾收集器收集上下文及其所有引用)对象)。

Your code doesn't use current DbContext at all. The problem is:

IntranetSession.Current.LoggedAccount.Employee

Followed by:

employee.OrganizationUnits.ToList()

Your employee stored in session was loaded with context which is already disposed but it still keeps reference to that context. When you loaded that employee you didn't eager load his organizations so once you access her OrganizationUnits it will try to trigger lazy loading on disposed context.

There are two ways to avoid this problem:

  • Eager load all information you need to use from your employee stored in session. It means retrieving employee like context.Employees.Include(e => e.OrganizationUnits).Single(...)
  • Store only employee's Id in session and load on-demand data you need

If you want to cache whole employee in session make sure that you will disable proxy creation for objects stored in session by calling :

 context.Configuration.ProxyCreationEnabled = false;

It will ensure that cached data will not keep reference to disposed context (which btw. prevent garbage collector to collect context and all its referenced objects).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文