实体框架上下文和结构图配置
我在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您的代码根本不使用当前的
DbContext
。问题是:其次:
存储在会话中的员工加载了已经处理的上下文,但它仍然保留对该上下文的引用。当您加载该员工时,您并没有急于加载他的组织,因此一旦您访问她的
OrganizationUnits
,它将尝试在已处置的上下文上触发延迟加载。有两种方法可以避免此问题:
context.Employees.Include(e => e.OrganizationUnits).Single(...)
一样检索员工如果您愿意要在会话中缓存整个员工,请确保您将通过调用禁用会话中存储的对象的代理创建:
它将确保缓存的数据不会保留对已处置上下文的引用(顺便说一句,阻止垃圾收集器收集上下文及其所有引用)对象)。
Your code doesn't use current
DbContext
at all. The problem is:Followed by:
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:
context.Employees.Include(e => e.OrganizationUnits).Single(...)
If you want to cache whole employee in session make sure that you will disable proxy creation for objects stored in session by calling :
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).