使用 Unity IoC 容器解析 C# 中的包装类
我想使用 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。最常见的重复代码是围绕已完成的处理程序 - 需要使用 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 有办法解决这个问题吗?或者这是用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);
}
}
I want to use Unity resolve IService
to two different implementations, to make use of a wrapper class, the equivalent of:
IService service = new DispatcherService(new RealService(), Application.Current.Dispatcher);
Where both DispatcherService
and RealService
implement the IService
interface.
I have a library containing some services with asynchronous operations. A simplified form of this service looks like this:
public interface IService
{
IAsyncResult StartSomeOperation();
event EventHandler<EventArgs> SomeOperationCompleted;
}
I have implementations for all of these services. I want this library to remain free of dependencies on WPF and on IoC containers, but ready for best use in cases where IoC containers and possibly WPF are in use.
I have a WPF Ui using the Unity IoC container. The most common repetitive code is around the completed handlers - they need to be marshalled back onto the UI thread using the Dispatcher. So I'm thinking about a wrapper, like this:
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);
}
}
}
I can new this up with code like
IService service = new DispatcherService(new RealService(), Application.Current.Dispatcher);
But unity does not like the fact that I am composing two different implementations of the IService interface.
This code fails horribly:
UnityContainer container = new UnityContainer();
container.RegisterInstance<Dispatcher>(Application.Current.Dispatcher);
container.RegisterType<IService, RealService>();
container.RegisterType<IService, DispatcherService>();
IService created = container.Resolve<IService>();
And if I register the services in the other order, the first registration is overwritten and I just get a RealService
.
Is there a way around this with Unity? Or has this been done with Unity's AOP? And if so, does that work in Silverlight? Can it be done without using Unity in the original library at all.
I can work around with a "marker" interface subclass, i.e.
public interface IServiceWithDispatcher : IService
{
}
...
UnityContainer container = new UnityContainer();
container.RegisterInstance<Dispatcher>(Application.Current.Dispatcher);
container.RegisterType<IService, RealService>();
container.RegisterType<IServiceWithDispatcher, DispatcherService>();
But I don't think that this is such a good idea, the empty interface is ugly and this won't scale well.
What's the way to resolve this object tree?
Update:
In line with Dzmitry Huba's the answer given, here is some sample code:
Add a reference to Microsoft.Practices.Unity.StaticFactory
Simple working code:
UnityContainer container = new UnityContainer();
container.AddNewExtension<StaticFactoryExtension>()
.Configure<IStaticFactoryConfiguration>()
.RegisterFactory<IService>(cont =>
new DispatcherService(new RealService(), Application.Current.Dispatcher));
IService created = container.Resolve<IService>();
More complete working code, that deals better with the potential dependencies the real service, like an IoC container should:
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>()
Also, considering that the factory registration is really verbose, and I will be doing more than one of them, I made an extension method:
public static class ContainerExtensions
{
public static void RegisterFactory<T>(this IUnityContainer container, FactoryDelegate factoryDelegate)
{
container.AddNewExtension<StaticFactoryExtension>()
.Configure<IStaticFactoryConfiguration>()
.RegisterFactory<T>(factoryDelegate);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您可以使用接受基于字符串的名称的 RegisterType 重载。在这种情况下,您将执行以下操作:
并用名称声明您的依赖项。
这将使您避免标记界面,这在大多数情况下不是一个好主意。
但是,如果想要保持代码不受 Unity 影响(例如 DependencyAttribute),并且在大多数情况下,您将在应用程序生命周期内仅使用 1 个实现(例如,仅使用 DispatcherService),那么您基本上需要决定是否需要包装请求的 IService是否使用 DispatcherService。在这种情况下,您可以查看 静态工厂扩展统一。工厂委托将了解配置,并根据配置将 IService 与 DispatcherService 包装在一起,或者简单地返回从容器获取的 IService 实现。
You can use RegisterType overloads that accept string based names. In this case you'll do something like:
and announce your dependencies with names as well.
This will allow you to avoid marker interface which is in most cases is not a good idea.
However if want to keep your code clean from Unity presence (like DependencyAttribute) and in most cases you will use only 1 implementation during application lifetime (for example, only DispatcherService will be used) you basically to make decision whether you need to wrap requested IService with DispatcherService or not. In this case you can look at Static Factory Extension for Unity. Factory delegate will be aware of configuration and based on configuration will either wrap IService with DispatcherService or simply return IService implementation obtained from container.
我发现这在 Castle Windsor IoC 容器中是微不足道的。只需以正确的顺序注册类 - 首先是包装器,然后是包装的类,正确的事情就会发生。
例如,
这不仅比 Unity 中的麻烦少得多,而且还保留了 IoC 的关键优势之一 - 如果 DispatcherService 构造函数的参数发生变化,则无需更改其他代码。
我们希望 Unity 的未来版本能够让这个场景像温莎一样简单。
I have found out that this is trivial to do in the Castle Windsor IoC container. Just register the classes in the right order - first the wrapper, then the wrapped class, and the right thing will happen.
e.g.
Not only is this a lot less fuss than in Unity, it preserves one of the key advantages of IoC - that if the parameters to the DispatcherService constructor change, no other code needs to change.
Let's hope that a future version of Unity steps up to make this scenario as simple as in Windsor.