Autofac: comment limiter la durée de vie d’un object identifiable sans passer par le conteneur IoC

J’apprends actuellement à utiliser Autofac et je suis obligé de disposer de manière déterministe des objects identifiables. Permettez-moi d’abord de présenter la situation avant que je ne pose mon problème.

Position de départ:

Disons que mon modèle d’object est défini via les interfaces suivantes:

 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 } 

De plus, je définis un délégué qui sera utilisé comme une usine IApple :

 delegate IApple AppleFactory; 

Configuration Autofac:

Maintenant, je voudrais enregistrer les types ci-dessus comme suit – notez que j’omets le code des deux classes Apple et Horse , car elles sont simples à implémenter:

 var builder = new Autofac.ContainerBuilder(); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterGeneratedFactory(); 

Mon problème:

Je ne sais pas trop comment mettre en œuvre la méthode IHorseKeeper.Feed . Voici ce que j’ai actuellement:

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

C’est le genre de code que j’aimerais avoir, car il est complètement agnostique à l’autofac. Cela pourrait tout aussi bien fonctionner avec un autre conteneur IoC, à condition que AppleFactory fonctionne comme prévu.

Cependant, comme Autofac gère l’ AppleFactory pour moi, il gardera une trace de tous les objects IApple qu’il produit pour moi et voudra donc les Dispose lui-même à la fin de la vie du conteneur. C’est-à-dire que la apple produite sera éliminée deux fois.

Je suppose que l’enregistrement de IApple tant que .ExternallyOwned() n’est pas une solution viable, car il pourrait être plus facile de laisser Autofac gérer la IApple de vie de IApple .

L’élimination déterministe avec Autofac nécessite la création d’un conteneur nested à l’aide de container.BeginLifetimeScope() , mais je ne souhaite pas l’utiliser dans HorseKeeper.FeedHorse car HorseKeeper devient alors dépendant d’Autofac et je souhaite conserver mon code IoC-agnostic. .

Question:

Comment implémenter HorseKeeper.FeedHorse de manière HorseKeeper.FeedHorse IoC (Autofac) tout en garantissant que les objects générés à la volée sont correctement éliminés?

Les autres réponses ici sont perspicaces, mais ont un problème. Dans les deux cas, si Apple a d’autres dépendances à éliminer, un nettoyage correct n’aura pas lieu.

Autofac 2 fournit une nouvelle fonctionnalité pour aider ici, appelée “instances possédées”. J’ai remarqué que votre code d’enregistrement est Autofac 1.4. Si vous ne parvenez pas à effectuer la mise à niveau, faites-le-moi savoir (il existe d’autres moyens moins transparents de le faire.)

Enregistrez Apple comme d’habitude (sans propriété externe):

 builder.RegisterType().As(); 

Déclarez AppleFactory en tant que:

 public delegate Owned AppleFactory(); 

Dans Autofac 2, vous n’avez plus besoin d’appeler RegisterGeneratedFactory () – c’est automatique.

Ensuite, dans HorseKeeper, nourrissez le cheval de la manière suivante:

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

(Notez la propriété .Value pour obtenir le IApple sous-jacent.

À la fin du bloc using, la pomme, ainsi que toutes ses dépendances, seront nettoyées.

Tous les autres composants qui utilisent directement IApple (en tant que dépendances) auront le comportement habituel.

Le seul moyen est de modifier l’enregistrement Apple avec le modificateur ExternallyOwned . Cela indique à Autofac de ne pas suivre l’object à éliminer, mais de laisser un tiers (votre code) gérer la mise au rebut. Mais comme vous l’indiquez, vous devez maintenant vous assurer que toutes les instances d’Apple sont supprimées manuellement, car vous ne recevrez aucune aide automatique d’Autofac.

 builder.RegisterType().As().ExternallyOwned(); 

Avec cet enregistrement, votre code de stream fonctionnera comme prévu, cependant.

Remarque : lors de la discussion sur le fait de savoir si l’interface doit hériter d’ IDisposable ou non: IMO, lorsqu’une interface hérite d’ IDisposable , ceci indique au développeur “consommateur” que l’instance doit être supprimée à un moment donné. Dans le cas de IApple, cette interface étant également IDisposable, le développeur doit s’assurer de disposer des instances (et doit ensuite être enregistré en tant qu’ExternallyOwned). Par contre, si la classe Apple ressemblait à ceci:

 class Apple: IApple, IDisposable { } 

les consommateurs de IApple ne sont désormais plus conscients du fait que les instances sont identifiables. Dans ce cas, nous laisserons le conteneur gérer l’élimination.

Ma conclusion est donc que c’est à moi, développeur de Apple et d’Apple, de décider si je demanderai aux consommateurs de s’en débarrasser ou de les laisser à un conteneur.

Si vous souhaitez parfois gérer vous-même la durée de vie des instances Apple et laisser parfois le conteneur s’en charger, vous pouvez définir deux interfaces:

 public IApple { void Consume(); } public IDisposableApple : IApple, IDisposable { } 

Et ensuite, enregistrez le cours deux fois:

 builder.RegisterType().As(); builder.RegisterType().As().ExternallyOwned(); 

Vous pouvez ensuite injecter un DisposableAppleFactory dans les classes devant créer et supprimer des pommes.

Pour les classes qui ont juste besoin d’une pomme ayant la même durée de vie que le conteneur, vous injectez plutôt IApple.

Cependant, le fait que vous ayez besoin des deux peut indiquer que vous mélangez des matières nouvelles et injectables . Apple peut simplement être un object “newable”, c’est-à-dire qu’il n’a pas besoin d’être géré par le conteneur IoC.