问题
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);
}
}
回答1:
You can use RegisterType overloads that accept string based names. In this case you'll do something like:
container.RegisterType<IService, RealService>("real");
container.RegisterType<IService, DispatcherService>("dispatcher");
and announce your dependencies with names as well.
[Dependency("real")]
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.
回答2:
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.
container.Kernel.AddComponent<IService, DispatcherService>();
container.Kernel.AddComponent<IService, RealService>();
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.
来源:https://stackoverflow.com/questions/1989299/resolving-wrapper-classes-in-c-sharp-with-the-unity-ioc-container