внедрить различные реализации вошедшей в систему роли пользователя - PullRequest
1 голос
/ 25 марта 2010
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 ?

Ответы [ 3 ]

2 голосов
/ 25 марта 2010

IHandlerSelector это путь. См. этот пост для примера использования.

В качестве альтернативы, если вы предпочитаете опыт, подобный AutoFac, вы можете использовать для этого фабрику:

container.Register(Component.For<IThe>().UsingFactoryMethod(
  c => HttpContext.Current.User.IsInRole("r1") ?
    c.Resolve<IThe>("r1") :
    c.Resolve<IThe>("r2"));

Или, если вы хотите использовать конкретные IThe только в одном контексте, вы можете использовать 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"));

Однако самый правильный способ сделать это - IHandlerSelector

1 голос
/ 26 марта 2010

контейнерно-независимый подход, очевидно, использует Абстрактную Фабрику :

public interface ITheFactory
{
    IThe Create(IPrincipal user);
}

Вы можете взять зависимость от ITheFactory вместо 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);
    }   
}   

Я не могу вспомнить, заполнен ли this.User в это время, но если это не так, вы можете просто сохранить ссылку на фабрику и лениво разрешить вашу зависимость при первом запросе.

Однако, Controller.User немного особенный, потому что он также должен быть доступен как Thread.CurrentPrincipal. Это означает, что в этом особом случае вам на самом деле не нужно вводить абстрактную фабрику. Вместо этого вы можете написать Decorator , который выполняет выбор при каждом использовании:

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);
    }
}

В этом случае вы сможете использовать исходный класс TheController без изменений.

1 голос
/ 25 марта 2010

В 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();

Если у вас много ролей, вы можете использовать метод вместо встроенного выражения, например ::10000

builder.Register(c => ChooseTheImplementation(c))

(Кстати, модификатор "ExternallyOwned" сообщает контейнеру, что результат функции находится в другом месте, например, через конкретные компоненты. Обычно вы можете не указывать его, но это делает хорошую документацию :))

...