2017-08-01 4 views
0

Je construis un morceau générique de logiciel middleware (un service de Windows) qui effectuera de nombreux jobs .. tels que:Quel est le meilleur moyen de transmettre les paramètres du constructeur d'exécution à Autofac?

  • Sync Inventaire
  • Les commandes d'importation
  • etc

I J'utilise C# avec un service Windows et Quartz.NET pour planifier les travaux à exécuter. J'utilise aussi AutoFac. D'après ce que je comprends, les dépendances AutoFac devraient être construites à la racine de la composition. C'est bien, mais j'ai certains services injectés qui nécessitent des paramètres d'exécution (les valeurs de configuration proviennent de la base de données).

Par exemple:

  • Certains emplois vont se connecter à un serveur SFTP dans lequel les informations de connexion sont stockées sous forme de paires de valeurs clés dans la base de données
  • Certains emplois vont se connecter à une API à distance dans lequel les informations d'authentification API sont stockés par rapport au travail dans la base de données.

Je l'ai fait quelques recherches sur ce sujet et certaines alternatives suggère essentiellement de retirer le constructeur de certains de ces services (comme un client SFTP) et les transmettre comme méthodes de configuration ..

au lieu du client avoir un constructeur tel que

SftpClient(string host, string username, string password, int timeoutInSeconds)

Il aurait un constructeur par défaut, et une méthode de configuration que vous passez dans ces.

Je n'aime pas ça du tout - cela va à l'encontre de ce que j'ai appris en essayant de configurer votre objet afin qu'il soit cohérent avec le constructeur.

Quelles sont les meilleures options? Ma méthode JobFactory prend actuellement une dépendance sur IComponentContext. J'ai vu qu'il existe des moyens de transmettre des paramètres à AutoFac pour construire l'objet, mais les choses que j'ai lues suggèrent que ce n'est pas idéal. Vaut-il mieux utiliser mon usine pour

Répondre

2

La solution la plus simple consiste à injecter une usine au lieu d'une instance. N'injectez pas le conteneur AutoFac lui-même (qui masque les dépendances). Écris juste une classe simple.

class SftpClientFactory : ISftpClientFactory 
{ 
    public SftpClient GetClient(string host, string userName, string password, int timeout) 
    { 
     return new SftpClient(host, userName, password, timeout); 
    } 
} 

Alors inscrivez-vous comme ceci:

container.RegisterType<ISftpFactory, SftpFactory>(); 

Et dans votre classe

class Example 
{ 
    private readonly ISftpClientFactory _clientFactory; 

    public Example(ISftpClientFactory injectedFactory) 
    { 
     _clientFactory = injectedFactory; 
    } 

    public void DoTheWork() 
    { 
     var client = _clientFactory.GetClient(host, userName, password, timeout); 
    } 
} 

En prime, vous avez maintenant un contrôle complet du cycle de vie d'un objet, qui lui ressemble peut être important avec un client sftp, qui peut contenir une ressource non gérée et être Disposable:

public void DoTheWork() 
    { 
     using (var client = _clientFactory.GetClient(host, userName, password, timeout)) 
     { 
      client.DownloadFile(); 
     } 
    } 

Ce schéma préserve l'inversion du contrôle, vous gardez toujours une seule racine de composition, le client peut toujours être stocké par votre projet de test unitaire, et vous obtenez toujours la résolution à la compilation des dépendances. Le seul inconvénient est qu'il est un peu plus de travail pour écrire le talon, car vous devez écrire une usine de talon aussi.

+0

Merci - et je suppose que les classes Factory seraient dans le même espace de noms que le client lui-même? Tels que, j'ai un projet "commun" séparé où ces services communs et clients vivent - ils iraient dans cet espace de noms et pas l'espace de noms du consommateur droit? – Lock

+0

Correct. L'usine va avec la classe (s) qu'il crée. Le consommateur ne doit pas savoir ou se soucier de l'origine de l'instance, à condition qu'elle se conforme à une interface. –

+0

Je ne vois pas comment je peux utiliser cette méthode quand j'ai des dépendances imbriquées. Dire par exemple ISmtpClient avait une dépendance sur IDependancy qui avait besoin de certaines valeurs d'exécution aussi .. comment cela fonctionnerait-il? – Lock

0

Vous pouvez tirer parti de delegate factories pour transmettre des paramètres personnalisés en phase d'exécution. Dans ce cas, vous n'avez pas besoin de créer des usines personnalisées et il serait assez facile de se moquer de vos tests.

Vous ne pouvez pas utiliser le délégué Func<X,Y,T> intégré, car les paramètres que vous devez transmettre sont du même type. Mais vous pouvez présenter votre propre délégué. Si vous disposez d'une interface distincte pour SmtpClient, il ressemblerait à ceci

public delegate ISmtpClient SmtpClientFactory(string host, string username, string password, int timeout); 

Ensuite, au lieu d'injecter ISmtpClient, vous devez injecter SmptClientFactory. Aucune inscription supplémentaire n'est nécessaire ici.

Si vous n'avez pas l'interface ISmtpClient, il semblerait à peu près la même chose, bien qu'il soit plus difficile à tester plus tard.