6

На моем уровне обслуживания я ввел в конструктор UnitOfWork и 2 репозитория. В модуле работы и хранилище есть экземпляр DbContext. Я хочу поделиться между ними. Как я могу сделать это с помощью Ninject? Какую область следует рассматривать?Ищет область видимости Ninject, которая ведет себя как InRequestScope

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

Я пытаюсь сделать что-то подобное ... и я использую DI, но мне нужно, чтобы мой UoW был Dispose d и создан вот так.

using (IUnitOfWork uow = new UnitOfWorkFactory.Create()) 
{ 
    _testARepository.Insert(a); 
    _testBRepository.Insert(b); 

    uow.SaveChanges(); 
} 

EDIT: Я просто хочу быть уверен, что я понимаю ... после того, как смотреть на https://github.com/ninject/ninject.extensions.namedscope/wiki/InNamedScope я, хотя о моей текущей архитектуры консоли приложения, которые на самом деле использовать Ninject.

Позволяет сказать:

Класс A представляет собой класс обслуживания слоя

Класс B является единицей работы, которая принимает во параметр интерфейс (IContextFactory)

Класс C представляет собой хранилище, которое принимает во параметр interface (IContextFactory)

Идея здесь заключается в том, чтобы иметь возможность выполнять операции контекста в 2 или более хранилищах и использовать единицу работы для применения изменений.

Класс D - это фабрика контекста (Entity Framework), которая предоставляет экземпляр (сохраняемый в контейнере) контекста, который совместно используется классом B и C (.. и будет использоваться и для других репозиториев).

Контекстная фабрика хранит экземпляр в своем контейнере, поэтому я не хочу повторно использовать этот экземпляр для всего имени, так как контекст должен быть удален в конце сервисной операции. Это основная цель InNamedScope на самом деле ?

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

Bind<IScsContextFactory>() 
    .To<ScsContextFactory>() 
    .InNamedScope("ServiceScope") 
    .WithConstructorArgument(
     "connectionString", 
     ConfigurationUtility.GetConnectionString()); 

Bind<IUnitOfWork>().To<ScsUnitOfWork>(); 

Bind<IAccountRepository>().To<AccountRepository>(); 
Bind<IBlockedIpRepository>().To<BlockedIpRepository>(); 

Bind<IAccountService>().To<AccountService>().DefinesNamedScope("ServiceScope"); 
Bind<IBlockedIpService>().To<BlockedIpService>().DefinesNamedScope("ServiceScope"); 
+0

связаны: http://stackoverflow.com/questions/14554151/dependency-injection-and-life-time-of-idisposable-objects –

+1

связанные с: http://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why/10588594#10588594 –

ответ

5

UPDATE: Этот подход работает против NuGet тока, но полагается на аномалии в InCallscope реализации, которая была зафиксирована в нынешних нестабильных пакетах NuGet. Я через несколько дней подберу этот ответ, чтобы отразить наилучший подход после некоторого размышления. NB, высокоуровневый способ структурирования вещей останется в значительной степени идентичным, точно будут указаны точные данные области Bind<DbContext>(). (Подсказка:. CreateNamedScope в нестабильном будет работать или один может создать командный обработчик, как DefinesNamedScope Причина я как раз не делать то, что я хочу, чтобы иметь то, что сочиняет/хорошо играет с InRequestScope)


Я настоятельно рекомендую читать Ninject.Extensions.NamedScope интеграционные тесты (серьезно, найти их и читать и перечитывать их)

DbContextявляется единица работы так не далее упаковка не требуется.

Как вы хотите, чтобы иметь возможность иметь несколько «запросы» в полете и хотят иметь одну единицу работы распределяются между ними, что вам нужно:

Bind<DbContext>() 
    .ToMethod(ctx => 
     new DbContext( 
      connectionStringName: ConfigurationUtility.GetConnectionString())) 
    .InCallScope(); 

InCallScope() означает, что:

  1. для данного объекта графа, состоящего для одного kernel.Get()вызова (следовательно, в Зов Scope), все, что требует DbContext получит са я пример.
  2. IDisposable. Dispose() будет называться, когда Kernel.Release() происходит для корневого объекта (или Kernel.Components.Get<ICache>().Clear() происходит из-за корня, если это не .InCallScope())

Там не должно быть никаких оснований для использования InNamedScope() и DefinesNamedScope(); У вас нет долгоживущих объектов, которые вы пытаетесь исключить из пула/родительского/группирования по умолчанию.

Если вы выше, вы должны быть в состоянии:

var command = kernel.Get<ICommand>(); 
try { 
    command.Execute(); 
} finally { 
    kernel.Components.Get<ICache>().Clear(command); // Dispose of DbContext happens here 
} 

Реализация команды выглядит следующим образом:

class Command : ICommand { 
    readonly IAccountRepository _ar; 
    readonly IBlockedIpRepository _br; 
    readonly DbContext _ctx; 
    public Command(IAccountRepository ar, IBlockedIpRepository br, DbContext ctx){ 
     _ar = ar; 
     _br = br; 
     _ctx = ctx; 
    } 
    void ICommand.Execute(){ 
     _ar.Insert(a); 
     _br.Insert(b); 
     _ctx.saveChanges(); 
    } 
} 

Обратите внимание, что в целом, я избегаю, имеющий неявный блок работы таким образом, а вместо этого - создание его и Disposal. Это делает команду выглядеть следующим образом:

class Command : ICommand { 
    readonly IAccountService _as; 
    readonly IBlockedIpService _bs; 
    readonly Func<DbContext> _createContext; 
    public Command(IAccountService @as, IBlockedIpServices bs, Func<DbContext> createContext){ 
     _as = @as; 
     _bs = bs; 
     _createContext = createContext; 
    } 
    void ICommand.Execute(){ 
     using(var ctx = _createContext()) { 
      _ar.InsertA(ctx); 
      _br.InsertB(ctx); 
      ctx.saveChanges(); 
     } 
    } 

Это не предполагает использование .InCallScope() на Bind<DbContext>() (но требует присутствия Ninject.Extensions.Factory's FactoryModule синтезировать Func<DbContext> от простой Bind<DbContext>()

+0

Я не могу использовать singleton .. потому что службы расположены в командах, которые создаются для потоков. Это игровой сервер. Но InNamedScope, похоже, является тем, что я искал. Когда в конце области действия добавляются другие экземпляры (см. Выше пример), которые являются транзитивными, но par экземпляра, у которого есть область, как-то распределяется? или мне нужно вызвать Release после выполнения службы? Что произойдет, если родительский экземпляр получит GC? – Rushino

+0

Спасибо, что очень хороший пример. Просто интересно .. что произойдет, если корневой состав не совсем такой? Я имею в виду, что у меня нет доступа к ядру. За исключением случаев, когда я использую локатор службы. Есть идеи ? Все началось с одного синглета. Я интегрирую свою архитектуру над какой-то другой библиотекой. И единственная точка входа, которую я нашел, - это команды (серверные запросы, в которых я внедряю свои службы). Таким образом, в принципе, все они созданы каждый раз .. и запросы перенаправляются к ним. – Rushino

+0

@ Rushino Не уверен, что вы имеете в виду здесь. Возможно, отдельный вопрос с иллюстрацией ваших ограничений может быть в порядке. Во-первых, вы можете использовать https://github.com/ninject/ninject.extensions.factory/wiki, чтобы предоставить надлежащим образом ограниченный доступ к созданию обработчиков команд и т. Д. В правильной точке вашего цикла обработки. например 'kernel.Get ();' может стать у вас с 'ICommandFactory' (с' ICommand CreateCommand() '), предоставленным для реализации запроса сервера' ServerQueryProcessor'. Другими словами, ваш процессор запросов становится корнем композиции. –

2

Как обсуждалось в the other answer,. InCallScope не является хорошим подходом к решению этой проблемы.

На данный момент я сбрасываю код, который работает против последней версии NuGet Unstable/Include PreRelease/Instal-Package -Pre выпуски Ninject.Web.Common без четкого пояснения. I переведет это на статью в вики Ninject.Extensions.NamedScope на каком-то этапе начали писать прохождение этой техники в the Ninject.Extensions.NamedScope wiki's CreateNamedScope/GetScope article.

Возможно, некоторые разряды также станут Pull Request (s) на определенном этапе (подсказка о шляпе @Remo Gloor, которая предоставила мне код схемы). associated tests and learning tests are in this gist for now), в ожидании упаковки в надлежащем выпущенном формате TBD.

ехес краткого изложения вы загрузите модуль ниже в ядро ​​и использовать .InRequestScope() на все, что вы хотите создать/Dispose д за обработчик вызова, а затем кормить запросы через через IHandlerComposer.ComposeCallDispose.

Если вы используете следующий модуль:

public class Module : NinjectModule 
{ 
    public override void Load() 
    { 
     Bind<IHandlerComposer>().To<NinjectRequestScopedHandlerComposer>(); 

     // Wire it up so InRequestScope will work for Handler scopes 
     Bind<INinjectRequestHandlerScopeFactory>().To<NinjectRequestHandlerScopeFactory>(); 
     NinjectRequestHandlerScopeFactory.NinjectHttpApplicationPlugin.RegisterIn(Kernel); 
    } 
} 

какие провода на заводе [1] и NinjectHttpApplicationPlugin, который предоставляет:

public interface INinjectRequestHandlerScopeFactory 
{ 
    NamedScope CreateRequestHandlerScope(); 
} 

Затем вы можете использовать этот компоновщик для запуска запроса InRequestScope():

public interface IHandlerComposer 
{ 
    void ComposeCallDispose(Type type, Action<object> callback); 
} 

Выполнено как:

class NinjectRequestScopedHandlerComposer : IHandlerComposer 
{ 
    readonly INinjectRequestHandlerScopeFactory _requestHandlerScopeFactory; 

    public NinjectRequestScopedHandlerComposer(INinjectRequestHandlerScopeFactory requestHandlerScopeFactory) 
    { 
     _requestHandlerScopeFactory = requestHandlerScopeFactory; 
    } 

    void IHandlerComposer.ComposeCallDispose(Type handlerType, Action<object> callback) 
    { 
     using (var resolutionRoot = _requestHandlerScopeFactory.CreateRequestHandlerScope()) 
      foreach (object handler in resolutionRoot.GetAll(handlerType)) 
       callback(handler); 
    } 
} 

Ninject Инфраструктура материал:

class NinjectRequestHandlerScopeFactory : INinjectRequestHandlerScopeFactory 
{ 
    internal const string ScopeName = "Handler"; 

    readonly IKernel _kernel; 

    public NinjectRequestHandlerScopeFactory(IKernel kernel) 
    { 
     _kernel = kernel; 
    } 

    NamedScope INinjectRequestHandlerScopeFactory.CreateRequestHandlerScope() 
    { 
     return _kernel.CreateNamedScope(ScopeName); 
    } 

    /// <summary> 
    /// When plugged in as a Ninject Kernel Component via <c>RegisterIn(IKernel)</c>, makes the Named Scope generated during IHandlerFactory.RunAndDispose available for use via the Ninject.Web.Common's <c>.InRequestScope()</c> Binding extension. 
    /// </summary> 
    public class NinjectHttpApplicationPlugin : NinjectComponent, INinjectHttpApplicationPlugin 
    { 
     readonly IKernel kernel; 

     public static void RegisterIn(IKernel kernel) 
     { 
      kernel.Components.Add<INinjectHttpApplicationPlugin, NinjectHttpApplicationPlugin>(); 
     } 

     public NinjectHttpApplicationPlugin(IKernel kernel) 
     { 
      this.kernel = kernel; 
     } 

     object INinjectHttpApplicationPlugin.GetRequestScope(IContext context) 
     { 
      // TODO PR for TrgGetScope 
      try 
      { 
       return NamedScopeExtensionMethods.GetScope(context, ScopeName); 
      } 
      catch (UnknownScopeException) 
      { 
       return null; 
      } 
     } 

     void INinjectHttpApplicationPlugin.Start() 
     { 
     } 

     void INinjectHttpApplicationPlugin.Stop() 
     { 
     } 
    } 
} 
Смежные вопросы