我有IEmailService
的两个实现,一个用于测试,一个用于live(is-A)。我有一个BusinessService
有一个IEmailService
参考。
BusinessService
IEmailService (has-A)
IEmailService
TestEmailService (is-A)
LiveEmailService (is-A)
在unity配置中,我按如下方式注册两个IEmailService
实现。
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<container>
<register type="DataAccess.IEmailService, DataAccess"
mapTo="DataAccess.LiveEmailService, DataAccess"
name="Live">
<lifetime type="singleton" />
</register>
<register type="DataAccess.IEmailService, DataAccess"
mapTo="DataAccess.TestEmailService, DataAccess"
name="Test">
<lifetime type="singleton" />
</register>
<container>
</unity>
基于appSetting
IEmailService
我希望Unity选择正确的实现。这将有助于测试。
<appSettings>
<add key="IEmailService" value="Test"/>
</appSettings>
问题是当Unity解析BusinessService
时,它会尝试解析(none)
IEmailService
而非Live
或Test
的命名映射,并抛出{{} 1}}。
ResolutionFailedException
抛出异常:
container.Resolve<BusinessService>();
我提出的解决方法是在代码中指定注册,并在BusinessServices.Test.BusinessServiceTest_Integration.Test103:
Microsoft.Practices.Unity.ResolutionFailedException : Resolution of the dependency failed, type = "BusinessServices.BusinessService", name = "(none)".
Exception occurred while: while resolving.
Exception is: InvalidOperationException - The current type, DataAccess.IEmailService, is an interface and cannot be constructed. Are you missing a type mapping?
-----------------------------------------------
At the time of the exception, the container was:
Resolving BusinessServices.BusinessService,(none)
Resolving parameter "emailService" of constructor BusinessServices.BusinessService(DataAccess.IEmailService emailService)
Resolving DataAccess.IEmailService,(none)
----> System.InvalidOperationException : The current type, DataAccess.IEmailService, is an interface and cannot be constructed. Are you missing a type mapping?
周围设置一个包装方法,以container.RegisterType
注册IEmailService
命名映射,同时基于{ {1}}价值。
(none)
这样可行,但我最终在两个地方指定了注册 - 配置和代码。有没有更好的方法来做到这一点?
更新
我实际上通过代码得到了正确的答案。我根本不需要统一配置。 appSetting
将IUnityContainer container;
// registering unity
static void Load()
{
container = new UnityContainer().LoadConfiguration();
RegisterType<IEmailService, TestEmailService>("Test");
RegisterType<IEmailService, LiveEmailService>("Live");
}
// register the `Test` or `Live` implementation with `(none)` named mapping as per appSetting
static void RegisterType<TFrom, TTo>(string name)
where TTo : TFrom
{
var tFromAppSetting= ConfigurationManager.AppSettings[typeof(TFrom).Name];
if (!string.IsNullOrEmpty(tFromAppSetting) && tFromAppSetting == name)
container.RegisterType<TFrom, TTo>();
}
或RegisterType<TFrom, TTo>(string name)
实施 {<1}}或Test
实施为 Live
命名映射,具体取决于(none)
值。 appSetting
也毫无例外地得到解决。
由于没有统一配置,我没有加载配置。
BusinessService
答案 0 :(得分:5)
在我看来,在配置中注册的唯一目的是不在代码中使用它们并且能够在不重新编译的情况下替换实现。所以你写的是尝试删除它的表单代码。我不明白为什么你想在配置中首先注册这两个。只需从配置中删除Live
一个用于测试的Test
,从配置中删除<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<container>
<register type="DataAccess.IEmailService, DataAccess"
mapTo="DataAccess.LiveEmailService, DataAccess">
<lifetime type="singleton" />
</register>
,然后在没有名称的情况下注册它们。
例如在应用程序app.config中:
container.RegisterType<IEmailService>(new InjectionFactory((c)=>
{
var name = GetImplementationsNameFromAppSettings();
return c.Resolve<IEmailService>(name);
});
因为你确实无法按照自己的方式行事:
另一种方法是在代码中注册一种确定哪个实例是默认实例的方法:
{{1}}