Pergunta

Atualmente, estou aprendendo a usar o Autofac e estou preso ao descartar IDisposable objetos deterministicamente. Deixe -me primeiro apresentar a situação antes de declarar meu problema.

Posicão inicial:

Digamos que meu modelo de objeto seja definido pelas seguintes interfaces:

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
}

Além disso, defino um delegado que será usado como um IApple fábrica:

delegate IApple AppleFactory;

Configuração do Autofac:

Agora, eu registraria os tipos acima da seguinte forma - observe que estou omitindo o código de ambas as classes Apple e Horse, já que eles são triviais para implementar:

var builder = new Autofac.ContainerBuilder();

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

Meu problema:

Eu não sei como implementar o método IHorseKeeper.Feed. Aqui está o que eu tenho atualmente:

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!
    }
}

Esse é o tipo de código que eu gostaria de ter, pois é completamente autofac-agnóstico. Poderia muito bem trabalhar com outro contêiner do COI, desde que AppleFactory funciona conforme o esperado.

No entanto, porque o Autofac lida com o AppleFactory Para mim, vai acompanhar todos IApple objetos que produz para mim e, portanto, desejarão Dispose eles próprios no final da vida do contêiner. Ou seja, o produzido apple será descartado duas vezes.

Suponho que o registro IApple Como .ExternallyOwned() não é uma solução viável, pois pode haver casos em que é mais fácil deixar o Autofac lidar com o IAppleS 'Lifetime.

Descarte determinístico com Autofac requer a criação de um recipiente aninhado usando container.BeginLifetimeScope(), porém eu não quero usar isso dentro HorseKeeper.FeedHorse Porque então HorseKeeper Torna-se dependente do Autofac, e eu gostaria de manter meu código ioc-agnóstico.

Pergunta:

Como faço para implementar HorseKeeper.FeedHorse De uma maneira agnóstica do COI (Autofac), garantindo que os objetos gerados na fly-fly sejam descartados adequadamente?

Foi útil?

Solução

As outras respostas aqui são perspicazes, mas têm um problema. Nos dois casos, se a Apple tiver outras dependências que precisam de descarte, a limpeza correta não acontecerá.

O Autofac 2 fornece um novo recurso para ajudar aqui, chamado "instâncias de propriedade". Percebi que seu código de registro é Autofac 1.4; portanto, se você não conseguir atualizar, me avise (existem outras maneiras menos transparentes de fazer isso.)

Registre a Apple como de costume (não de propriedade externamente):

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

Declare a AppleFactory como:

public delegate Owned<IApple> AppleFactory();

No AutoFAC 2, você não precisa mais ligar para o RegisterGeneratedFactory () - isso é automático.

Então, em cavalos, alimente o cavalo assim:

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

(Observe a propriedade .Value para obter o IAPPLE subjacente.

No final do uso do Block the Apple, além de todas as suas dependências, serão limpas.

Quaisquer outros componentes que usam o IAPPLE diretamente (como dependências) receberão o comportamento usual.

Outras dicas

A única maneira é modificar o registro da Apple com o ExternallyOwned modificador. Isso instrui o AutoFAC a não rastrear o objeto para descarte, mas permita que alguém externo (seu código) lide com o descarte. Mas, como você afirma, agora você terá que garantir que todas as instâncias da Apple estejam descartadas manualmente, pois você não receberá ajuda automática do Autofac.

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

Com este registro, seu código de alimentação funcionará conforme o esperado, no entanto.

Observação: na discussão se a interface deve herdar IDisposable ou não: IMO, quando uma interface herda IDisposable, isso é uma indicação para o desenvolvedor "consumindo" de que a instância deve ser descartada em algum momento. No caso de IAPPLE, uma vez que essa interface também é idisposta, o desenvolvedor deve garantir instâncias (e devo também ser registrado como proprietário externo). Por outro lado, se a aula da Apple parecia assim:

class Apple: IApple, IDisposable
{ }

Os consumidores do IAPPLE agora não têm conhecimento do fato de que as instâncias são idiposáveis. Nesse caso, deixaremos o disposição do contêiner.

Portanto, minha conclusão é que cabe a mim como desenvolvedor da Apple e Iapple escolher se exigirei que os consumidores lidem com o descarte ou o deixem em um contêiner.

Se às vezes você deseja gerenciar a vida inteira das instâncias da Apple e, às vezes, deixar o contêiner lidar com ele, pode definir duas interfaces:

public IApple
{
   void Consume();
}

public IDisposableApple : IApple, IDisposable
{
}

E depois registre a aula duas vezes:

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

Em seguida, você pode injetar um disposleacleAppleFactory em classes que precisam criar e descartar maçãs.

Para as aulas que precisam apenas de uma maçã com a mesma vida útil do contêiner, você injeta o IAPPLE.

No entanto, o fato de você precisar ambos pode indicar que está misturando novos e injetáveis. A Apple pode simplesmente ser um objeto "novo", ou seja, um que não precisa ser gerenciado pelo contêiner do COI.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top