[英]C#, Unity IoC: Registering and resolving generic interfaces, good practices
[英]Resolving wrapper classes in C# with the Unity IoC container
我想使用Unity将IService
解析为两个不同的实现,以利用包装类,等效于:
IService service = new DispatcherService(new RealService(), Application.Current.Dispatcher);
DispatcherService
和RealService
都在其中实现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 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),则基本上可以决定是否需要包装请求的IService是否使用DispatcherService。 在这种情况下,您可以查看Unity的静态工厂扩展 。 工厂委托将了解配置,并基于配置将IService与DispatcherService包装在一起,或者仅返回从容器获取的IService实现。
我发现在温莎城堡IoC容器中这样做很简单。 只需以正确的顺序注册类-首先是包装器,然后是包装的类,然后正确的事情就会发生。
例如
container.Kernel.AddComponent<IService, DispatcherService>();
container.Kernel.AddComponent<IService, RealService>();
这不仅比Unity少了很多麻烦,而且保留了IoC的主要优势之一-如果DispatcherService构造函数的参数发生更改,则无需更改其他代码。
让我们希望Unity的未来版本能够使该方案像在Windsor中一样简单。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.