Pregunta

Actualmente estoy aprendiendo a usar Autofac, y estoy atascado con la eliminación objetos IDisposable determinista. Permítanme en primer lugar presento la situación antes de que voy a exponer mi problema.

Posición inicial:

Digamos que mi modelo de objeto se define a través de las siguientes 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
}

Además, defino un delegado que será utilizado como una fábrica de IApple:

delegate IApple AppleFactory;

configuración Autofac:

Ahora, me gustaría registrar los tipos anteriores de la siguiente manera - en cuenta que estoy omitiendo el código de ambas clases Apple y Horse, ya que son triviales de implementar:

var builder = new Autofac.ContainerBuilder();

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

Mi problema:

No sé muy bien cómo poner en práctica el método IHorseKeeper.Feed. Esto es lo que tengo actualmente:

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

Este es el tipo de código me gustaría tener, ya que es completamente independiente del Autofac. Se podría muy bien trabajar con otro contenedor IoC, siempre y cuando las obras AppleFactory como se esperaba.

Sin embargo, debido Autofac se encarga de la AppleFactory para mí, que se mantendrá un registro de todos los objetos que produce IApple para mí, y por lo tanto va a querer Dispose ellos sí al final de la vida útil del recipiente. Ie., La apple producida estará dispuesto dos veces.

supongo registrando IApple como .ExternallyOwned() hay una solución viable, ya que puede haber casos en los que es más fácil dejar que Autofac manejar toda la vida los IApples'.

disposición determinista con Autofac requiere la creación de un contenedor anidada utilizando container.BeginLifetimeScope(), sin embargo no quiero utilizar este HorseKeeper.FeedHorse dentro porque entonces HorseKeeper se vuelve dependiente de Autofac, y me gustaría mantener mi código COI-agnóstico.

Pregunta:

¿Cómo se implementa en un HorseKeeper.FeedHorse COI (Autofac) forma -agnostic garantizando al mismo tiempo que la marcha en objetos generados se disponen adecuadamente?

¿Fue útil?

Solución

Las otras respuestas aquí son perspicaces, pero tienen un problema. En ambos casos, si Apple tiene otras dependencias que necesitan disposición, la limpieza correcta no sucederá.

Autofac 2 ofrece una nueva característica para ayudar aquí, los llamados "casos de propiedad". Noté que su código de registro es Autofac 1.4, por lo que si usted es incapaz de actualizar hágamelo saber (hay otros, menos transparentes, maneras de hacer esto.)

Registrar Apple como de costumbre (que no es propiedad externamente):

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

Declarar AppleFactory como:

public delegate Owned<IApple> AppleFactory();

En Autofac 2, no es necesario llamar a RegisterGeneratedFactory () más -. Esto es automático

A continuación, en HorseKeeper, alimentar a los caballos como esto:

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

(Nota la propiedad .Value para obtener el iApple subyacente.

Al final del uso de bloquear la manzana, además de todas sus dependencias, será limpiado.

Los demás componentes que utilizan iApple directamente (como dependencias) obtendrán el comportamiento habitual.

Otros consejos

La única manera es modificar el registro de Apple con el modificador ExternallyOwned. Esto indica Autofac a no seguir el objeto para su eliminación, sino más bien dejar que alguien externo (su código) manejar la disposición. Pero, como usted afirma, que ahora tendrá que asegurarse de que todas las instancias de manzana se disponen de forma manual, ya que no obtendrá ayuda automática de Autofac.

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

Con este registro el código de RSS funcionará como se espera, sin embargo.

Nota: en la explicación si la interfaz debe heredar IDisposable o no: OMI, cuando una interfaz hereda IDisposable, esto es una indicación para el desarrollador "consumidor" que la instancia debe eliminarse en algún punto en el tiempo. En el caso de iApple, ya que la interfaz es también IDisposable, el desarrollador debe asegurarse de disponer instancias (y debe también se puede registrar como ExternallyOwned). Por otro lado, si la clase de Apple era la siguiente:

class Apple: IApple, IDisposable
{ }

consumidores del iApple es ahora plenamente conscientes del hecho de que los casos es IDisposable. En este caso vamos a dejar que la disposición de asa del contenedor.

Así que mi conclusión es que es a mí como el desarrollador de Apple y iApple de elegir si voy a obligar al consumidor a manejar su eliminación o dejar en manos de un contenedor.

Si a veces se desea administrar el tiempo de vida de la manzana casos usted mismo, y algunas veces deja el contenedor manejarlo, entonces se puede definir dos interfaces:

public IApple
{
   void Consume();
}

public IDisposableApple : IApple, IDisposable
{
}

Y a continuación, registrar la clase dos veces:

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

A continuación, puede inyectar un DisposableAppleFactory en clases que necesitan para crear y disponer las manzanas.

Para las clases, que sólo necesitan una manzana con el mismo tiempo de vida como el contenedor, se inyecta iApple lugar.

Sin embargo, el hecho de que necesita ambos pueden indicar que usted está mezclando noreferrer newables e inyectables . Apple podría ser simplemente un objeto "newable", es decir, uno que no necesita ser gestionado por el contenedor IoC.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top