操作无法完成,因为 DbContext 已被释放 - LifestylePerWcfOperation
The operation cannot be completed because the DbContext has been disposed - LifestylePerWcfOperation
我的 WCF Web 服务目前遇到了一些并发问题。目前负载较小,但预计未来几天会增加很多。
整体设置是WCF,Entity Framework6,依赖注入(Castle Windsor),UnitOfWork & Repository Pattern。
我设置了一个并行命中服务的压力测试,我可以重新创建并发错误,例如....
- 提交数据库事务时报告错误,但无法确定事务在数据库服务器上是成功还是失败。
已成功提交对数据库的更改,但更新对象上下文时出错。 ObjectContext 可能处于不一致状态。内部异常消息:AcceptChanges 无法继续,因为对象的键值与 ObjectStateManager 中的另一个对象冲突。在调用 AcceptChanges 之前确保键值是唯一的。
属性'ID'是对象关键信息的一部分,不可修改
根据我所做的研究,我似乎应该使用 LifestylePerWcfOperation 注入我的 DbContext。目前正在使用 LifestyleTransient 完成。
当我切换到 LifestylePerWcfOperation 并重新运行测试时,我每次都会得到这个:
- 操作无法完成,因为 DbContext 已被释放
所以我真的有两个问题:
- 我假设真正的解决方案是将我的 DbContext 切换到 LifestylePerWcfOperation 是否正确?
- 如果是这样,为什么我的 DbContext 被吞噬了?
这是一些代码:
AppStart.cs:
IocContainer.AddFacility<TypedFactoryFacility>();
IocContainer.Register(Component.For<IRepositoryFactory>().AsFactory());
IocContainer.Register(Component.For<IUnitOfWork>()
.ImplementedBy<UnitOfWork>)
/*.LifestylePerWcfOperation()*/);
IocContainer.Register(Component.For(typeof(IDbContext))
.ImplementedBy(dbContextType)
.DependsOn(Dependency.OnValue("connectionString", String.Format(ConfigurationManager.ConnectionStrings["---"].ConnectionString))));
/*.LifestylePerWcfOperation()*/
IocContainer.Register(Component.For(typeof(IRepository<>))
.ImplementedBy(typeof(Repository<>))
.LifestyleTransient()
/*.LifestylePerWcfOperation()*/);
// Register the actual dbContextType so consuming applications can access it without an interface
IocContainer.Register(Component.For(dbContextType)
.Named(dbContextType.Name)
.DependsOn(Dependency.OnValue("connectionString", String.Format(ConfigurationManager.ConnectionStrings["----"].ConnectionString)));
/*.LifestylePerWcfOperation()*/
UnitOfWork.cs:
public class UnitOfWork : IUnitOfWork
{
private readonly IDbContext _dbContext;
private bool _disposed;
public UnitOfWork(IDbContext dbContext)
{
_dbContext = dbContext;
}
public int Commit()
{
return _dbContext.SaveChanges();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_dbContext.Dispose();
}
_disposed = true;
}
}
}
Repository.cs
public class Repository<T> : IRepository<T> where T : class
{
private readonly IDbSet<T> _dbset;
public Repository(IDbContext dbContext)
{
_dbset = dbContext.Set<T>();
}
public IQueryable<T> Query()
{
return _dbset.AsQueryable();
}
public virtual IEnumerable<T> GetAll()
{
return _dbset.AsEnumerable();
}
public virtual void Add(T entity)
{
_dbset.Add(entity);
}
public virtual void Delete(T entity)
{
_dbset.Remove(entity);
}
public virtual void Delete(ICollection<T> entities)
{
entities.ToList().ForEach(Delete);
}
}
如果需要,我可以添加更多代码。感谢您提供的任何帮助!!
回答您的问题:
1) 您将生活方式更改为 LifestylePerWcfOperation 是正确的
2) 您正在从 UnitOfWork 中处理 DBContext。 DBContext 由 Windsor 注入,因此 Windsor 将对其调用 Dispose。使用 windsor 时,永远不要处置注入的对象,只有当您使用工厂或 Resolve 创建了一个对象时,您才必须释放(而不是处置)该对象,以便 Windsor 可以处置它。
我希望通过 LifeStylePerWcfOperations 和删除 Dispose 代码,代码应该 运行 没问题。
祝你好运,
马尔维恩.
我能够解决这个问题。
修复正是 Marwijn 提到的。修复后,我仍然遇到类似的错误。
最后的修复是获取每一个注册并使其成为 LifestylePerWcfOperation。我注册了一些生命周期较短的对象,因此 DbContext 也被这些对象处理掉了。
如果有人遇到此错误,并且您正在使用 Castle/EF,请不要显式处置 DbContext(让 Castle 执行)并使所有内容都成为 LifestylePerWcfOperation。那应该解决它。然后返回并有选择地重置您不想成为 LifestylePerWcfOperation 的任何对象的生活方式,并确保它不会在您进行时中断。
我的 WCF Web 服务目前遇到了一些并发问题。目前负载较小,但预计未来几天会增加很多。
整体设置是WCF,Entity Framework6,依赖注入(Castle Windsor),UnitOfWork & Repository Pattern。
我设置了一个并行命中服务的压力测试,我可以重新创建并发错误,例如....
- 提交数据库事务时报告错误,但无法确定事务在数据库服务器上是成功还是失败。
已成功提交对数据库的更改,但更新对象上下文时出错。 ObjectContext 可能处于不一致状态。内部异常消息:AcceptChanges 无法继续,因为对象的键值与 ObjectStateManager 中的另一个对象冲突。在调用 AcceptChanges 之前确保键值是唯一的。
属性'ID'是对象关键信息的一部分,不可修改
根据我所做的研究,我似乎应该使用 LifestylePerWcfOperation 注入我的 DbContext。目前正在使用 LifestyleTransient 完成。
当我切换到 LifestylePerWcfOperation 并重新运行测试时,我每次都会得到这个:
- 操作无法完成,因为 DbContext 已被释放
所以我真的有两个问题:
- 我假设真正的解决方案是将我的 DbContext 切换到 LifestylePerWcfOperation 是否正确?
- 如果是这样,为什么我的 DbContext 被吞噬了?
这是一些代码:
AppStart.cs:
IocContainer.AddFacility<TypedFactoryFacility>();
IocContainer.Register(Component.For<IRepositoryFactory>().AsFactory());
IocContainer.Register(Component.For<IUnitOfWork>()
.ImplementedBy<UnitOfWork>)
/*.LifestylePerWcfOperation()*/);
IocContainer.Register(Component.For(typeof(IDbContext))
.ImplementedBy(dbContextType)
.DependsOn(Dependency.OnValue("connectionString", String.Format(ConfigurationManager.ConnectionStrings["---"].ConnectionString))));
/*.LifestylePerWcfOperation()*/
IocContainer.Register(Component.For(typeof(IRepository<>))
.ImplementedBy(typeof(Repository<>))
.LifestyleTransient()
/*.LifestylePerWcfOperation()*/);
// Register the actual dbContextType so consuming applications can access it without an interface
IocContainer.Register(Component.For(dbContextType)
.Named(dbContextType.Name)
.DependsOn(Dependency.OnValue("connectionString", String.Format(ConfigurationManager.ConnectionStrings["----"].ConnectionString)));
/*.LifestylePerWcfOperation()*/
UnitOfWork.cs:
public class UnitOfWork : IUnitOfWork
{
private readonly IDbContext _dbContext;
private bool _disposed;
public UnitOfWork(IDbContext dbContext)
{
_dbContext = dbContext;
}
public int Commit()
{
return _dbContext.SaveChanges();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_dbContext.Dispose();
}
_disposed = true;
}
}
}
Repository.cs
public class Repository<T> : IRepository<T> where T : class
{
private readonly IDbSet<T> _dbset;
public Repository(IDbContext dbContext)
{
_dbset = dbContext.Set<T>();
}
public IQueryable<T> Query()
{
return _dbset.AsQueryable();
}
public virtual IEnumerable<T> GetAll()
{
return _dbset.AsEnumerable();
}
public virtual void Add(T entity)
{
_dbset.Add(entity);
}
public virtual void Delete(T entity)
{
_dbset.Remove(entity);
}
public virtual void Delete(ICollection<T> entities)
{
entities.ToList().ForEach(Delete);
}
}
如果需要,我可以添加更多代码。感谢您提供的任何帮助!!
回答您的问题:
1) 您将生活方式更改为 LifestylePerWcfOperation 是正确的
2) 您正在从 UnitOfWork 中处理 DBContext。 DBContext 由 Windsor 注入,因此 Windsor 将对其调用 Dispose。使用 windsor 时,永远不要处置注入的对象,只有当您使用工厂或 Resolve 创建了一个对象时,您才必须释放(而不是处置)该对象,以便 Windsor 可以处置它。
我希望通过 LifeStylePerWcfOperations 和删除 Dispose 代码,代码应该 运行 没问题。
祝你好运, 马尔维恩.
我能够解决这个问题。
修复正是 Marwijn 提到的。修复后,我仍然遇到类似的错误。
最后的修复是获取每一个注册并使其成为 LifestylePerWcfOperation。我注册了一些生命周期较短的对象,因此 DbContext 也被这些对象处理掉了。
如果有人遇到此错误,并且您正在使用 Castle/EF,请不要显式处置 DbContext(让 Castle 执行)并使所有内容都成为 LifestylePerWcfOperation。那应该解决它。然后返回并有选择地重置您不想成为 LifestylePerWcfOperation 的任何对象的生活方式,并确保它不会在您进行时中断。