2016-11-25 5 views
0

Я был одаренным, когда мне пришлось работать с уже настроенным приложением Ninject DI, которое я вырос и значительно добавил к разработке приложения, над которым я работаю ,Инъекция зависимостей с Ninject разделяет одни и те же объекты с разными экземплярами

Теперь я нахожу проблему, которую я хотел бы исправить. Мне удалось обойти это с помощью наследования, но хотелось бы более чистого решения.

У меня есть два подключения, которые необходимо ввести в разные службы и хранилища. Затем мне нужны репозитории, которые также должны быть правильно связаны с правильным сервисом, имеющим тот же UnitOfWork.

Я думаю, что, возможно, я прошу чего-то, что невозможно без наследования и специализации, но именно поэтому я спрашиваю.

Мне удалось решить этот вопрос, создав подкласс классов Repository и UnitOfWork, но ничего не делает, кроме внедрения базового класса. Мне просто не нравится идея подкласса, полностью зависящая от функциональности суперкласса, в основном пустые фигурные скобки, помимо конструктора, для меня это не похоже на ООП, чтобы решить эту проблему. Поэтому я искал лучшего решения, используя одноклассное решение, если это возможно в DI.

Так что, если вы можете игнорировать решение я говорил о том, потому что я полностью вернулась изменение это то, что я остался с:

Глядя на код ниже вы можете увидеть, что это цель.

... 

public class UnitOfWork : IUnitOfWork 
{ 
    private static readonly log4net.ILog log = log4net.LogManager.GetLogger("UnitOfWork"); 
    public DbContext DataContext { get; set; } 

    public UnitOfWork(string connectionString) 
    { 
     DataContext = new DbContext(connectionString); 
    } 

    public void Commit() 
    { 
     ... 
    } 
} 

... 

public class Repository<T> : IRepository<T> where T : class 
{ 
    public IUnitOfWork unitOfWork { get; set; } 
    private readonly IDbSet<T> dbSet; 

    //private static readonly log4net.ILog log = log4net.LogManager.GetLogger("Repository"); 

    public Repository(IUnitOfWork unitOfWork) 
    { 
     this.unitOfWork = unitOfWork; 
     dbSet = this.unitOfWork.DataContext.Set<T>(); 
    } 
    ... 
} 
... 

public class IPOPDataModules : NinjectModule 
{ 
    public override void Load() 
    { 
    Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope().WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BE_TESTEntities"].ConnectionString); 
    Bind<IRepository<tOrder>>().To<Repository<tOrder>>().InRequestScope(); 
    } 
} 

... 

public class DataModules : NinjectModule 
{ 
    public override void Load() 
    { 
    Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope().WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BAPSEntities"].ConnectionString); 
    Bind<IRepository<Data.Quote>>().To<Repository<Data.Quote>>().InRequestScope(); 
    } 
} 

... 

public class QuoteService : IQuoteService 
{ 
    private IUnitOfWork unitOfWork; 
    private IRepository<Data.Quote> quoteRepository; 
    public QuoteService(IUnitOfWork unitOfWork, IRepository<Data.Quote> quoteRepository) 
    { 
     ... 
    } 
} 

... 

public class IPOPService : IIPOPService 
{ 
    private IUnitOfWork unitOfWork; 
    private IRepository<Data.tOrder> tOrderRepository; 

    public IPOPService(IUnitOfWork unitOfWork, IRepository<Data.tOrder>) 
    { 
     ... 
    } 
} 

То, что я хочу знать, можно ли один и те же UnitOfWork и Repository объектов два различных соединений и иметь их вводят в разные экземпляры для соответствующих служб (IPOPService для подключения IPOP_BE_TEST, QuoteService для подключения IPOP_BAP)

Опять же, код выше не достигает желаемого, но это архитектура, которую я хотел бы поиграть, чтобы заставить ее работать.

ответ

0

Ваш вопрос еще не ясен для меня. Но проверьте документацию на следующие две области, которые могут быть интересны для вашего сценария.

  • InCallScope приведет к тому, что для дерева разрешений будет создан только один экземпляр. Обычно я использую эту область применения для настольных приложений для единицы работы. См. Документацию here. Для этого вам понадобится расширение Ninject.Extensions.NamedScope.
  • InRequestScope приведет к тому, что в веб-приложении будет создан только один экземпляр для каждого HTTP-запроса. Обычно я использую эту область для единицы работы. См. Документацию here. Для этого вам понадобится пакет Ninject.Web.Common.
+0

Спасибо за ввод @Zoltan, я работаю над сайтом MVC, и я внес некоторые изменения в вопрос, который, надеюсь, должен устранить возникшую путаницу. Что касается ответа выше, я хочу иметь возможность как-то отобразить правильные 'UnitOfWork' и' Repository' (с правильными подключениями db) к двум различным вышеперечисленным службам. Я думаю, что мне не хватает некоторой логики Ninject, которая могла бы, если возможно, сделать что-то. – Saleh

0

Что вы ищете, это области привязки Ninject. Всякий раз, когда вы объявляете привязывающий Ninject, будет предоставлен делегат этой привязки, который используется процессом активации, чтобы определить, должен ли он создать новый экземпляр этой службы или если он должен вернуть ранее сконструированный экземпляр.

Итак, если вы хотите реализовать синглтон в Ninject, вы просто объявить связь, которая выглядит следующим образом:

Bind<IRepository<Data.Quote>>().To<Repository<Data.Quote>>().InSingletonScope(); 

InSingletonScope() и InRequestScope() просто сахар (или в случае InRequestScope метод расширения) на IBindingInSyntax<T> для метода InScope(Func<Ninject.Activation.IContext, object> scope). Каждый раз, когда вы хотите убедиться, что Ninject возвращает тот же экземпляр службы в данной ситуации, все, что вам нужно сделать, это реализовать настраиваемую область.

Если я правильно понял ваш вопрос, вы хотите убедиться, что при обращении к вашему заявлению те же экземпляры Repository<T> и IUnitOfWork будут введены во все службы вашего приложения. В этом случае вы бы просто написать привязок, как это:

Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope().WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BE_TESTEntities"].ConnectionString); 
Bind<IRepository<tOrder>>().To<Repository<tOrder>>().InRequestScope(); 

Однако, ваша проблема, как представляется, что у вас есть два отдельных модуля, с двумя отдельными привязками. Я бы предположил, что вам нужно использовать один модуль с контекстной привязкой, чтобы определить, какая строка соединения должна быть предоставлена ​​для какой части системы. Таким образом, ваш один модуль может выглядеть следующим образом:

Bind<IUnitOfWork>() 
    .To<UnitOfWork>() 
    .WhenInjectedInto<IIPOPService>() 
    .InRequestScope() 
    .WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BE_TESTEntities"].ConnectionString); 
Bind<IUnitOfWork>() 
    .To<UnitOfWork>() 
    .WhenInjectedInto<IQuoteService>() 
    .InRequestScope() 
    .WithConstructorArgument("connectionString", ConfigurationManager.ConnectionStrings["IPOP_BAPSEntities"].ConnectionString); 

Bind<IRepository<tOrder>>().To<Repository<tOrder>>().InRequestScope(); 

Таким образом, вы можете быть уверены, что, когда Ninject в решении IIPOPService создаст экземпляр UnitOfWork инициализирован с струной "IPOP_BE_TESTEntities" соединения, и при решении IQuoteService, он будет использовать строка подключения "IPOP_BAPSEntities", но в противном случае в этой области запроса только один экземпляр будет создан Ninject.

Надеюсь, это поможет.

+0

Спасибо за ваше предложение @ s3raph86, к сожалению, существует около 40 нечетных сервисов, подобных IPOPService и IQuoteService, и мне было интересно, было ли более элегантное решение, если возможно, для всех с минимальными изменениями кода. – Saleh

+0

Это не должно быть dealbreaker @Saleh - Ninject предоставляет множество опций для контекстной привязки. Что вы можете рассказать мне о правилах, которые вам нужно использовать при принятии решения о применении одной строки соединения или другой? Возможно, вы могли бы определить атрибут для украшения своих сервисов, а затем использовать его в своих привязках? – s3raph86

+0

В настоящее время приложение поддерживает 3 подключения и больше в будущем (вопрос выше показывает пример 2). Я не уверен, если это возможно, с минимальными изменениями кода. Я хочу указать UnitOfWork с conn, чтобы сказать 'IPOP_BAPS', нескольким репозиториям и сервисам и в то же время указать другой UnitOfWork с другим соединением с другим набором репозитории и услуги. Кроме того, поскольку репозитории внедряются в сервисы, UnitOfwork в сервисе и репозитории вставляется в необходимость того, чтобы они были одинаковыми. Услуги не должны использоваться повторно, но репозитории, которые я хотел бы использовать повторно. – Saleh