0

Ma classe d'entité a une dépendance sur un référentiel.Comment éviter de passer une référence au conteneur ou d'utiliser CommonServiceLocator pour une entité EntityFactory

public class User 
{ 
    private readonly IUserRepository _userRepository; 

    public User(IUserRepository userRepository) 
    { 
     _userRepository = userRepository; 
    } 

    ... 
} 

Et j'ai une classe EntityFactory utilisée par le Repository pour créer des entités.

public class UserRepository : IUserRepository 
{ 
    private readonly EntityFactory _entityFactory; 

    public UserRepository(EntityFactory entityFactory) 
    { 
     _entityFactory = entityFactory; 
    } 

    ... 
} 

// EntityFactory #1 with no references or dependencies to DI frameworks or CommonServiceLocator 
public class EntityFactory 
{ 
    public User InstantiateUser() 
    { 
     return new User(); // Requires IUserRepository parameter 
    } 
} 

// EntityFactory #2 with reference to Ninject 
using Ninject; 

public class EntityFactory 
{ 
    private readonly IKernel _kernel; 

    public EntityFactory(IKernel kernel) 
    { 
     _kernel = kernel; 
    } 

    public User InstantiateUser(IKernel kernel) 
    { 
     return new User(_kernel.Get<IUserRepository>()); 
    } 
} 

// EntityFactory #3 with reference to CommonServiceLocator 
using Microsoft.Practices.ServiceLocation; 

public class EntityFactory 
{ 
    public User InstantiateUser() 
    { 
     return new User(ServiceLocator.Current.GetInstance<IUserRepository>()); 
    } 
} 

Y at-il un moyen d'éviter la EntityFactory ayant une référence au conteneur ou à l'aide du CommonServiceLocator? (Context Agnostic)

Ou suis-je juste en train de concevoir mes classes incorrectes et la classe User ne devrait pas dépendre de n'importe quel dépôt?

Edit: Voici le code en utilisant la méthode de David:

// Ninject binding 
Bind<Func<User>>().ToMethod(cxt =>() => new User(cxt.Kernel.Get<IUserRepository>())); 

// EntityFactory class 
private readonly Func<User> _userFactory; 
public EntityFactory(Func<User> userFactory) 
{ 
    _userFactory = userFactory; 
} 
public User InstantiateUser() 
{ 
    return userFactory.Invoke(); 
} 
+0

Vous ne pouvez pas simplement appeler 'ServiceLocator.Current.GetInstance ()' ou '_kernel.Get ()'? – Steven

Répondre

1

Votre cadre de DI devrait vous fournir une méthode de création d'usines:

public class EntityFactory 
{ 
    public EntityFactory(Func<User> userFactory) { /* ... */ } 

    public User InstantiateUser() 
    { 
     return userFactory.Invoke(); 
    } 
} 

Lorsque le EntityFactory est créé, il va recevoir une User bonne usine qui peut ensuite être utilisé pour créer des utilisateurs correctement résolus sans aucune référence à l'IoC.

+0

Oui, cela semble être ce que je cherche. Bien que je ne suis pas encore sûr d'avoir plusieurs champs d'usine Func dans mon EntityFactory. Cela semble trop modifier le code pour le faire correspondre au style DI. Comment cela fonctionnerait-il avec Unit Testing? – Rudy

+0

@Rudy: si votre entité EntityFactory invoque uniquement des méthodes d'usine, il peut être judicieux de s'en passer complètement. Un autre serait de créer un petit FactoryByTypeCache qui encapsulerait/cache/memoize la création d'usine. Dans les tests unitaires, vous pouvez remplacer les délégués d'usine dans votre conteneur et/ou remplacer complètement EntityFactory. (Voir aussi le modèle Repository) –

0

Quel est le problème avec l'option 2 ou 3? Si vous utilisez IoC ou un localisateur de service, vous aurez besoin d'une référence quelque part.

J'ai une référence globale au conteneur IoC et l'utilise pour résoudre les interfaces partout. Cela ressemble beaucoup au localisateur de service, mais utilise plutôt le conteneur IoC.

Je ne crois pas qu'il existe un moyen de contourner cela.

+1

Les options 2 ou 3 rendent très difficile la désactivation de l'IoC, ou utilisent deux configurations différentes dans le même domaine, car elles répandent des références statiques partout. Le scénario d'utilisation approprié ne nécessite des références à l'IoC que lors de l'enregistrement des composants et de la résolution de «l'application» dans l'orchestrateur. –

+0

Je crois que l'option 2 l'implémente réellement, exigeant que le conteneur IoC soit passé lors de l'instanciation de EntityFractory. J'ai mis en œuvre IoC en ayant un wrapper de classe statique qui a la possibilité de basculer les conteneurs IoC en fonction de la référence 'ThreadStatic' (le contexte de la requête en cours). Cependant, il est également possible de créer simplement un second 'AppDomain' si le conteneur IoC est spécifique à, par exemple. un module ou un plugin. –