代码之家  ›  专栏  ›  技术社区  ›  mayabelle

Castle Windsor DI安装程序:依赖关系工厂方法对ApiController属性具有嵌套依赖关系

  •  0
  • mayabelle  · 技术社区  · 10 年前

    我正在尝试与温莎城堡实施DI。目前,我有一个带有重载构造函数的控制器,如下所述(这是一个反模式: https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=97 ):

    public class MyController : ApiController
    {
        protected IStorageService StorageService;
    
        protected MyController()
        {
            StorageService = StorageServiceFactory.CreateStorageService(User.Identity as ClaimsIdentity);
        }
    
        protected MyController(IStorageService storageService)
        {
            StorageService = storageService;
        }
    }
    

    我正在尝试摆脱第一个构造函数,让Castle Windsor处理存储服务依赖关系的解决。

    我创建了一个Castle Windsor安装程序类,如下所示:

    public class StorageServiceInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(
                Component.For<IStorageService>()
                         .UsingFactoryMethod(
                             () => StorageServiceFactory.CreateStorageService(User.Identity as ClaimsIdentity)));
        }
    }
    

    问题是 User (具有类型 IPrincipal )是上的属性 ApiController ,因此无法从安装程序访问。我怎样才能做到这一点?


    更新:

    @PatrickQuirk似乎在暗示,有一种更好的方法可以在不需要工厂的情况下使用温莎城堡来实现这一点。

    我的StorageServiceFactory如下所示:

    public static class StorageServiceFactory
    {
        public static IStorageService CreateStorageService(ClaimsIdentity identity)
        {
            if (identity == null)
            {
                return null;
            }
    
            Claim providerKeyClaim = identity.FindFirst(ClaimTypes.NameIdentifier);
            if (providerKeyClaim == null || string.IsNullOrEmpty(providerKeyClaim.Value))
            {
                return null;
            }
    
            StorageProviderType storageProviderType;
            string storageProviderString = identity.FindFirstValue("storage_provider");
            if (string.IsNullOrWhiteSpace(storageProviderString) || !Enum.TryParse(storageProviderString, out storageProviderType))
            {
                return null;
            }
    
            string accessToken = identity.FindFirstValue("access_token");
            if (string.IsNullOrWhiteSpace(accessToken))
            {
                return null;
            }
    
            switch (storageProviderType)
            {
                // Return IStorageService implementation based on the type...
            }
        }
    }
    

    是否有一种方法可以结合选择正确的 IStorageService 进入温莎的依赖解决方案,并完全避免工厂?还是我还需要它?

    我喜欢@PatrickQuirk的解决方案,只是为了依赖注入而不得不为工厂创建包装器和相应的包装器接口似乎很奇怪。理想情况下,我会让api控制器的构造函数将IStorageService作为参数,这看起来更直观/更符合实际需要设置的字段。

    1 回复  |  直到 10 年前
        1
  •  2
  •   Patrick Quirk    10 年前

    我不认为多重构造函数是一种罪恶,因为它对 StorageServiceFactory 是的,但我基本上同意你的做法。

    代替工厂方法,将工厂对象传递到类中,并让它创建存储服务:

    public class MyController : ApiController
    {
        protected IStorageService StorageService;
    
        protected MyController(IStorageServiceFactory storageServiceFactory)
        {
            StorageService = storageServiceFactory.CreateStorageService(User.Identity as ClaimsIdentity);
        }
    }
    

    然后定义工厂接口和实现:

    public interface IStorageServiceFactory
    {
        IStorageService Create(ClaimsIdentity claimsIdentity);
    }
    
    public class StorageServiceFactoryImpl : IStorageServiceFactory
    {
        public IStorageService Create(ClaimsIdentity claimsIdentity)
        {
            return StorageServiceFactory.CreateStorageService(claimsIdentity);
        }
    }
    

    这样,您就有了一个构造函数,并且对存储服务工厂的依赖性是明确的。


    关于您的更新:

    ……仅仅为了依赖注入而不得不为工厂创建包装器和相应的包装器接口似乎很奇怪。

    这就是依赖注入的一点。

    我提出的包装器解决了两个问题:它消除了从类内部调用静态方法的需要(隐藏依赖项),并允许延迟解析(因为依赖项依赖于要创建的成员数据)。

    如果您有方法更改创建 IStorageService 不要依赖你交给的班级成员, 然后 您可以直接传入一个(前提是您可以告诉Windsor如何创建一个)。