我正在尝试用温莎城堡实现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;
}
}
我正在尝试摆脱第一个构造函数,并让温莎城堡处理存储服务依赖项的解析。
我创建了一个温莎城堡安装程序类,如下所示:
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似乎在暗示,使用温莎城堡有更好的方法可以做到这一点,而根本不需要工厂。
My 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 作为参数,这似乎更直观/与实际需要设置的字段一致。
认为多个构造函数像对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
的依赖关系,不依赖于你给它的类的成员,那么你可以直接传入一个(前提是你可以告诉温莎如何创建一个)。