我想使用Unity解析IService
到两个不同的实现,以使用包装类,相当于:
IService service = new DispatcherService(new RealService(), Application.Current.Dispatcher);
其中 DispatcherService
和 RealService
都实现 IService
接口(interface)。
我有一个库,其中包含一些具有异步操作的服务。该服务的简化形式如下所示:
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);
但是 Unity 不喜欢我编写 IService 接口(interface)的两个不同实现。 这段代码严重失败:
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来完成。
我可以使用“标记”接口(interface)子类,即
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);
}
}
最佳答案
您可以使用接受基于字符串的名称的 RegisterType 重载。在这种情况下,您将执行以下操作:
container.RegisterType<IService, RealService>("real");
container.RegisterType<IService, DispatcherService>("dispatcher");
并用名称声明您的依赖项。
[Dependency("real")]
这将使您避免标记界面,这在大多数情况下不是一个好主意。
但是,如果想要保持代码不受 Unity 影响(例如 DependencyAttribute),并且在大多数情况下,您将在应用程序生命周期内仅使用 1 个实现(例如,仅使用 DispatcherService),那么您基本上需要决定是否需要是否使用 DispatcherService 包装请求的 IService。在这种情况下,您可以查看 Static Factory Extension for Unity 。工厂委托(delegate)将了解配置,并根据配置使用 DispatcherService 包装 IService 或简单地返回从容器获取的 IService 实现。
关于c# - 使用 Unity IoC 容器解析 C# 中的包装类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1989299/