2014-02-19 103 views
2

我正在使用MS Unity和'按規則註冊'(自動註冊)來註冊名稱空間中的所有類。的代碼(下面所示)工作在它應該和返回預期的結果MS Unity:解決自動註冊類型

var container = new UnityContainer(); 
     container.RegisterTypes(AllClasses.FromLoadedAssemblies().Where(
      t => t.Namespace == "DependencyInjectionExample.Test"), 
      WithMappings.FromMatchingInterface, 
      WithName.Default, 
      WithLifetime.ContainerControlled); 

結果

容器具有4個登記

  • IUnityContainer '[默認]' 集裝箱
  • TestClass1 '[默認]'ContainerControlled
  • TestClass2'[default]'ContainerControlled
  • TestClass3 '[默認]' ContainerControlled

我的問題是,我無法弄清楚如何解決這些問題。我試過

var allRegistered = container.ResolveAll<ITestable>(); 

但它不返回任何東西(所有的測試類都實現了ITestable)。當我嘗試

var singleRegistered = container.Resolve<ITestable>(); 

我得到'ResolutionFailedException' - 「ITestable類型沒有可訪問的構造函數」。我讀過這是因爲註冊類型沒有被命名,但是在使用自動註冊時這是不可能的。

我應該如何解決註冊類型?

編輯

namespace DependencyInjectionExample.Test 
{ 
public interface ITestable 
{ 
    string SaySomething(); 
} 
} 

其中的三個測試類。他們都做同樣的事情。

namespace DependencyInjectionExample.Test 
{ 
public class TestClass1 : ITestable 
{ 
    public TestClass1() { } 

    public string SaySomething() 
    { 
     return "TestClass1 hello"; 
    } 
} 
} 
+0

如果仔細觀察,您會看到只有具體的類在容器中註冊。在容器中註冊的唯一接口是默認的IUnityContainer引用。你在這些類中實現接口嗎(TestClass1等)?另外,你不能在同一個接口上註冊多個類,所以如果你在這三個TestClasses中實現ITestable,它將不起作用。發佈容器中註冊的類的代碼,以便我們更好地瞭解發生了什麼。 –

+0

如果我只能爲一個接口註冊一個類,那麼有什麼意義?我希望能夠將實現接口的類添加到名稱空間,然後自動解決它們。例如通過獲取具體類的列表。 – barto90

+0

這就是爲什麼你有一個用名字註冊每個接口的方法。如果你想爲一個接口有3個不同的實例,那麼你必須使用該方法手動註冊每個實例。對不起,但這是你實現你想要的唯一方法。 _你可能想看看MEF_。它隨框架一起提供,我認爲它支持您需要的所有功能。 –

回答

7

我仔細看了一下RegisterTypes,它接受了一個Func類型的參數,您可以使用它來爲每個實例提供名稱。沒有必要創建一個擴展來實現這一點。在創建屬性後,很容易爲要在容器中註冊的每個實例提供特定的名稱。

首先創建屬性類:

[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] 
public sealed class UnityNamedInstanceAttribute : Attribute 
{ 
    private readonly string instanceName; 

    public UnityNamedInstanceAttribute(string instanceName) 
    { 
     this.instanceName = instanceName; 
    } 

    public string InstanceName 
    { 
     get { return this.instanceName; } 
    } 
} 

然後成立專班,以幫助解決實例的名稱:

public static class UnityNamedInstance 
{ 
    public static string AttributeName(Type type) 
    { 
     var namedAttribute = type 
      .GetCustomAttributes(typeof(UnityNamedInstanceAttribute), true) 
      .FirstOrDefault() 
      as UnityNamedInstanceAttribute; 
     return namedAttribute != null ? namedAttribute.InstanceName : null; 
    } 

    public static string AttributeNameOrDefault(Type type) 
    { 
     return UnityNamedInstance.AttributeName(type) ?? WithName.Default(type); 
    } 
} 

使用我們ealier創建的屬性聲明你的測試類:

public interface ITest 
{ 
    void DebugName(); 
} 

[UnityNamedInstance("Test A")] 
public class TestA : ITest 
{ 
    #region ITest Members 

    public void DebugName() 
    { 
     Debug.WriteLine("This is TestA"); 
    } 

    #endregion 
} 
[UnityNamedInstance("Test B")] 
public class TestB : ITest 
{ 
    #region ITest Members 

    public void DebugName() 
    { 
     Debug.WriteLine("This is TestB"); 
    } 

    #endregion 
} 

[UnityNamedInstance("Test C")] 
public class TestC : ITest 
{ 
    #region ITest Members 

    public void DebugName() 
    { 
     Debug.WriteLine("This is TestC"); 
    } 

    #endregion 
} 

並按常規方法將您的註冊更改爲:

 container.RegisterTypes(
      AllClasses.FromLoadedAssemblies().Where(t => t.Namespace == "DependencyInjectionExample.Test"), 
      WithMappings.FromAllInterfaces, // This way you have the same instance for each interface the class implements 
      UnityNamedInstance.AttributeNameOrDefault, // Use our helper to solve the name of the instance 
      WithLifetime.ContainerControlled); 

或者你可以只避免創建屬性和輔助並命名類名後的實例,如下所示:

 container.RegisterTypes(
      AllClasses.FromLoadedAssemblies().Where(t => t.Namespace == "DependencyInjectionExample.Test"), 
      WithMappings.FromAllInterfaces, 
      WithName.TypeName, // Use the type name for the instances 
      WithLifetime.ContainerControlled); 

現在你可以使用傳遞給名稱訪問每個類實例在類聲明的屬性構造函數或類名:

 // Access each instance using the name you gave in the attribute 
     container.Resolve<ITest>("Test A").DebugName(); 
     container.Resolve<ITest>("Test B").DebugName(); 
     container.Resolve<ITest>("Test C").DebugName(); 

如果你想所有註冊的情況下,實現一個特定的接口得到:

 foreach (var test in container.Resolve<ITest[]>()) { 
      test.DebugName(); 
     } 

OR

 foreach (var test in container.ResolveAll<ITest>()) { 
      test.DebugName(); 
     } 

我非常有興趣知道這是否符合您的需求。感謝您的反饋!

+0

這正是我正在尋找的解決方案。坦克你這麼多。 – barto90

+0

沒有問題的隊友,只是抱歉花了這麼久回來的解決方案。 –

0

丹尼斯布拉特的評論有它的關鍵項目,幫助我解決了這個問題。

如果您只使用帶有接口的構造函數的通用存儲庫,則註冊類型的GetInstance方法會失敗並顯示依賴性異常。

如果您註冊界面手動之前,您自動註冊它的工作原理的具體類,即

ViewModelLocator:

unityContainer.RegisterType(typeof(IGenericRepository<>), typeof(GenericRepository<>)); 

unityContainer.RegisterTypes(AllClasses.FromAssembliesInBasePath().Where(t => t.Namespace.StartsWith("Mynamespace.")), 
      WithMappings.FromAllInterfaces, 
      WithName.TypeName, 
      WithLifetime.ContainerControlled,null,true); 

型號:

public MainViewModel(IGenericRepository<Client> clientRepository, IExceptionManager exceptionManager) 
    { whatever } 

意見:

return ServiceLocator.Current.GetInstance<MainViewModel>("MainViewModel"); 

它會找到它們並返回正確的實例。