3

IEmailServiceテスト用とライブ用 (is-A) の2 つの実装があります。そしてBusinessService、has-AIEmailService参照があります。

BusinessService
    IEmailService (has-A)

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

Unity config では、次のように 2 つの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>

appSettingforに基づいて、IEmailServiceUnity に正しい実装を選択してもらいます。これは、テスト中に役立ちます。

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

問題は、ユニティが解決するときに、 orの代わりに の名前付きマッピングBusinessServiceを解決しようとし、 をスローすることです。(none)IEmailServiceLiveTestResolutionFailedException

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>();
}

これは機能しますが、登録を 2 つの場所 (構成とコード) で指定することになります。これを行うためのより良い方法はありますか?

アップデート

私は実際にコードでそれを正しくしました。ユニティ構成はまったく必要ありません。は、値に応じてまたは実装を名前付きマッピングとしてRegisterType<TFrom, TTo>(string name)登録します。も例外なく解決されます。TestLive(none)appSettingBusinessService

ユニティ構成がないため、構成をロードしていません。

container = new UnityContainer();
4

1 に答える 1

5

私の意見では、構成に登録する唯一のポイントは、コードに登録せず、再コンパイルせずに実装を置き換えることができることです。したがって、フォームコードを削除しようとしていると書かれています。私が理解していないのは、最初に構成に両方の登録を入れたい理由です。Liveテスト用の構成から 1 つを削除し、Testアプリケーション用の構成から 1 つを削除し、両方を名前なしで登録します。

たとえば、アプリケーション 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>

あなたは本当にあなたのやり方でそれをすることを妨げているので:

これを回避するもう 1 つの方法は、どのインスタンスがデフォルトのインスタンスであるかを判断する方法のみをコードに登録することです。

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