6

j'ai un service WCF et sur le client j'ai:injection de dépendance avec plusieurs référentiels

var service = new ServiceReference1.CACSServiceClient() 

Le code de service réel est:

public CACSService() : this(new UserRepository(), new BusinessRepository()) { } 

public CACSService(IUserRepository Repository, IBusinessRepository businessRepository) 
{ 
    _IRepository = Repository; 
    _IBusinessRepository = businessRepository; 
} 

Donc, tout cela fonctionne très bien, mais je ne « aime la façon dont je suis Newing toutes les dépôts en même temps parce que le code client pourrait ne pas besoin de nouveau le UserRepository et seulement intéressé par newing le BusinessRepository. Donc, y a-t-il un moyen de passer quelque chose à ce code:
var service = new ServiceReference1.CACSServiceClient()
de dire quel référentiel à nouveau en fonction du code qui appelle le service ou tout autre conseil que je dois faire lors de la conception des dépôts pour mon cadre d'entité. Thankx

+1

+1 pour "newing up" – Jacob

Répondre

0

Au lieu d'instancier (« newing up ») les dépôts sur la construction, vous pouvez charger paresseux dans leurs propriétés. Cela vous permettrait de conserver votre second constructeur, mais que votre premier constructeur ne fasse rien.

L'utilisateur pourrait alors affecter ces derniers, au besoin, sinon.

Par exemple:

public class CACSService 
{ 
    public CACSService() {} 

    public CACSService(IUserRepository Repository, IBusinessRepository businessRepository) 
    { 
     _IRepository = Repository; 
     _IBusinessRepository = businessRepository; 
    } 

    private IUserRepository _IRepository; 
    public IUserRepository Repository 
    { 
     get { 
      if (this._IRepository == null) 
        this._IRepository = new UserRepository(); 
      return this._IRepository; 
     } 
    } 

    // Add same for IBusinessRepository 
} 
0

Faites vos dépôts ont l'état-niveau de l'objet? Probablement pas, alors créez-les en tant que singletons et ayez un conteneur DI pour les fournir à CACService.

Sinon, sont-ils réellement coûteux à créer? Si ce n'est pas le cas, la création d'un nouveau par requête a un coût négligeable par rapport aux opérations RPC et de base de données.

Utilisation du conteneur d'injection de dépendance Ninject, votre CACService pourrait ressembler à ce qui suit. D'autres conteneurs DI ont des mécanismes tout aussi succincts pour ce faire. Et pendant le démarrage de votre application, vous diriez à Ninject à propos de ces types.

Bind<IUserRepository>().To<UserRepository>().InSingletonScope(); 
Bind<IBusinessRepository>().To<BusinessRepository>().InSingletonScope(); 
+3

Il n'est pas nécessaire d'avoir recours aux singletons et aux constructeurs par défaut. WCF est parfaitement capable de supporter ** Injection Constructeur **. –

+0

Cette mise en œuvre semble cool, je vais essayer et aussi ce que Mark Seeman a dit à propos de chargement paresseux du référentiel. – user282807

+0

Les amis ne permettent pas aux amis d'écrire des singletons. –

0

Préface: Ceci est un guide général d'inversion de dépendance. Si vous avez besoin du constructeur par défaut pour faire le travail (par exemple, s'il est créé par réflexion ou autre chose), il sera plus difficile de le faire proprement.

Si vous souhaitez que votre application soit configurable, cela signifie que vous pouvez varier la façon dont votre graphe d'objet est construit. En termes très simples, si vous voulez modifier une implémentation de quelque chose (par exemple vous voulez parfois une instance de UserRepository, d'autres fois vous voulez une instance de MemoryUserRepository), alors le type utilise l'implémentation (CACService dans ce cas) devrait ne pas être accusé de le renouveler. Chaque utilisation de new vous lie à une implémentation spécifique. Misko has written some nice articles about this point. Le principe d'inversion de dépendance est souvent appelé "parametrise from above", car chaque type concret reçoit ses dépendances (déjà instanciées) de l'appelant.

Pour mettre cela en pratique, déplacez le code de création d'objet du constructeur sans paramètre de CACService et placez-le dans une usine.

Vous pouvez alors choisir de câbler les choses différemment selon les choses comme:

  • lecture dans un fichier de configuration
  • passant des arguments à l'usine
  • créer un autre type d'usine

Séparer les types en deux catégories (types créer choses et types que faire choses) est une technique puissante.

E.g. Voici une façon relativement simple de le faire en utilisant une interface d'usine - nous simplement nouveau quelle usine est appropriée à nos besoins et appelons sa méthode Create. Nous utilisons un conteneur d'injection de dépendances (Autofac) pour faire ce travail au travail, mais il peut être trop lourd pour vos besoins.

public interface ICACServiceFactory 
{ 
    CACService Create(); 
} 

// A factory responsible for creating a 'real' version 
public class RemoteCACServiceFactory : ICACServiceFactory 
{ 
    public CACService Create() 
    { 
     return new CACService(new UserRepository(), new BusinessRepository()); 
    }   
} 

// Returns a service configuration for local runs & unit testing 
public class LocalCACServiceFactory : ICACServiceFactory 
{ 
    public CACService Create() 
    { 
     return new CACService(
       new MemoryUserRepository(), 
       new MemoryBusinessRepository()); 
    }  
} 
16

La beauté pure DI est que vous ne devriez pas vous soucier de la durée de vie de vos dépendances, car ceux-ci sont gérés pour vous par ceux qui les fournir (un conteneur DI ou un autre code que vous avez écrit).

(En aparté, vous devez vous débarrasser de votre injection Bastard actuels constructeurs. Jetez le constructeur parameterless et garder celui qui annonce explicitement ses dépendances.)

Gardez votre constructeur comme celui-ci, et l'utilisation _IRepository et _IBusinessRepository au besoin:

public CACSService(IUserRepository Repository, IBusinessRepository businessRepository) 
{ 
    _IRepository = Repository; 
    _IBusinessRepository = businessRepository; 
} 

Si vous craignez que l'un de ces dépôts ne vont pas être nécessaires à l'exécution, vous pouvez injecter une mise en œuvre de chargement paresseux, disons, IUserRepsository au lieu du vrai vous aviez à l'esprit en tête.

Supposons que IUserRepository ressemble à ceci:

public interface IUserRepository 
{ 
    IUser SelectUser(int userId); 
} 

Vous pouvez maintenant mettre en œuvre une mise en œuvre de chargement paresseux comme ceci:

public class LazyUserRepository : IUserRepository 
{ 
    private IUserRepository uRep; 

    public IUser SelectUser(int userId) 
    { 
     if (this.uRep == null) 
     { 
      this.uRep = new UserRepository(); 
     } 
     return this.uRep.SelectUser(userId); 
    } 
} 

Lorsque vous créez CACService, vous pouvez le faire en injectant LazyUserRepository dedans, ce qui garantit que le UserRepository réel ne sera initialisé que s'il est nécessaire.

La beauté de cette approche est que vous n'avez pas à le faire jusqu'à ce que vous en ayez besoin. Souvent, cela ne sera vraiment pas nécessaire, donc c'est bien de pouvoir reporter de telles optimisations jusqu'à ce qu'elles soient réellement nécessaires.

J'ai d'abord décrit la technique de dépendances paresseuseshere et here.

+2

d'accord. Ne vous limitez pas à ctor par défaut parce que c'est ce que suggère WCF. utilisez IInstanceProvider pour créer votre instance de service. –

+0

Thankx Mark pour la réponse. Je pourrais avoir 10 ou 20 dépôts pour mes classes de domaine, est-ce que je dois implémenter le chargement paresseux pour chacun? ce serait beaucoup de code. – user282807

+0

Eh bien, l'un de mes principaux points est que vous n'avez peut-être pas à faire cela du tout. Réfléchissez bien si cela pourrait être une optimisation prématurée ou non. Par exemple, si vous utilisez LINQ to SQL ou LINQ to Entities, vous pourriez très bien finir par implémenter tous vos référentiels basés sur le même contexte, et dans ce cas, la surcharge de création de plusieurs instances de référentiel peut être négligeable. –