Unity: propagate named registration
With Unity (2.0) I register two named interfaces of IFoo
, mapped to two different implementations:
Container
.RegisterType<IFoo, Foo1>("first")
.RegisterType<IFoo, Foo2>("second");
Well, I have a service like this:
public class ServiceImpl : IService {
public ServiceImpl(IFoo foo, IDependency dep, IJustAnother stuff) { ... }
public MyValueObject[] FindVOs() { ... }
}
If want to register two named services each depending on one IFoo
, i write this:
Container
.RegisterType<IService, ServiceImpl>(
"first",
new InjectionConstructor(
new ResolvedParameter<IFoo>("first"),
new ResolvedParameter<IDependency>(),
new ResolvedParameter<IJustAnother>()
)
)
.RegisterType<IService, ServiceImpl>(
"second",
new InjectionConstructor(
new ResolvedParameter<IFoo>("second"),
new ResolvedParameter<IDependency>(),
new ResolvedParameter<IJustAnother>()
)
开发者_JS百科 )
It works, but I find it ugly and not clever, because for each new implementation of IFoo
I need to add two RegisterType
calls.
Is there a better way (maybe with an extension)?
Just another question: how Windsor container deal with this? with auto-registration?
* EDIT *
For example, if I need a CompositeService like this:
public class CompositeService : IService {
public ServiceImpl(IService[] services) { _services = services; }
public MyValueObject[] FindVOs() {
return _services.SelectMany(_ => _.FindVOs() );
}
}
I could register this way:
Container
.RegisterType<IService, CompositeService>();
and Unity will inject all named registration in the composite constructor. The problem is that this way it works only giving a name to all of the IService
. And i can't inject IFoo[]
instead of IService
because IFoo
is used only by ServiceImpl
which is a particular implementation of IService
. This is the reason I asked if there is a better way to register them, avoiding redundancy, in Unity.
Your response in the comments gave an important clue of what you are trying to accomplish:
Foo1 could be an implementation for the administrative pages while Foo2 for normal users
You can effectively solve this by registering an InjectionFactory
. The shortest example I can come up with is the following:
container.Register<IFoo>(new InjectionFactory(c =>
{
if (HttpContext.User.IsInRole("SUPERUSERS"))
return container.Resolve<Foo1>();
return container.Resolve<Foo2>();
}));
You can also solve this by defining a decorator/proxy of IFoo that does this. This keeps your DI configuration a bit cleaner, but does take a bit more code. Here is an example:
using System.Security.Principal;
public class UserBasedFoo : IFoo
{
private readonly IPrincipal currentUser;
private readonly IFoo normalUserFoo;
private readonly IFoo superUserFoo;
public UserBasedFoo(IPrincipal currentUser,
IFoo normalUserFoo, IFoo superUserFoo)
{
this.currentUser = currentUser;
this.normalUserFoo = normalUserFoo;
this.superUserFoo = superUserFoo;
}
object IFoo.FooMethod()
{
return this.CurrentUserFoo.FooMethod();
}
private IFoo CurrentUserFoo
{
get
{
if (this.currentUser.IsInRole("SUPERUSERS"))
return this.superUserFoo;
return this.normalUserFoo;
}
}
}
You can configure this as follows:
container.Register<IPrincipal>(
new InjectionFactory(c => HttpContext.User));
container.Register<IFoo>(new InjectionFactory(c =>
{
return new UserBasedFoo(container.Resolve<IPrincipal>(),
container.Resolve<Foo1>(),
container.Resolve<Foo2>());
}));
Downside of this last approach is that it is more code, but it is more flexible, reusable and testable. This class itself has no dependency on ASP.NET, which is important if you want to unit test it.
I hope this helps.
I solved writing a UnityContainerExtension
(with its own BuilderStrategy
), that allows me to do this:
Container
.RegisterType<IFoo, Foo1>("first")
.RegisterType<IFoo, Foo2>("second")
.RegisterType<IService, ServiceImpl>(
new InjectionPropagator<IFoo>()
)
;
that way i don't need to register a new named IService
for each named IFoo
, for me very useful because I can add named IFoo
registrations in the XML configuration file.
精彩评论