2010-01-01 11 views
2

Je veux utiliser l'unité résout IService à deux implémentations différentes, d'utiliser une classe wrapper, l'équivalent de:Résolution des classes wrapper en C# avec le conteneur Unité IoC

IService service = new DispatcherService(new RealService(), Application.Current.Dispatcher); 

Lorsque les deux DispatcherService et RealService mettre en œuvre l'interface IService.

J'ai une bibliothèque contenant certains services avec des opérations asynchrones. Une forme simplifiée de ce service ressemble à ceci:

public interface IService 
{ 
    IAsyncResult StartSomeOperation(); 
    event EventHandler<EventArgs> SomeOperationCompleted; 
} 

J'ai implémentations pour tous ces services. Je souhaite que cette bibliothèque reste exempte de dépendances sur WPF et sur les conteneurs IoC, mais prête à être utilisée au mieux dans les cas où des conteneurs IoC et éventuellement WPF sont utilisés.

J'ai un WPi Ui utilisant le conteneur Unity IoC. Le code répétitif le plus courant concerne les gestionnaires terminés - ils doivent être ramenés sur le thread d'interface utilisateur à l'aide de Dispatcher. Donc, je pense à un emballage, comme ceci:

using System; 
using System.Windows.Threading; 

public class DispatcherService : IService 
{ 
    private Dispatcher dispatcher; 
    private IService wrappedService; 

    public DispatcherService(IService wrappedService, Dispatcher dispatcher) 
    { 
     this.wrappedService = wrappedService; 
     this.wrappedService.SomeOperationCompleted += this.OnWrappedOperationCompleted; 
     this.dispatcher = dispatcher; 
    } 

    public IAsyncResult StartSomeOperation() 
    { 
     return this.wrappedService.StartSomeOperation(); 
    } 

    public event EventHandler<EventArgs> SomeOperationCompleted; 

    private void OnWrappedOperationCompleted(object sender, EventArgs e) 
    { 
     if (this.SomeOperationCompleted != null) 
     { 
      Action completedSynch =() => this.SomeOperationCompleted(sender, e); 
      this.dispatcher.Invoke(completedSynch); 
     } 
    } 
} 

Je new ceci avec code comme

IService service = new DispatcherService(new RealService(), Application.Current.Dispatcher); 

Mais l'unité n'aime pas le fait que je compose deux implémentations différentes de la Interface IService. Ce code échoue horriblement:

UnityContainer container = new UnityContainer(); 
    container.RegisterInstance<Dispatcher>(Application.Current.Dispatcher); 
    container.RegisterType<IService, RealService>(); 
    container.RegisterType<IService, DispatcherService>(); 

    IService created = container.Resolve<IService>(); 

Et si j'inscrire les services dans l'autre ordre, le premier enregistrement est écrasé et je viens obtenir un RealService.

Y a-t-il un moyen de contourner cela avec Unity? Ou cela a-t-il été fait avec l'AOP d'Unity? Et si c'est le cas, cela fonctionne-t-il dans Silverlight? Peut-il être fait sans utiliser Unity dans la bibliothèque d'origine du tout.

Je peux travailler autour d'une sous-classe d'interface « marqueur », à savoir

public interface IServiceWithDispatcher : IService 
{ 

} 

... 

UnityContainer container = new UnityContainer(); 
container.RegisterInstance<Dispatcher>(Application.Current.Dispatcher); 

container.RegisterType<IService, RealService>(); 
container.RegisterType<IServiceWithDispatcher, DispatcherService>(); 

Mais je ne pense pas que ce soit une bonne idée, l'interface vide est laid et ce ne sera pas bien échelle .

Quelle est la manière de résoudre cet arbre d'objets?


Mise à jour:

Conformément à la réponse de Dzmitry Huba donnée, voici quelques exemples de code:

Ajouter une référence à Microsoft.Practices.Unity.StaticFactory

Code de travail simple:

UnityContainer container = new UnityContainer(); 

    container.AddNewExtension<StaticFactoryExtension>() 
     .Configure<IStaticFactoryConfiguration>() 
     .RegisterFactory<IService>(cont => 
       new DispatcherService(new RealService(), Application.Current.Dispatcher)); 

    IService created = container.Resolve<IService>(); 

Plus de code de travail complet, qui traite mieux avec le p dépendances POTENTIELS le service réel, comme un conteneur IoC doit:

UnityContainer container = new UnityContainer(); 
    container.RegisterInstance<Dispatcher>(Application.Current.Dispatcher); 
    container.RegisterType<IService, RealService>("Real"); 

    container.AddNewExtension<StaticFactoryExtension>() 
     .Configure<IStaticFactoryConfiguration>() 
     .RegisterFactory<IService>(cont => 
       new DispatcherService(
         cont.Resolve<IService>("Real"), 
         cont.Resolve<Dispatcher>())); 

    IService created = container.Resolve<IService>() 

En outre, étant donné que l'enregistrement de l'usine est vraiment bavard, et je vais faire plus d'un d'entre eux, je fait une méthode d'extension:

public static class ContainerExtensions 
{ 
    public static void RegisterFactory<T>(this IUnityContainer container, FactoryDelegate factoryDelegate) 
    { 
     container.AddNewExtension<StaticFactoryExtension>() 
      .Configure<IStaticFactoryConfiguration>() 
      .RegisterFactory<T>(factoryDelegate); 
    } 
} 

Répondre

6

Vous pouvez utiliser des surcharges RegisterType acceptant les noms basés sur des chaînes. Dans ce cas, vous allez faire quelque chose comme:

container.RegisterType<IService, RealService>("real"); 
container.RegisterType<IService, DispatcherService>("dispatcher"); 

et d'annoncer vos dépendances avec des noms. Cela vous permettra d'éviter l'interface de marqueur qui, dans la plupart des cas, n'est pas une bonne idée. Cependant, si vous voulez garder votre code propre de la présence d'Unity (comme DependencyAttribute) et dans la plupart des cas, vous n'utiliserez qu'une seule implémentation pendant la durée de vie de l'application (par exemple, seul DispatcherService sera utilisé). pour encapsuler IService demandé avec DispatcherService ou non. Dans ce cas, vous pouvez regarder Static Factory Extension for Unity. Le délégué d'usine sera informé de la configuration et, en fonction de la configuration, il encapsulera IService avec DispatcherService ou retournera simplement l'implémentation IService obtenue à partir du conteneur.

+0

Excellent, merci pour la suggestion d'usine statique. Vous devez ajouter une référence à Microsoft.Practices.Unity.StaticFactory. J'ai posté un exemple de code. – Anthony

0

J'ai découvert que c'est trivial à faire dans le conteneur IoC Castle Windsor. Il suffit d'enregistrer les classes dans le bon ordre - d'abord le wrapper, puis la classe enveloppée, et la bonne chose va se passer.

par exemple.

container.Kernel.AddComponent<IService, DispatcherService>(); 
container.Kernel.AddComponent<IService, RealService>(); 

Non seulement est-ce un beaucoup moins de bruit que dans l'unité, elle conserve l'un des principaux avantages de IoC - que si les paramètres au changement constructeur DispatcherService, aucun autre code doit changer.

Espérons qu'une prochaine version d'Unity intensifiera ce scénario aussi simple qu'à Windsor.