Castle Windsor DI 安装程序:依赖工厂方法对 ApiController 有嵌套依赖 属性
Castle Windsor DI installer: dependency factory method has nested dependency on ApiController property
我正在尝试使用 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 安装程序 class,如下所示:
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
合并到 Windsor 的依赖项解析中并完全避免工厂?还是我还需要它?
我喜欢@PatrickQuirk 的解决方案,只是为了依赖注入而必须为工厂创建包装器和相应的包装器接口似乎很奇怪。理想情况下,我会让 api 控制器的构造函数将 IStorageService 作为参数,这似乎更多 intuitive/consistent 实际需要设置的字段。
我认为多个构造函数不像对 StorageServiceFactory
的隐藏依赖那么严重,但我在很大程度上同意你的方法。
不使用工厂方法,而是将工厂对象传递到 class 并让它创建存储服务:
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);
}
}
这样,您只有一个构造函数,并且对存储服务工厂的依赖是明确的。
关于您的更新:
...it seems odd to have to create a wrapper and corresponding wrapper interface for the factory just for the sake of dependency injection.
好吧,这就是依赖注入的意义所在。
我建议的包装器解决了两个问题:它消除了从 class 内部调用静态方法的需要(隐藏依赖项),并允许延迟解析(因为您的依赖项依赖于成员数据待创建)。
如果您有办法更改创建 IStorageService
的依赖关系,使其不依赖于您提供给它的 class 的成员,那么你可以直接传入一个(前提是你可以告诉Windsor如何创建一个)。
我正在尝试使用 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 安装程序 class,如下所示:
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
合并到 Windsor 的依赖项解析中并完全避免工厂?还是我还需要它?
我喜欢@PatrickQuirk 的解决方案,只是为了依赖注入而必须为工厂创建包装器和相应的包装器接口似乎很奇怪。理想情况下,我会让 api 控制器的构造函数将 IStorageService 作为参数,这似乎更多 intuitive/consistent 实际需要设置的字段。
我认为多个构造函数不像对 StorageServiceFactory
的隐藏依赖那么严重,但我在很大程度上同意你的方法。
不使用工厂方法,而是将工厂对象传递到 class 并让它创建存储服务:
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);
}
}
这样,您只有一个构造函数,并且对存储服务工厂的依赖是明确的。
关于您的更新:
...it seems odd to have to create a wrapper and corresponding wrapper interface for the factory just for the sake of dependency injection.
好吧,这就是依赖注入的意义所在。
我建议的包装器解决了两个问题:它消除了从 class 内部调用静态方法的需要(隐藏依赖项),并允许延迟解析(因为您的依赖项依赖于成员数据待创建)。
如果您有办法更改创建 IStorageService
的依赖关系,使其不依赖于您提供给它的 class 的成员,那么你可以直接传入一个(前提是你可以告诉Windsor如何创建一个)。