2016-03-29 5 views
3

Я использую Unity в C#. У меня есть интерфейс, который я вызываю IConnectionStringLoader, у которого есть два производных интерфейса.Регистрация Unity переопределяет друг друга

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

public interface IDbConnectionStringLoader : IConnectionStringLoader 
{ 
} 

public interface IMetaDataConnectionStringLoader : IConnectionStringLoader 
{ 
} 

Она имеет только одну реализацию:

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 

Если я инвертировать порядок регистрации на выходе будет 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

Я не знаком с Единством. Но похоже, что они сопоставляются с одним экземпляром. Таким образом, вы должны изменить время жизни ConnectionStringLoader (Per dependency).

Если вы не поделитесь случаем, зачем вы вкладываете все вещи в один класс? ConnectionStringLoader Методы = IDbConnectionStringLoader методы + IMetaDataConnectionStringLoader методы.

Когда вы разрешите IDbConnectionStringLoader, он не будет использовать методы IMetaDataConnectionStringLoader, которые уже находятся в примере (наоборот, это правда).

Crating две разные производный класс лучше в данный момент:

Абстрактный класс:

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

Я не думаю, что они сопоставляются с теми же экземплярами. Он вызывает CallStringLoader ctor дважды, но с таким же значением параметра 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) –

Смежные вопросы