2010-01-01 75 views
2

我想使用統一解決IService兩種不同的實現方式,儘量使用一個包裝類的,相當於:解決包裝類在C#中使用Unity IoC容器

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

如果雙方DispatcherServiceRealService實施接口IService

我有一個包含一些異步操作服務的庫。這種服務的簡化形式如下所示:

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

我有所有這些服務的實現。我希望這個庫保持對WPF和IoC容器的依賴,但是在IoC容器和可能的WPF正在使用的情況下可以最好地使用。

我有一個使用Unity IoC容器的WPF UI。最常見的重複代碼是圍繞完成的處理程序 - 它們需要使用分派器整理回UI線程。所以我想的包裝,像這樣:

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); 
     } 
    } 
} 

我可以將新這個了類似的代碼

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

但整體並不喜歡我創作的兩種不同的實現的事實IService接口。 此代碼可怕的失敗:

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

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

如果我在另外的順序登記服務,首次註冊將被覆蓋,我只是得到一個RealService

Unity有沒有辦法解決這個問題?或者在Unity的AOP中完成了這一切?如果是這樣,那麼在Silverlight中工作嗎?完全可以在原始庫中不使用Unity的情況下完成。

我可以解決一個「標記」接口的子類,即

public interface IServiceWithDispatcher : IService 
{ 

} 

... 

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

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

但我不認爲這是一個好主意,空接口是醜陋的,這將無法很好地擴展。

解決這個對象樹的方法是什麼?


更新:

與Dzmitry虎霸的給出了答案線,下面是一些示例代碼:

增加提及Microsoft.Practices.Unity.StaticFactory

簡單的工作代碼:

UnityContainer container = new UnityContainer(); 

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

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

更完整的工作代碼,更好地處理p otential依賴真正的服務,如IoC容器應:

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>() 

另外,考慮到工廠登記是非常囉嗦了,我會做他們的不止一個,我做了一個擴展方法:

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

回答

6

您可以使用接受基於字符串的名稱的RegisterType重載。在這種情況下,您將執行類似的操作:

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

並宣佈您的名稱依賴關係。

[Dependency("real")] 

這將允許您避免標記界面,這在大多數情況下不是一個好主意。

但是,如果想讓代碼保持乾淨(如DependencyAttribute),並且在大多數情況下在應用程序生命週期中只使用1個實現(例如,只使用DispatcherService),您基本上可以決定是否需要使用DispatcherService包裝請求的IService。在這種情況下,你可以看看Static Factory Extension for Unity。工廠委託會知道配置,並且基於配置將使用DispatcherService包裝IService,或者簡單地返回從容器獲取的IService實現。

+0

非常感謝靜態工廠的建議。 您必須添加對Microsoft.Practices.Unity.StaticFactory的引用。 我發佈了一些示例代碼。 – Anthony 2010-01-01 19:29:37

0

我發現在Castle Windsor IoC容器中做這件事很簡單。只需按照正確的順序註冊課程 - 首先是包裝,然後是包裝課程,然後纔會發生。

例如

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

不僅比團結這個很多做文章少,它保留了國際奧委會的一個關鍵優勢 - 如果參數的構造函數DispatcherService變化,沒有其他代碼需要改變。

讓我們希望,團結的未來版本臺階,以使這種情況一樣簡單溫莎。