public class TheController : Controller
{
IThe the;
public TheController( IThe the)
{
//when User.IsInRole("r1") The1 should be injected else r2
this.the = the;
}
}
public class The1 : IThe{}
public class The2 : IThe{}
//anybody knows a good way of doing this ?
views:
55answers:
3IHandlerSelector
is the way to go. See this post for an example of usage.
Alternatively if you prefer AutoFac-like experience you can use factory for that:
container.Register(Component.For<IThe>().UsingFactoryMethod(
c => HttpContext.Current.User.IsInRole("r1") ?
c.Resolve<IThe>("r1") :
c.Resolve<IThe>("r2"));
Or if you want to use specific IThe
just in one context, you can use DynamicParameters:
container.Register(Component.For<TheFactory>().Lifestyle.Transient.DynamicParameters(
(c, d) => HttpContext.Current.User.IsInRole("r1") ?
d["the"] = c.Resolve<IThe>("r1") :
c.Resolve<IThe>("r2"));
However the most correct way of doing this is IHandlerSelector
In Autofac:
var builder = new ContainerBuilder();
// Give the different implementations names
builder.RegisterType<The1>.Named<IThe>("r1");
builder.RegisterType<The2>.Named<IThe>("r2");
// Use a function for the default IThe
builder.Register(
c => HttpContext.Current.User.IsInRole("r1") ?
c.Resolve<IThe>("r1") :
c.Resolve<IThe>("r2"))
.As<IThe>()
.ExternallyOwned();
If you have a lot of roles, you can use a method instead of the inline expression, e.g.:
builder.Register(c => ChooseTheImplementation(c))
(BTW, The "ExternallyOwned" modifier tells the container that the result of the function is disposed elsewhere, e.g. via the concrete components. You can usually leave it out but it makes good documentation :))
The container-agnostic approach obviously employs an Abstract Factory:
public interface ITheFactory
{
IThe Create(IPrincipal user);
}
You can take a dependency on ITheFactory instead of IThe:
public class TheController : Controller
{
private readonly IThe the;
public TheController(ITheFactory theFactory)
{
if (theFactory == null)
{
throw new ArgumentNullException("theFactory");
}
this.the = theFactory.Create(this.User);
}
}
I can't really remember if this.User
is populated at this time, but if it isn't, you can just keep a reference to the factory and lazily resolve your dependency the first time it's requested.
However, Controller.User
is a bit special because it ought to be available as Thread.CurrentPrincipal
as well. This means that in this special case you don't actually have to introduce an Abstract Factory. Instead, you can write a Decorator that performs the selection every time it's used:
public class UserSelectingThe : IThe
{
private readonly IThe the1;
private readonly IThe the2;
public UserSelectingThe(IThe the1, IThe the2)
{
if (the1 == null)
{
throw new ArgumentNullException("the1");
}
if (the2 == null)
{
throw new ArgumentNullException("the2");
}
this.the1 = the1;
this.the2 = the2;
}
// Assuming IThe defines the Foo method:
public Baz Foo(string bar)
{
if (Thread.CurrentPrincipal.IsInRole("r1"))
{
return this.the1.Foo(bar);
}
return this.the2.Foo(bar);
}
}
In this case, you would be able to use your original TheController class unchanged.