C# Autofac:如何在不绕过IoC容器的情况下限制IDisposable对象的生存期

C# Autofac:如何在不绕过IoC容器的情况下限制IDisposable对象的生存期,c#,inversion-of-control,dispose,idisposable,autofac,C#,Inversion Of Control,Dispose,Idisposable,Autofac,我目前正在学习如何使用Autofac,并且我一直坚持以确定性方式处理IDisposable对象。让我先介绍一下情况,然后再陈述我的问题 起始位置: 假设我的对象模型是通过以下接口定义的: interface IApple : IDisposable { void Consume(); } interface IHorse { void Eat(IApple apple); // is supposed to call apple.Consume() } interface

我目前正在学习如何使用Autofac,并且我一直坚持以确定性方式处理
IDisposable
对象。让我先介绍一下情况,然后再陈述我的问题

起始位置: 假设我的对象模型是通过以下接口定义的:

interface IApple : IDisposable
{
    void Consume();
}

interface IHorse
{
    void Eat(IApple apple);   // is supposed to call apple.Consume()
}

interface IHorseKeeper
{
    void FeedHorse();   // is supposed to call horse.Eat(apple)
                        //   where 'horse' is injected into IHorseKeeper
                        //   and 'apple' is generated by IHorseKeeper on-the-fly
}
public IApple
{
   void Consume();
}

public IDisposableApple : IApple, IDisposable
{
}
此外,我定义了一个将用作
IApple
工厂的委托:

delegate IApple AppleFactory;
自动传真配置: 现在,我将按如下方式注册上述类型——注意,我省略了两个类的代码
Apple
Horse
,因为它们的实现非常简单:

var builder = new Autofac.ContainerBuilder();

builder.RegisterType<Apple>().As<IApple>();
builder.RegisterType<Horse>().As<IHorse>();
builder.RegisterType<HorseKeeper>().As<IHorseKeeper>();
builder.RegisterGeneratedFactory<AppleFactory>();
这是我想要的代码类型,因为它完全不受Autofac的影响。只要
AppleFactory
按预期工作,它也可以与另一个IoC容器一起工作

但是,由于Autofac为我处理
AppleFactory
,它将跟踪它为我生成的所有
IApple
对象,因此希望
在容器生命周期结束时自行处置它们。即,生产的
苹果
将被处理两次

我认为将
IApple
注册为
.ExternallyOwned()
不是可行的解决方案,因为在某些情况下,让Autofac更容易处理
IApple
的生存期

需要使用
container.BeginLifetimeScope()
创建嵌套容器,但是我不想在
HorseKeeper.FeedHorse
中使用它,因为
HorseKeeper
依赖于Autofac,我想保持我的代码与IoC无关

问题:
如何以IoC(自动FAC)不可知的方式实现
HorseKeeper.FeedHorse
,同时确保正确处理动态生成的对象?

唯一的方法是使用
外部拥有的
修改器修改Apple注册。这指示Autofac不要跟踪要处理的对象,而是让外部人员(您的代码)处理处理。但正如您所说,您现在必须确保手动处理所有Apple实例,因为您不会从Autofac获得自动帮助

builder.RegisterType<Apple>().As<IApple>().ExternallyOwned();
IApple的消费者现在完全不知道实例是可识别的。在这种情况下,我们将让容器处理处置


因此,我的结论是,作为Apple和IApple的开发人员,我可以选择是要求消费者处理处置,还是让容器处理处置。

如果您有时想自己管理Apple实例的生命周期,有时让容器处理,那么您可以定义两个接口:

interface IApple : IDisposable
{
    void Consume();
}

interface IHorse
{
    void Eat(IApple apple);   // is supposed to call apple.Consume()
}

interface IHorseKeeper
{
    void FeedHorse();   // is supposed to call horse.Eat(apple)
                        //   where 'horse' is injected into IHorseKeeper
                        //   and 'apple' is generated by IHorseKeeper on-the-fly
}
public IApple
{
   void Consume();
}

public IDisposableApple : IApple, IDisposable
{
}
然后注册该类两次:

builder.RegisterType<Apple>().As<IApple>();
builder.RegisterType<Apple>().As<IDisosableApple>().ExternallyOwned(); 

builder.RegisterType。苹果可能只是一个“可更新”的对象,即不需要由IoC容器管理的对象。

这里的其他答案很有见地,但有一个问题。在这两种情况下,如果苹果有其他需要处理的依赖项,则不会进行正确的清理

Autofac 2在此提供了一个新的帮助功能,称为“自有实例”。我注意到您的注册码是Autofac 1.4,因此如果您无法升级,请告诉我(还有其他不太透明的方法)

照常注册苹果(非外部所有):

(请注意.Value属性以获取基础IApple

在使用块结束时,将清除苹果及其所有依赖项


直接使用IApple的任何其他组件(作为依赖项)将获得通常的行为。

我不能说如何具体使用Autofac解决这个问题,但总的来说,让IApple从IDisposable派生是一个漏洞百出的抽象。你最好重新考虑一下你的设计。@Mark Seemann,我很好奇为什么
IApple
不需要实现类来实现它ement
IDisposable
也是如此。请简要解释一下。为什么要这样做?实现IDisposable向运行时和其他开发人员表明,所讨论的类型包含对非托管内存的引用。对于接口,这是不可能的,因此它表明您正在让您对一个或多个具体的实现泄漏到抽象中。此外,请看一下Nicholas Blumhardt在2月4日上午5:10的讨论中的评论:总结得非常好。好的,谢谢。我以后会记住这一点!(但是,我仍然不同意,
IDisposable
仅用于释放非托管资源。实现
IDisposable
IMO还有其他正当理由。)感谢您提供了非常清晰和有用的解释,特别是关于
外部拥有的
如何与
IDisposable
很好地协同工作以实现一致的设计。我仍然有点失望,尽管这似乎是一种全有或全无的情况,也就是说,似乎没有合理的方法告诉Autofac进行管理仅在某些情况下处置。我不确定我是否清楚您所说的“某些情况”是什么意思.LifetimeScope是一种对容器中的处置进行更细粒度控制的方法,但我可以看到这如何将容器暴露给其他不知道的类。否则,我无法看到Autofac如何在没有外部代码输入的情况下知道何时应处置实例。使用“某些实例”,我基本上是指@wcoenen在他的回答中演示的内容——即,两次注册相同的具体类型,但针对不同的接口,其中一个标记为
ExternallyOwned
,另一个则不是。然后类可以从这些版本中进行选择,从而在很大程度上保持与IoC无关(我认为).Owned是您正在寻找的Autofac的线索。ExternalyOwned仅适用于Apple,因此如果Apple具有需要处理的依赖项,则此选项将被取消
builder.RegisterType<Apple>().As<IApple>();
public delegate Owned<IApple> AppleFactory();
public void FeedHorse()
{
    using (var apple = appleFactory())
    {
        horse.Eat(apple.Value);
    }
}