【问题标题】:Restricting Windsor Container from resolving an object based on限制 Windsor Container 基于解析对象
【发布时间】:2014-01-02 23:05:39
【问题描述】:

我想创建一个容器,它允许解析 ISomeService,但不允许解析 ISomeOtherService。即使我对 ISomeService 的注册依赖于 ISomeOtherService。

这有意义吗?

public interface ISomeService {}

public interface ISomeOtherService {}

public class SomeService : ISomeService
{
    public SomeService(ISomeOtherService someOtherService) {}
}
public class SomeOtherService : ISomeOtherService {}

我想要的这个容器会为 ISomeService 解析 SomeService 但如果我尝试解析 ISomeOtherService 或 SomeOtherService 它将失败。

这是糟糕的设计吗?

所以,有一点上下文...我有 ASP.Net MVC 控制器,将由各种开发人员开发。这些控制器应该可以访问 ISomeService 之类的应用程序服务,但不能访问它们的依赖项。我想避免必须对所有这些服务进行代码审查,以确保开发人员没有违反架构设计。他们应该能够获得对 ISomeService 的引用,但 ISomeOtherService 是一个 DB 存储库,他们不应该直接处理它,但 ISomeService 确实需要这个引用。

我不介意在解析过程中希望(它是一个 ASP.NET MVC 应用程序,我已经有一个用于创建控制器的扩展点)所以我可以看看正在解析的控制器,看看它的依赖关系并确保它们在白名单上,但我不知道如何轻松评估与 Windsor 的依赖关系。或者,我只需要自己通过查看构造函数参数来完成吗?

【问题讨论】:

    标签: c# castle-windsor containers windsor-3.0


    【解决方案1】:

    您可以使用 SubDependencyResolver 进行检查。请看下面的代码:

    public class SubDependencyResolver : ISubDependencyResolver
    {
        public bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model,
                               DependencyModel dependency)
        {
            // If not an ISomeotherComponent or SomeOtherComponent is resolved ignore.
            if (dependency.TargetType != typeof (ISomeOtherComponent) && dependency.TargetType != typeof (SomeOtherComponent)) return false;
    
            // check if we are resolving for SomeComponent
            if (model.Implementation == typeof (SomeComponent)) return false;
    
            // We are resolving for a different component then SomeComponent.
            Debug.Assert(false);
            return false;
        }
    
        public object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model,
                              DependencyModel dependency)
        {
            // We will never actually resolve the component, but always use the standard SubDependencyResolver, as Can resolve always returns false;
            return null;
        }
    }
    
    
    class Program
    {
        static void Main(string[] args)
        {
            var container = new WindsorContainer();
            container.Kernel.Resolver.AddSubResolver(new SubDependencyResolver());
            container.Register(
                Component.For<ISomeOtherComponent>().ImplementedBy<SomeOtherComponent>(),
                Component.For<ISomeComponent>().ImplementedBy<SomeComponent>(),
                Component.For<Legal>()
    //          Component.For<Illegal>() uncommenting this line will assert.
                );
        }
    } 
    
    public interface ISomeComponent
    {
    }
    
    public interface ISomeOtherComponent
    {
    }
    
    public class SomeComponent : ISomeComponent
    {
        public SomeComponent(ISomeOtherComponent someOtherComponent)
        {
        }
    }
    
    public class SomeOtherComponent : ISomeOtherComponent
    {
    }
    
    public class Legal
    {
        public Legal(ISomeComponent component)
        {
        }
    }
    
    public class Illegal
    {
        public Illegal(ISomeOtherComponent component)
        {
        }
    }
    

    【讨论】:

      【解决方案2】:

      我希望避免对所有这些服务进行代码审查,以确保开发人员没有违反架构设计。他们应该能够获得对 ISomeService 的引用,但 ISomeOtherService 是一个 DB 存储库,他们不应该直接处理它,但 ISomeService 确实需要这个引用。

      为了达到这个目标,我将完全避免从“WebUI”引用DataAccess,并按以下方式开发它:

      假设我们有项目WebUIApplicationServicesDataAcccess

      WebUI 知道(参考)ApplicationServicesApplicationServices 知道DataAcccess。因此,WebUI 没有直接引用 DataAcccess 类型中的类型,并且不可能在 WebUI 中创建任何存储库的实例。

      网页界面:

      public class HomeController : Controller
      {
          public ActionResult Index()
          {
              var service = new ApplicationServices.SomeService();
              service.DoSmth();
      
              return View();
          }
      }
      

      应用服务:

      public class SomeService : ISomeService
      {
          private readonly ISomeOtherService someOtherService;
      
          public SomeService() : this(new SomeOtherService())
          {
      
          }
      
          public SomeService(ISomeOtherService someOtherService)
          {
              this.someOtherService = someOtherService;
          }
      
          public void DoSmth()
          {
              someOtherService.DoDbCall();
          }
      }
      
      public interface ISomeService
      {
          void DoSmth();
      }
      

      数据访问:

      public class SomeOtherService : ISomeOtherService
      {
          public void DoDbCall()
          {
              /* Db Calls */
          }
      }
      
      public interface ISomeOtherService
      {
          void DoDbCall();
      }
      

      为了从DataAcccess 安装组件,应将installers 放入ApplicationServices

      【讨论】:

        【解决方案3】:

        查看 Windsor 文档,您可能能够利用 UsingFactoryMethod 配置选项来确定是否允许正在实例化的组件使用该组件。

        下面是一个基本实现,其中 Disallowed 类无法解析,因为它依赖于 Restricted,并且只有 Allowed 类能够使用该依赖项。它本质上是一个白名单,除了在 Windsor 中配置。

        class Program
        {
            static void Main(string[] args)
            {
        
                try
                {
                    var container = new Castle.Windsor.WindsorContainer();
        
                    container.Register
                    (
                        Component
                        .For<Restricted>()
                        .UsingFactoryMethod
                        (
                            (k, c) =>
                            {
                                var requestingType = c.Handler.ComponentModel.Implementation;
                                if (requestingType == typeof(Allowed))
                                {
                                    return new RestrictedImp();
                                }
                                else
                                {
                                    var errorMessage = string.Format
                                    (
                                        "The type [{0}] is not permitted to resolve [{1}].", 
                                        requestingType.Name, 
                                        c.RequestedType.Name
                                    );
                                    throw new InvalidOperationException(errorMessage);
                                }
                            }
                        )
                        .LifeStyle
                        .Transient
                    );
                    container.Register(Component.For<Allowed>());
                    container.Register(Component.For<Disallowed>());
        
                    var a = container.Resolve<Allowed>();
                    var b = container.Resolve<Disallowed>();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
        
                Console.ReadLine();
            }
        }
        
        interface Restricted { }
        
        class RestrictedImp : Restricted
        {
        
        }
        
        class Allowed
        {
            public Allowed(Restricted restricted)
            {
        
            }
        }
        
        class Disallowed
        {
            public Disallowed(Restricted restricted)
            {
        
            }
        }
        

        请注意,我实际上对 Castle Windsor 并不是很熟悉,我只是假设它会有类似于 Ninject Bind&lt;T&gt;.ToMethod(blah) 的东西,它允许您在每次解析组件时调用一个方法。似乎有足够的上下文附加到解决方案中,您可以执行一些基本的权限检查。可能有更好的方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-11-13
          • 1970-01-01
          • 1970-01-01
          • 2011-12-15
          • 2023-03-23
          • 1970-01-01
          • 1970-01-01
          • 2011-02-21
          相关资源
          最近更新 更多