2016-03-29 283 views
3

我在Unity中使用C#。我有一個接口我叫IConnectionStringLoader,它有兩個派生的接口。Unity註冊互相重疊

​​

它只有一個執行:

public class ConnectionStringLoader : IDbConnectionStringLoader, IMetaDataConnectionStringLoader 
{ 
    private readonly string _connectionStringName; 

    public ConnectionStringLoader(string connectionStringName) 
    { 
     _connectionStringName = connectionStringName; 
    } 

    public string Get() 
    { 
     var cs = ConfigurationManager.ConnectionStrings[_connectionStringName]; 
     if (cs != null) 
     { 
      return cs.ConnectionString; 
     } 
     return null; 
    } 

    public void Write() 
    { 
     Console.WriteLine(_connectionStringName); 
    } 
} 

我的註冊看起來像這樣:

container.RegisterType<IMetaDataConnectionStringLoader, ConnectionStringLoader>(new InjectionConstructor("MetaConnection")); 
container.RegisterType<IDbConnectionStringLoader, ConnectionStringLoader>(new InjectionConstructor("DbConnection")); 

接口的一點是,我可以在我的課注入不同的接口和爲每個實現獲取正確的連接字符串。 但問題是,無論什麼註冊完成最後將覆蓋前一個。

var foo = _container.Resolve<IDbConnectionStringLoader>(); 
var bar = _container.Resolve<IMetaDataConnectionStringLoader>(); 
foo.Write(); 
bar.Write(); 

輸出是:

DbConnection 
DbConnection 

如果I反註冊的輸出將是MetaConnection兩次的順序。所以我迄今爲止的結論是最後一次註冊覆蓋了前一次註冊。但是,如果我改變實現派生類它的工作原理:

public class SomeOtherConnectionStringLoader : ConnectionStringLoader 
{ 
    public ConnectionStringLoaderImpl(string connectionStringName) : base(connectionStringName) 
    { 
    } 
} 

並更改註冊:

container.RegisterType<IMetaDataConnectionStringLoader, ConnectionStringLoader>(new InjectionConstructor("MetaConnection")); 
container.RegisterType<IDbConnectionStringLoader, SomeOtherConnectionStringLoader >(new InjectionConstructor("DbConnection")); 

現在一切正常,但我不明白爲什麼。我嘗試過不同的終生管理者,但結果相同。我認爲Unity會嘗試用基於界面的「正確」注入參數來創建一個ConnectionStringLoader的實例,但在這裏似乎還有其他一些邏輯。

任何建議爲什麼註冊覆蓋對方?

+0

你是如何調用'Write'雖然它不是一部分的界面? –

+0

好找@YacoubMassad。我在這裏粘貼代碼後創建了一個簡單的測試。這不是邏輯的一部分,除了證明錯誤的注入已被注入外。我將編輯我的問題。感謝您的注意。 – smoksnes

回答

0

我不熟悉Unity。但它似乎是映射到同一個實例。所以你應該改變ConnectionStringLoader的生命週期(Per依賴)。

如果你不會分享實例,爲什麼你把所有東西放在一個類中? ConnectionStringLoader方法= IDbConnectionStringLoader方法+ IMetaDataConnectionStringLoader方法。

當您解析IDbConnectionStringLoader時,它將不會使用已在實例中的IMetaDataConnectionStringLoader方法(反之亦然)。

裝箱兩個不同的派生類在這一點上更好:

抽象類:

public abstract class ConnectionStringLoader : IConnectionStringLoader 
{ 
    private readonly string _connectionStringName; 

    public ConnectionStringLoader(string connectionStringName) 
    { 
     _connectionStringName = connectionStringName; 
    } 

    public string Get() 
    { 
     var cs = ConfigurationManager.ConnectionStrings[_connectionStringName]; 
     if (cs != null) 
     { 
      return cs.ConnectionString; 
     } 
     return null; 
    } 

    public void Write() 
    { 
     Console.WriteLine(_connectionStringName); 
    } 
} 

派生類:

public sealed class DbConnectionStringLoader : ConnectionStringLoader, IDbConnectionStringLoader 
{ 
    public DbConnectionStringLoader(string connectionStringName):base(connectionStringName) 
    { 

    } 
    //Implement methods here just belongs to IDbConnectionStringLoader 
} 

public sealed class MetaDataConnectionStringLoader : ConnectionStringLoader, IMetaDataConnectionStringLoader 
{ 
    public MetaDataConnectionStringLoader(string connectionStringName):base(connectionStringName) 
    { 

    } 
    //Implement methods here just belongs to IMetaDataConnectionStringLoader 
} 
+0

是的,它似乎是映射到同一個實例,即使我使用TransientLifetimeManager。創建兩個實現可能是解決它的唯一方法,但是我想避免它,因爲實現沒有任何作用。它們沒有與ConnectionStringLoader不同的特定實現。 – smoksnes

+0

我不認爲它們映射到相同的實例。它確實調用了ConnectionStringLoader控制器兩次,但是具有相同的Injection參數值,即在這種情況下「DbConnection」 – AksharRoop

0

令人驚訝的是不會調用ConnectionStringLoader ctor的兩倍,但與同注射構件。如果你看看container.Registrations,確實有兩個註冊,所以它不會與其他註冊。我確實看過RegisterType的實現,但沒有得到我的頭腦。

一種替代方法是命名您的註冊,不確定它是否符合您的整體統一引導策略。

container.RegisterType<IMetaDataConnectionStringLoader, ConnectionStringLoader>("bar", new InjectionConstructor("MetaConnection")); 
container.RegisterType<IDbConnectionStringLoader, ConnectionStringLoader>("foo", new InjectionConstructor("DbConnection")); 

var foo = container.Resolve<IDbConnectionStringLoader>("foo"); 
var bar = container.Resolve<IMetaDataConnectionStringLoader>("bar"); 
1

老實說,因爲只有由同一個類實現了兩個接口,你正在使用的接口方式看起來很奇怪我。我會覺得更自然的使用註冊名稱按照下面的方法:

// If it is a loader the Write method makes no sense (IConnectionStringRepository?) 
public interface IConnectionStringLoader 
{ 
    string Get(); 
    void Write(); 
} 

public class ConnectionStringLoader : IConnectionStringLoader 
{ 
    private readonly string _connectionStringName; 

    public ConnectionStringLoader(string connectionStringName) 
    { 
     _connectionStringName = connectionStringName; 
    } 

    public string Get() 
    { 
     var cs = ConfigurationManager.ConnectionStrings[_connectionStringName]; 
     if (cs != null) 
     { 
      return cs.ConnectionString; 
     } 
     return null; 
    } 

    public void Write() 
    { 
     Console.WriteLine(_connectionStringName); 
    } 
} 

註冊執照:

container.RegisterType<IConnectionStringLoader, ConnectionStringLoader>("Database", new InjectionConstructor("MetaConnection")); 
container.RegisterType<IConnectionStringLoader, ConnectionStringLoader>("Metadata", new InjectionConstructor("DbConnection")); 

決議:

var foo = _container.Resolve<IConnectionStringLoader>("Database"); 
var bar = _container.Resolve<IConnectionStringLoader>("Metadata"); 
foo.Write(); 
bar.Write(); 
+0

是的,我也考慮過這種方法。但是,我的實現需要知道註冊名稱,這就是爲什麼我試圖使用不同的接口。我認爲你應該儘量避免使用註冊名稱,因爲。或者我錯了? – smoksnes

+0

我們通常在構造函數中注入所有東西,所以我們在註冊時完成所有工作。在類中使用容器通常被認爲是一種反模式(搜索ServiceLocator反模式)。考慮到這一點,問題始終存在於容器註冊中,在解析構造函數依賴項時必須指定要解析的名稱(它與InjectionConstructor類似) –