10

我目前正在学习如何使用 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
}

此外,我定义了一个将用作IApple工厂的委托:

delegate IApple AppleFactory;

Autofac配置:

现在,我将按如下方式注册上述类型 - 请注意,我省略了两个类的代码AppleHorse,因为它们实现起来很简单:

var builder = new Autofac.ContainerBuilder();

builder.RegisterType<Apple>().As<IApple>();
builder.RegisterType<Horse>().As<IHorse>();
builder.RegisterType<HorseKeeper>().As<IHorseKeeper>();
builder.RegisterGeneratedFactory<AppleFactory>();

我的问题:

我不太清楚如何实现方法IHorseKeeper.Feed。这是我目前拥有的:

class HorseKeeper : IHorseKeeper
{
    private readonly IHorse horse;
    private readonly AppleFactory appleFactory;

    public HorseKeeper(IHorse horse, AppleFactory appleFactory)
    //                 ^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^
    //                         constructor injection
    {
        this.horse = horse;
        this.appleFactory = appleFactory;
    }

    public void FeedHorse()
    {
        using (var apple = appleFactory())
        {
            horse.Eat(apple);
        }  // <- Dispose() apple now (ASAP), as it's no longer needed!
    }
}

这是我想要的那种代码,因为它完全与 Autofac 无关。只要AppleFactory按预期工作,它也可以与另一个 IoC 容器一起工作。

然而,因为 AutofacAppleFactory为我处理,它会跟踪它为我生成的所有对象,因此在容器的生命周期结束时会自己IApple想要它们。Dispose即,生产的产品apple将被处理两次。

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

使用 Autofac 进行确定性处理需要使用创建嵌套容器container.BeginLifetimeScope(),但是我不想在内部使用它,HorseKeeper.FeedHorse因为那样HorseKeeper会依赖于 Autofac,并且我想保持我的代码与 IoC 无关。

问题:

如何HorseKeeper.FeedHorse以与 IoC (Autofac) 无关的方式实现,同时确保正确处理动态生成的对象?

4

3 回答 3

15

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

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

像往常一样注册 Apple(非外部所有):

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

将 AppleFactory 声明为:

public delegate Owned<IApple> AppleFactory();

在 Autofac 2 中,您不再需要调用 RegisterGeneratedFactory() - 这是自动的。

然后,在 HorseKeeper 中,像这样喂马:

public void FeedHorse()
{
    using (var apple = appleFactory())
    {
        horse.Eat(apple.Value);
    }
}

(注意 .Value 属性以获取底层 IApple。

在 using 块结束时,将清理苹果及其所有依赖项。

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

于 2010-02-14T00:39:32.607 回答
3

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

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

不过,通过此注册,您的 Feed 代码将按预期工作。

注意:关于接口是否应该继承的讨论IDisposable:IMO,当接口继承时IDisposable,这向“消费”开发人员表明应该在某个时间点处理该实例。在 IApple 的情况下,由于该接口也是 IDisposable,因此开发人员应确保释放实例(然后还必须注册为 ExternallyOwned)。另一方面,如果 Apple 类看起来像这样:

class Apple: IApple, IDisposable
{ }

IApple 的消费者现在完全不知道实例是 IDisposable 的。在这种情况下,我们将让容器处理处置。

所以我的结论是,作为 Apple 和 IApple 的开发者,我可以选择是要求消费者处理还是将其存放在容器中。

于 2010-02-13T14:49:55.250 回答
2

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

public IApple
{
   void Consume();
}

public IDisposableApple : IApple, IDisposable
{
}

然后注册课程两次:

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

然后,您可以将 DisposableAppleFactory 注入需要创建和处置苹果的类中。

对于只需要一个与容器具有相同生命周期的苹果的类,您可以注入 IApple。

但是,您需要两者的事实可能表明您正在混合使用 newables 和 injectionables。Apple 可能只是一个“可更新的”对象,即不需要由 IoC 容器管理的对象。

于 2010-02-13T15:40:22.753 回答