2016-03-29 38 views
3

Ich verwende Unity in C#. Ich habe eine Schnittstelle, die ich IConnectionStringLoader nenne, die zwei abgeleitete Schnittstellen haben.Unity-Registrierungen überschreiben einander

public interface IConnectionStringLoader 
{ 
    string Get(); 
    void Write(); 
} 

public interface IDbConnectionStringLoader : IConnectionStringLoader 
{ 
} 

public interface IMetaDataConnectionStringLoader : IConnectionStringLoader 
{ 
} 

Es hat nur eine Implementierung:

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

wie Meine Registrierung aussieht:

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

Der Punkt der Schnittstellen ist, dass ich die verschiedenen Schnittstellen in meinen Klassen injizieren kann und Ermitteln Sie die richtige Verbindungszeichenfolge für jede Implementierung. Aber das Problem ist, dass die letzte Registrierung die vorherige überschrieben wird.

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

Ausgang ist:

DbConnection 
DbConnection 

Wenn ich die Reihenfolge der Anmeldung invertieren der Ausgang MetaConnection zweimal sein wird. Meine Schlussfolgerung ist also, dass die letzte Registrierung die vorherige überschreibt. wenn ich die Implementierung einer abgeleiteten Klasse ändern funktioniert es jedoch:

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

Und die Registrierungen ändern:

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

Jetzt ist alles funktioniert, aber ich verstehe nicht, warum. Ich habe verschiedene Lebenszeichen ausprobiert, aber mit dem gleichen Ergebnis. Ich dachte, Unity würde versuchen, eine Instanz von ConnectionStringLoader mit dem "richtigen" Injektionsparameter basierend auf der Schnittstelle zu erstellen, aber es scheint hier eine andere Logik zu geben.

Irgendwelche Vorschläge, warum sich die Registrierungen gegenseitig überschreiben?

+0

Wie Sie 'Write' nennen, obwohl es nicht Teil ist der Schnittstelle? –

+0

Gut finden @ YacoubMassad. Ich habe es hinzugefügt, nachdem ich den Code eingefügt habe, um einen einfachen Test zu erstellen. Es ist nicht wirklich Teil der Logik, außer zu beweisen, dass die falsche Injektion injiziert wurde. Ich werde meine Frage bearbeiten. Danke, dass du es bemerkst. – smoksnes

Antwort

0

Ich bin nicht vertraut mit Unity. Aber es scheint, dass sie auf dieselbe Instanz abbilden. Sie sollten also die Lebensdauer von ConnectionStringLoader ändern (pro Abhängigkeit).

Wenn Sie keine Instanz teilen, warum legen Sie alle Dinge in eine Klasse? ConnectionStringLoader Methoden = IDbConnectionStringLoader Methoden + IMetaDataConnectionStringLoader Methoden.

Wenn Sie IDbConnectionStringLoader auflösen, wird es nicht IMetaDataConnectionStringLoader Methoden verwenden, die bereits in der Instanz ist (umgekehrt ist es wahr).

Crating zwei verschiedene abgeleitete Klasse ist an dieser Stelle besser:

Abstrakte Klasse:

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

Abgeleitete Klassen:

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

Ja, es scheint, dass sie auf dieselbe Instanz mappen, selbst wenn ich TransientLifetimeManager verwende. Das Erstellen von zwei Implementierungen kann der einzige Weg sein, es zu lösen, aber ich möchte es vermeiden, da die Implementierungen nichts tun. Sie haben keine spezifischen Implementierungen, die sich von ConnectionStringLoader unterscheiden. – smoksnes

+0

Ich glaube nicht, dass sie auf dieselben Instanzen abgebildet werden. Es ruft den ConnectionStringLoader ctor zweimal auf, aber mit dem gleichen Injection-Parameterwert, d.h. in diesem Fall "DbConnection". – AksharRoop

0

Überraschenderweise hat es zweimal ConnectionStringLoader Ctor nennen, aber mit dem gleichen Injektionselement. Wenn Sie sich container.Registrations ansehen, gibt es tatsächlich zwei Registrierungen, so dass es nicht mit anderen überschrieben wird. Ich habe mir die Implementierung von RegisterType angeschaut, aber ich habe mich nicht darum gekümmert.

Eine Alternative besteht darin, Ihre Registrierungen zu benennen, nicht sicher, ob sie in Ihre Bootstrap-Strategie für die Unity-Einheit passt.

1

Ehrlich gesagt, die Art, wie Sie die Schnittstellen verwenden, sieht für mich seltsam aus, weil es zwei Schnittstellen nur von der gleichen Klasse implementiert sind. Ich würde natürlicher finden den nächsten Ansatz Registrierungsnamen zu folgen:

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

Anmeldungen:

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

Auflösungen:

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

Ja, ich habe diesen Ansatz ebenfalls berücksichtigt. Aber dann muss meine Implementierung den Registrierungsnamen kennen, weshalb ich versucht habe, stattdessen andere Schnittstellen zu verwenden. Ich dachte, du solltest versuchen, möglichst keine Registrierungsnamen zu verwenden, weil. Oder liege ich falsch? – smoksnes

+0

Wir injizieren normalerweise alles in den Konstruktor, so dass wir alle Arbeiten zur Registrierungszeit erledigen. Die Verwendung des Containers innerhalb der Klassen wird normalerweise als eine Art Anti-Patter betrachtet (Suche nach ServiceLocator-Anti-Pattern). Zu diesem Zweck befindet sich das Problem immer in Containerregistrierungen, in denen Sie den Namen angeben müssen, der beim Auflösen einer Konstruktorabhängigkeit aufgelöst werden soll (ähnlich wie der InjectionConstructor). –

Verwandte Themen