Autofac: Как ограничить время жизни объекта IDisposable, не раздавая контейнер МОК

Я в настоящее время изучаю, как использовать 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:

Теперь, я зарегистрировался бы, вышеупомянутые типы следующим образом - отмечают, что я опускаю код обоих классов Apple и Horse, так как они тривиальны для реализации:

var builder = new Autofac.ContainerBuilder();

builder.RegisterType().As();
builder.RegisterType().As();
builder.RegisterType().As();
builder.RegisterGeneratedFactory();

Моя проблема:

Я не вполне знаю, как реализовать метод 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 работы как ожидалось.

Однако, потому что Autofac обрабатывает AppleFactory для меня это будет отслеживать все IApple объекты это производит для меня и поэтому захочет Dispose их самостоятельно в конце времени жизни контейнера. Т.е., произведенный apple будет расположен дважды.

Я предполагаю регистрацию IApple как .ExternallyOwned() не эффективное решение, поскольку могли бы быть случаи, где легче позволить Autofac обработать IAppleвремя жизни.

Детерминированное распоряжение с Autofac требует создания вложенного контейнерного использования container.BeginLifetimeScope(), однако я не хочу использовать эту внутреннюю часть HorseKeeper.FeedHorse потому что тогда HorseKeeper становится зависящим от Autofac, и я хотел бы сохранить своего агностика МОК кода.

Вопрос:

Как я реализую HorseKeeper.FeedHorse в МОК (Autofac) - агностический путь при обеспечении, что на лету сгенерированные объекты расположены правильно?

10
задан stakx supports GoFundMonica 13 February 2010 в 13:11
поделиться

3 ответа

Другие ответы здесь полезны, но имеют проблему. В обоих случаях, если у 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 напрямую (в качестве зависимостей), получат обычное поведение.

15
ответ дан 3 December 2019 в 19:32
поделиться

Единственный способ - изменить регистрацию Apple с помощью модификатора ExternalOwned . Это указывает Autofac не отслеживать объект для удаления, а позволить кому-то внешнему (вашему коду) обрабатывать удаление. Но, как вы заявляете, теперь вам нужно будет убедиться, что все экземпляры Apple удаляются вручную, поскольку вы не получите автоматической помощи от Autofac.

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

Однако с этой регистрацией ваш код Feed будет работать должным образом.

Примечание : при обсуждении, должен ли интерфейс наследовать IDisposable или нет: IMO, когда интерфейс наследует IDisposable , это указание на "потребляющего" разработчика что экземпляр должен быть удален в определенный момент времени. В случае с IApple, поскольку этот интерфейс также является IDisposable, разработчик должен убедиться, что утилизированы экземпляры (и должен также затем быть зарегистрирован как ExternalOwned). С другой стороны, если бы класс Apple выглядел так:

class Apple: IApple, IDisposable
{ }

потребители IApple теперь полностью не осведомлены о том, что экземпляры являются IDisposable. В этом случае мы позволим контейнеру обрабатывать утилизацию.

Итак, я пришел к выводу, что мне как разработчику Apple и IApple решать, требовать ли я от потребителей заниматься утилизацией или оставлять это на усмотрение контейнера.

3
ответ дан 3 December 2019 в 19:32
поделиться

Если вы иногда хотите самостоятельно управлять временем жизни экземпляров Apple, а иногда позволять контейнеру обрабатывать это, то вы можете определить два интерфейса:

public IApple
{
   void Consume();
}

public IDisposableApple : IApple, IDisposable
{
}

И затем дважды зарегистрируйте класс:

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

Затем вы можете внедрить DisposableAppleFactory в классы, которым необходимо создавать и удалять яблоки.

Для классов, которым просто нужно яблоко с тем же временем жизни, что и у контейнера, вместо этого вы вводите IApple.

Однако тот факт, что вам нужны оба, может указывать на то, что вы смешиваете новые и инъекции . Apple может быть просто «новым» объектом, то есть тем, которым не нужно управлять контейнером IoC.

2
ответ дан 3 December 2019 в 19:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: