Igo*_*rek 12 .net dependency-injection idisposable inversion-of-control autofac
我对使用Autofac的实现中的Dispose()方法有点困惑IDisposable
说我的对象有一定的深度:
Controller取决于IManager;Manager取决于IRepository;Repository取决于ISession;ISession是IDisposable.这导致以下对象图:
new Controller(
new Manager(
new Repository(
new Session())));
Run Code Online (Sandbox Code Playgroud)
我是否还需要使我的Manager和Repository实现IDisposable,并在Controller中调用Manager.Dispose(),在Manager中调用Repository.Dispose()等,或者Autofac会自动知道我的调用堆栈中哪些对象需要正确处理?Controller对象已经是IDisposable,因为它派生自基本ASP.NET Web API控制器
Ste*_*ven 27
资源的一般规则是:
拥有该资源的人负责处理它.
这意味着如果一个类拥有一个资源,它应该以与它创建它相同的方法处理它,或者如果这不可能,这通常意味着拥有类必须实现IDisposable,因此它可以在其Dispose方法中处理资源.
但重要的是要注意,一般来说,如果一个类负责创建它,那么它只拥有一个资源.但是当注入资源时,这意味着该资源在消费者之前就已经存在了.消费者没有创建资源,在这种情况下应该不处理它.虽然我们可以将资源的所有权传递给使用者(并在类的文档中传递所有权的传递),但通常不应传递所有权,因为这会使代码复杂化并使应用程序非常脆弱.
虽然在某些情况下转移对象所有权的策略可能有意义,例如对于可重用API(如System.IO.StreamReader)的类型,在处理作为对象图的一部分的组件时,它总是很糟糕(我们所谓的注入式).我将在下面解释原因.
因此,即使您Controller依赖于需要处理的依赖关系,您的控制器也不应该丢弃它们.原因很简单:
ObjectDisposedException抛出.IDisposable在该抽象上实现.这意味着这种抽象漏掉了实现细节 ; 这违反了依赖倒置原则.IDisposable在抽象实现时,您正在泄漏实现细节,因为该抽象的每个实现都不太可能需要确定性处理,因此您在考虑某个实现时定义了抽象.消费者不应该对实施有任何了解,无论是否需要确定性处置.IDisposable也会导致违反接口隔离原则,因为抽象现在包含一个额外的方法(即Dispose),并非所有消费者都需要调用.他们可能不需要打电话,因为 - 我们已经意识到 - 资源可能比消费者更长久.让它IDisposable在那种情况下实现是危险的,因为任何人都可以调用Dispose它导致应用程序中断.如果我们对测试非常严格,这也意味着我们必须测试消费者没有调用 Dispose方法.考虑一下这将导致的额外测试代码.这是需要编写和维护的代码.所以相反,我们应该只让实现实现Dispose.这使得抽象的任何消费者都不会怀疑它是否应该调用IDisposable(因为没有Dispose方法可以调用抽象).
现在,由于只有实现实现,Dispose并且只有您的Composition Root创建了这个实现,因此组合根负责处理它.如果你的DI容器创建了这个资源,它也应该处理它.Autofac实际上会为您做到这一点.你可以轻松测试这个.如果您在不使用DI容器(又名Pure DI)的情况下连接对象图形,则意味着您必须自己将这些对象放置在Composition Root中.
要意识到这会使您的代码更简单.实现IDisposable抽象并让消费者处理其依赖关系将导致IDisposable通过您的系统像病毒一样传播(正如异步编程像病毒一样传播并污染您的代码库).
它展开,因为您总是可以想到需要清理其资源的抽象实现,因此您必须IDisposable在每个抽象上实现.这意味着每个需要一个或多个依赖项的实现也必须实现IDisposable,这会爬上对象图.这为系统中的每个类增加了大量代码和不必要的复杂性.