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

时间:2015-03-09 17:03:42

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

我正在努力与Castle Windsor一起实施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似乎暗示有一种更好的方法可以使用Castle Windsor而不需要工厂。

我的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 个答案:

答案 0 :(得分:2)

我不认为多个构造函数与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如何创建一个)。