2010-01-01 74 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变化,没有其他代码需要改变。

让我们希望,团结的未来版本台阶,以使这种情况一样简单温莎。