Omu
Omu

Reputation: 71206

inject different implementations by logged User Role

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 ?

Upvotes: 1

Views: 234

Answers (3)

Krzysztof Kozmic
Krzysztof Kozmic

Reputation: 27374

IHandlerSelector 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

Upvotes: 2

Nicholas Blumhardt
Nicholas Blumhardt

Reputation: 31832

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 :))

Upvotes: 1

Mark Seemann
Mark Seemann

Reputation: 233150

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.

Upvotes: 1

Related Questions