C# 处置比处置实例寿命更长的对象引用
我目前正在NHibernate之上开发一个工作单元和存储库模式(作为备注:我没有决定使用一个或另一个模式,因此请不要讨论存储库模式对于已经实现一个模式的am ORM的有用性)。 我首先根据文档构建一个singleton(使用IoC配置)Sessionmanager,它创建工作单元实例(为了可读性而删除了私有方法): 可以看到,存储库是使用C# 处置比处置实例寿命更长的对象引用,c#,nhibernate,garbage-collection,idisposable,C#,Nhibernate,Garbage Collection,Idisposable,我目前正在NHibernate之上开发一个工作单元和存储库模式(作为备注:我没有决定使用一个或另一个模式,因此请不要讨论存储库模式对于已经实现一个模式的am ORM的有用性)。 我首先根据文档构建一个singleton(使用IoC配置)Sessionmanager,它创建工作单元实例(为了可读性而删除了私有方法): 可以看到,存储库是使用ISession或IStatelessSession创建的。这两个接口都实现了IDisposable接口,这意味着它们应该被处理掉。因此,我的存储库也实现了ID
ISession
或IStatelessSession
创建的。这两个接口都实现了IDisposable
接口,这意味着它们应该被处理掉。因此,我的存储库也实现了IDisposable
。然而,问题就在这里。理论上,我可以从一个工作单元创建任意多个存储库,例如:
public void UpdatePets(List<Cat> felines, List<Dog> carnines, ISessionManager manager)
{
var uow = manager.Create("Pets", DataAccess.Statefull);
using (var t = uow.OpenTransaction())
{
using (var catRepo = uow.GetRepository<Cat>())
{
catRepo.Add(felines);
t.Commit();
}
}
//Some Businesslogic that forbids using one Transaction to mitigate the problem of an already disposed ISession or IStatelessSession.
using(var t = uow.OpenTransaction())
{
using (var dogRepo = uow.GetRepository<Dog>())
{
dogRepo.Add(carnines);
t.Commit();
}
}
}
第二个存储库将抛出ObjectDisposedexception,因为第二个存储库中我引用的ISession来自我的工作单元,并且当我的第一个存储库离开使用块时,该ISession已经被释放。因此,为了克服这个问题,我将在我的存储库中执行以下操作:
public void Dispose()
{
_session = null;
GC.SuppressFinalize(this);
}
然而,这感觉是错误的,因为我没有正确地处理对我应该处理的对象的引用,而是“闭上眼睛,忘记它”,这在编程中可能永远都不是一个好的解决方案。
因此,我的问题基本上是:“有没有一种好方法可以正确地处理可能比持有引用的对象寿命更长的对象?”您需要建立所有权,并使用它来确定哪个类负责清理每个“对象”。无论有多少其他类与之交互,都应该由所有者(并且只有所有者)调用
Dispose
所有权需要是独占的,并且(包装类除外)嵌套1-所有者的生存期至少需要与拥有的对象一样长。通过遵循这两条规则,我们可以确保当不再需要对象时,Dispose是a)调用once2b)
存储库“拥有”会话是错误的。可以为多个存储库提供相同的会话(因此,如果它们是所有者,我们就失去了独占属性),正如您自己的标题所暗示的,它们的生存期比它短
一些一般的经验法则
- 作为参数传递给实例方法的对象通常不会归实例所有
- 实例创建的对象的生命周期超过单个方法调用的生命周期,该对象将归实例所有
- 由实例方法创建的对象,如果该对象的生存期不超过单个方法调用的生存期,则该对象将归该方法所有
1对于包装器,内部对象将倾向于首先创建,可能会进行配置,然后传递给包装器对象。因此包装器对象的生存期在内部对象之后开始
2不严格要求。Disposables应该允许Dispose被多次调用,但这更多的是为了安全带和背带的防御,而不是一个理想的模式。您需要建立所有权,并使用它来确定哪个类负责清理每个“东西”。无论有多少其他类与之交互,都应该由所有者(并且只有所有者)调用
Dispose
所有权需要是独占的,并且(包装类除外)嵌套1-所有者的生存期至少需要与拥有的对象一样长。通过遵循这两条规则,我们可以确保当不再需要对象时,Dispose是a)调用once2b)
存储库“拥有”会话是错误的。可以为多个存储库提供相同的会话(因此,如果它们是所有者,我们就失去了独占属性),正如您自己的标题所暗示的,它们的生存期比它短
一些一般的经验法则
- 作为参数传递给实例方法的对象通常不会归实例所有
- 实例创建的对象的生命周期超过单个方法调用的生命周期,该对象将归实例所有
- 由实例方法创建的对象,如果该对象的生存期不超过单个方法调用的生存期,则该对象将归该方法所有
1对于包装器,内部对象将倾向于首先创建,可能会进行配置,然后传递给包装器对象。因此包装器对象的生存期在内部对象之后开始
2不严格要求。Disposables应该允许Dispose被多次调用,但这更多是为了安全带和背带的防御,而不是一种理想的模式。为什么您认为存储库应该处理会话?是的,他们持有对会话的引用,但他们并不“拥有”会话。当我学习使用C#编程时,我问过/问过我的每个人都说了两件事:1。如果你有它的参考资料,你要对它负责!2.如果需要处理它,请在您的类不再需要它时立即处理它。称之为对更好的解决方案的盲目性或只是直觉,但我不喜欢在我的课堂上有东西要处理,而没有适当地处理它。我同意@Damien_不信教者,UnitOfWork不能在你的世界里处理课时,不可能存在循环引用-父级可以引用它的子级,子级可以引用父级,但不能同时引用两者,因为它们只是进入了调用彼此的Dispose的循环。您采用了一个合理的通用模式,而将其作为一个盲目的规则来应用。在我看来,对象不应该对其构造函数参数或方法参数负责。这意味着没有列表操作,没有处理外部对象,没有调用公共setter。。。等等,你认为存储库应该如何处理ses
public void UpdatePets(List<Cat> felines, List<Dog> carnines, ISessionManager manager)
{
var uow = manager.Create("Pets", DataAccess.Statefull);
using (var t = uow.OpenTransaction())
{
using (var catRepo = uow.GetRepository<Cat>())
{
catRepo.Add(felines);
t.Commit();
}
}
//Some Businesslogic that forbids using one Transaction to mitigate the problem of an already disposed ISession or IStatelessSession.
using(var t = uow.OpenTransaction())
{
using (var dogRepo = uow.GetRepository<Dog>())
{
dogRepo.Add(carnines);
t.Commit();
}
}
}
private void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
if (_session != null)
_session.Dispose();
}
_disposed = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose()
{
_session = null;
GC.SuppressFinalize(this);
}