Unity命名映射问题

hIp*_*pPy 3 c# dependency-injection unity-container

我有两个实现IEmailService,一个用于测试,一个用于实时(is-A).我有一个BusinessService有-A IEmailService的参考.

BusinessService
    IEmailService (has-A)

IEmailService
    TestEmailService (is-A)
    LiveEmailService (is-A)
Run Code Online (Sandbox Code Playgroud)

在unity config中,我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>
Run Code Online (Sandbox Code Playgroud)

根据该appSettingIEmailService我要团结,挑选正确实施.这将有助于测试.

<appSettings>
    <add key="IEmailService" value="Test"/>
</appSettings>
Run Code Online (Sandbox Code Playgroud)

问题是当unity解析时BusinessService,它会尝试解析(none)命名映射IEmailService而不是Live或者Test抛出一个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?
Run Code Online (Sandbox Code Playgroud)

我想出了一个解决方法是指定代码的注册以及和周围有一个包装方法container.RegisterType来注册IEmailService使用(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>();
}
Run Code Online (Sandbox Code Playgroud)

这有效,但我最终在两个地方指定了注册 - 配置和代码.这样做有更好的方法吗?

更新

我实际上通过代码得到了它.我根本不需要统一配置.的RegisterType<TFrom, TTo>(string name)寄存器或者所述TestLive如实施(none)命名映射取决于appSetting值.BusinessService也毫无例外地解决了.

由于没有统一配置,我没有加载配置.

container = new UnityContainer();
Run Code Online (Sandbox Code Playgroud)

Raf*_*fal 5

在我看来,在配置中注册的唯一要点是不在代码中使用它们并且能够在不重新编译的情况下替换实现.所以你写的是尝试删除它的表单代码.我不明白为什么你想在配置中首先注册这两个.只需Live从配置中删除测试和Test应用程序的配置,然后在没有名称的情况下注册它们.

例如,在app.config应用程序中:

<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
   <container>
      <register type="DataAccess.IEmailService, DataAccess"
        mapTo="DataAccess.LiveEmailService, DataAccess">
              <lifetime type="singleton" />
     </register>
Run Code Online (Sandbox Code Playgroud)

既然你真的很谦虚地按照你的方式去做:

另一种方法是在代码中注册一种确定哪个实例是默认实例的方法:

 container.RegisterType<IEmailService>(new InjectionFactory((c)=>
   {
        var name = GetImplementationsNameFromAppSettings();
        return c.Resolve<IEmailService>(name);
   });
Run Code Online (Sandbox Code Playgroud)