【问题标题】:inject different implementations by logged User Role通过记录的用户角色注入不同的实现
【发布时间】:2010-03-25 15:13:11
【问题描述】:
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 ?

【问题讨论】:

    标签: asp.net-mvc dependency-injection inversion-of-control castle-windsor ioc-container


    【解决方案1】:

    IHandlerSelector 是要走的路。使用示例见this post

    或者,如果您更喜欢类似 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

    【讨论】:

      【解决方案2】:

      container-agnostic 方法显然采用了Abstract Factory

      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 类。

      【讨论】:

        【解决方案3】:

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

        如果你有很多角色,你可以使用方法代替内联表达式,例如:

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

        (顺便说一句,“ExternallyOwned”修饰符告诉容器该函数的结果被放置在其他地方,例如通过具体组件。您通常可以将其省略,但它是很好的文档:))

        【讨论】:

        • 据我了解这个答案,您将在应用程序根目录中执行此注册。此时,User 不可用(它是 Controller 类的属性)。
        • 已修复 - 现在使用 HttpContext。谢谢马克。
        • 温莎城堡有能力做这种事情吗?
        • 是的 - 见上面 Krzysztof 的回答。 Windsor 倾向于使用接口而不是 lambda,因为它的可扩展性点,但最终结果是相同的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-09-12
        • 2017-07-07
        • 1970-01-01
        • 2020-08-04
        • 2012-12-05
        • 2011-04-02
        • 1970-01-01
        相关资源
        最近更新 更多