Unity命名映射问题

时间:2012-11-07 18:26:08

标签: c# dependency-injection unity-container

我有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而非LiveTest的命名映射,并抛出{{} 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)

这样可行,但我最终在两个地方指定了注册 - 配置和代码。有没有更好的方法来做到这一点?

更新

我实际上通过代码得到了正确的答案。我根本不需要统一配置。 appSettingIUnityContainer 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

1 个答案:

答案 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}}