我刚刚开始使用Unity IOC,希望有人能提供帮助。我需要能够在运行时切换Unity中的依赖项。我有两个容器,分别用于生产和开发/测试环境," prodRepository"和" testRepository"在web.config中定义如下:
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias alias="TestDataService" type="MyApp.API.Data.TestDataRepository.TestDataService, MyApp.API.Data" />
<alias alias="ProdDataService" type="MyApp.API.Data.ProdDataRepository.ProdDataService, MyApp.API.Data" />
<assembly name="MyApp.API.Data" />
<container name="testRepository">
<register type="MyApp.API.Data.IDataService" mapTo="TestDataService">
<lifetime type="hierarchical" />
</register>
</container>
<container name="prodRepository">
<register type="MyApp.API.Data.IDataService" mapTo="ProdDataService">
<lifetime type="hierarchical" />
</register>
</container>
</unity>
在WebApiConfig类中,Unit配置如下
public static void Register(HttpConfiguration config)
{
config.DependencyResolver = RegisterUnity("prodRepository");
//... api configuration ommitted for illustration
}
public static IDependencyResolver RegisterUnity(string containerName)
{
var container = new UnityContainer();
container.LoadConfiguration(containerName);
return new UnityResolver(container);
}
为了测试,我创建了一个简单的控制器和操作来切换配置:
[HttpGet]
public IHttpActionResult SwitchResolver(string rName)
{
GlobalConfiguration.Configuration
.DependencyResolver = WebApiConfig.RegisterUnity(rName);
return Ok();
}
我从网络浏览器中调用它: http://localhost/MyApp/api/Test/SwitchResolver?rName=prodRepository
当我尝试通过API从存储库中检索实际数据时,首先它来自&#34; prodRepository&#34;,可以理解,因为它是如何在代码中初始化的。我把它切换到&#34; testRepository&#34;从浏览器中,数据来自测试仓库,如预期的那样。当我将其切换回prodRepository时,API会不断向我发送测试仓库中的数据。 我在控制器中看到GlobalConfiguration.Configuration .DependencyResolver按预期将容器和注册更改为URL查询中指定的容器和注册,但它似乎只更改配置一次然后保持在该配置。
好的,所以这个邪恶的计划就是我想出来的,但由于我是新手,我可能完全是错误的方向。 我需要能够在运行时动态指定要使用哪个容器,希望无需重新加载API。以上代码是否有意义或您建议什么?
答案 0 :(得分:2)
看起来你在很多方面都出错:
以下是#4的例子:
注意: WebApi是无状态的。请求结束后,它不会在服务器上存储任何内容。此外,如果您的WebApi客户端不是浏览器,您可能无法使用session state等技术来存储您从一个请求访问哪个数据提供者,因为这取决于cookie。
因此,采取
SwitchResolver
行动可能是荒谬的。您应该在每个请求上提供存储库,或者具有可以使用每个请求的参数覆盖的默认存储库。
public interface IDataService
{
void DoSomething();
bool AppliesTo(string provider);
}
public interface IDataServiceStrategy
{
void DoSomething(string provider);
}
public class TestDataService : IDataService
{
public void DoSomething()
{
// Implementation
}
public bool AppliesTo(string provider)
{
return provider.Equals("testRepository");
}
}
public class ProdDataService : IDataService
{
public void DoSomething()
{
// Implementation
}
public bool AppliesTo(string provider)
{
return provider.Equals("prodRepository");
}
}
这是完成所有繁重任务的班级。
有一个GetDataService
方法,它根据传入的字符串返回所选服务。请注意,您也可以公开此方法,以便将IDataService
的实例返回给您的控制器,这样您就不必进行DoSomething
的两次实现。
public class DataServiceStrategy
{
private readonly IDataService[] dataServices;
public DataServiceStrategy(IDataService[] dataServices)
{
if (dataServices == null)
throw new ArgumentNullException("dataServices");
this.dataServices = dataServices;
}
public void DoSomething(string provider)
{
var dataService = this.GetDataService(provider);
dataService.DoSomething();
}
private IDataService GetDataService(string provider)
{
var dataService = this.dataServices.Where(ds => ds.AppliesTo(provider));
if (dataService == null)
{
// Note: you could alternatively use a default provider here
// by passing another parameter through the constructor
throw new InvalidOperationException("Provider '" + provider + "' not registered.");
}
return dataService;
}
}
请参阅这些替代实施以获得灵感:
Best way to use StructureMap to implement Strategy pattern
Factory method with DI and Ioc
这里我们使用container extension而不是XML配置向Unity注册服务。
您还应确保使用correct way to register Unity with WebApi as per MSDN。
public static IDependencyResolver RegisterUnity(string containerName)
{
var container = new UnityContainer();
container.AddNewExtension<MyContainerExtension>();
return new UnityResolver(container);
}
public class MyContainerExtension
: UnityContainerExtension
{
protected override void Initialize()
{
// Register data services
// Important: In Unity you must give types a name in order to resolve an array of types
this.Container.RegisterType<IDataService, TestDataService>("TestDataService");
this.Container.RegisterType<IDataService, ProdDataService>("ProdDataService");
// Register strategy
this.Container.RegisterType<IDataServiceStrategy, DataServiceStrategy>(
new InjectionConstructor(new ResolvedParameter<IDataService[]>()));
}
}
public class SomeController : ApiController
{
private readonly IDataServiceStrategy dataServiceStrategy;
public SomeController(IDataServiceStrategy dataServiceStrategy)
{
if (dataServiceStrategy == null)
throw new ArgumentNullException("dataServiceStrategy");
this.dataServiceStrategy = dataServiceStrategy;
}
// Valid values for rName are "prodRepository" or "testRepository"
[HttpGet]
public IHttpActionResult DoSomething(string rName)
{
this.dataServiceStrategy.DoSomething(rName);
return Ok();
}
}
我强烈建议您阅读Mark Seemann撰写的Dependency Injection in .NET一书。它将帮助您走上正确的道路,并帮助您为应用程序做出最佳选择,因为它们适用于DI,这比我在SO上的单个问题上可以回答的要多。