使用Unity IoC容器解决C#中的包装类

时间:2010-01-01 18:50:25

标签: c# unity-container ioc-container wrapper

我想将Unity resolve IService用于两个不同的实现,以使用包装类,相当于:

 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。最常见的重复代码围绕已完成的处理程序 - 需要使用Dispatcher将它们编组回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的AOP完成的吗?如果是这样,那么它在Silverlight中有效吗?可以在原始库中完全不使用Unity的情况下完成。

我可以使用“marker”接口子类,即

public interface IServiceWithDispatcher : IService
{

}

...

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

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

但我认为这不是一个好主意,空白的界面很丑陋,而且不会很好地扩展。

解决此对象树的方法是什么?


更新

根据Dzmitry Huba给出的答案,这里有一些示例代码:

添加对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>();

更完整的工作代码,更好地处理真实服务的潜在依赖关系,如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);
    }
}

2 个答案:

答案 0 :(得分:6)

您可以使用接受基于字符串的名称的RegisterType重载。在这种情况下,您将执行以下操作:

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

并使用名称宣布您的依赖项。

[Dependency("real")]

这将允许您避免标记界面,这在大多数情况下不是一个好主意。

但是,如果想要保持代码在Unity存在(例如DependencyAttribute)中清除,并且在大多数情况下,您将在应用程序生命周期中仅使用1个实现(例如,仅使用DispatcherService),您基本上可以决定是否需要是否使用DispatcherService包装请求的IService。在这种情况下,您可以查看Static Factory Extension for Unity。工厂代理将了解配置,并根据配置将IService与DispatcherService包装在一起,或者只返回从容器中获取的IService实现。

答案 1 :(得分:0)

我发现在Castle Windsor IoC容器中这是微不足道的。只需按正确的顺序注册类 - 首先是包装器,然后是包装类,然后就会发生正确的事情。

e.g。

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

与Unity相比,这不仅仅是很多,它保留了IoC的一个主要优点 - 如果DispatcherService构造函数的参数发生变化,则不需要更改其他代码。 / p>

让我们希望Unity的未来版本能够让这个场景像Windsor一样简单。