3

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

BusinessService
    IEmailService (has-A)

IEmailService
    TestEmailService (is-A)
    LiveEmailService (is-A)

在统一配置中,我注册了两个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>

基于appSettingforIEmailService我希望 Unity 选择正确的实现。这将有助于测试。

<appSettings>
    <add key="IEmailService" value="Test"/>
</appSettings>

问题是当 unity 解决时BusinessService,它会尝试解决(none)命名映射IEmailService而不是LiveorTest并抛出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(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)注册为命名映射。也无一例外地解决了。TestLive(none)appSettingBusinessService

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

container = new UnityContainer();
4

1 回答 1

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>

既然你真的不愿意按照自己的方式去做:

解决此问题的另一种方法是仅在代码中注册一种确定哪个实例是默认实例的方法:

 container.RegisterType<IEmailService>(new InjectionFactory((c)=>
   {
        var name = GetImplementationsNameFromAppSettings();
        return c.Resolve<IEmailService>(name);
   });
于 2012-11-07T18:36:30.317 回答