6

Na mojej warstwie usługi wstrzyknąłem UnitOfWork i 2 repozytoria w konstruktorze. Jednostka pracy i repozytorium mają instancję DbContext Chcę się podzielić między nimi dwoma. Jak mogę to zrobić z programem Ninject? Który zakres należy rozważyć?Wyszukiwanie zakresu Ninject, który zachowuje się jak InRequestScope

Jestem nie jestem w aplikacji internetowej, więc nie mogę użyć InRequestScope.

Próbuję zrobić coś podobnego ... i używam DI, ale potrzebuję, żeby moja wersja była Dispose d i została stworzona w ten sposób.

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

    uow.SaveChanges(); 
} 

EDIT: Chcę po prostu mieć pewność, rozumiem ... po spojrzeniu na https://github.com/ninject/ninject.extensions.namedscope/wiki/InNamedScope I chociaż o mojej obecnej architektury aplikacji konsoli, która faktycznie korzysta Ninject.

Umożliwia znaczy:

klasy A jest klasa Warstwa usług

Klasa B jest jednostką pracy, które biorą pod parametru interfejs (IContextFactory)

klasy C jest magazyn, który bierze pod Parametr interface (IContextFactory)

Chodzi o to, aby móc wykonywać operacje kontekstowe na 2 lub więcej repozytoriach i używać jednostki pracy do zastosowania zmian.

Klasa D jest fabryką kontekstu (Entity Framework), która dostarcza instancję (zachowaj w kontenerze) kontekstu, który jest współdzielony pomiędzy Klasą B i C (.. i będzie również dla innych repozytoriów).

Fabryka kontekstów zachowuje instancję w swoim kontenerze, więc nie chcę ponownie używać tej instancji pod wszystkimi nazwami, ponieważ kontekst musi zostać usunięty na końcu operacji usługi. Jest to główny cel InNamedScope tak właściwie ?

Rozwiązanie byłoby, ale nie jestem pewien, czy robię to dobrze, instancja usług będzie transcient, co oznacza, że ​​faktycznie nigdy się nie disposed? :

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

związane: http://stackoverflow.com/questions/14554151/dependency-injection-and-life-time-of-idisposable-objects –

+1

związanych z: http://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why/10588594#10588594 –

Odpowiedz

5

UPDATE: To podejście działa na prąd Nuget, ale opiera się na anomalię w realizacji InCallscope który został ustalony w obecnych niestabilnych pakietów Nuget. Będę poprawiać tę odpowiedź w ciągu kilku dni, aby odzwierciedlić najlepsze podejście po pewnym rozmyślaniu. Uwaga: sposób budowania struktury na wysokim poziomie pozostanie prawie identyczny, tylko dokładne szczegóły zakresu ustalania Bind<DbContext>() będą działać. (Podpowiedź:. CreateNamedScope w niestabilny będzie działać lub można skonfigurować Handler Command DefinesNamedScope Powód I dont po prostu zrób to, że chcę mieć coś, co komponuje/gra dobrze z InRequestScope)


Gorąco polecam czytanie testów integracyjnych Ninject.Extensions.NamedScope (poważnie je znaleźć i przeczytać je i ponownie przeczytać)

DbContextjest jednostka pracy, więc nie ma potrzeby dalszego owijania.

jak chcesz, aby móc mieć wiele „żądań” w locie i chcesz mieć pojedynczą jednostkę pracy dzielone między nimi, trzeba:

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

InCallScope() oznacza, że:

  1. dla danego wykresu obiektu składającego się z pojedynczej kernel.Get()wezwania (stąd w wezwania Zakres), każdy, który wymaga DbContext dostanie AF ja instancja.
  2. the IDisposable. Dispose() zostanie wywołana gdy Kernel.Release() dzieje dla obiektu głównego (lub Kernel.Components.Get<ICache>().Clear() zdarza korzenia jeśli nie jest .InCallScope())

Nie powinno być żadnego powodu, aby użyć InNamedScope() i DefinesNamedScope(); Nie masz długo żyjących obiektów, które próbujesz wykluczyć z domyślnego łączenia/rodzicielstwa/grupowania.

Jeśli do powyższego, należy być w stanie:

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

Realizacja poleceń wygląda następująco:

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

Należy zauważyć, że w ogóle unikam konieczności niejawny Jednostka Pracy w ten sposób, a zamiast tego powierzchnia jest tworzona i Disposal. To sprawia, że ​​polecenia wyglądać tak:

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

Wiąże się to nie wykorzystanie .InCallScope() na Bind<DbContext>() (ale nie wymagają obecności Ninject.Extensions.Factory's FactoryModule do syntezy Func<DbContext> od prostego Bind<DbContext>()

+0

Nie mogę użyć singleton .. ponieważ usługi znajdują się w komendach, które są tworzone dla wątków. To jest serwer gry. Ale InNamedScope wydaje się być tym, czego szukałem .. kiedy koniec zakresu robi inne instancje (patrz powyższy przykład dodany), które są przejściowe, ale są parą instancji, która ma jakiś zakres, w jaki sposób się pozbyć? lub czy muszę wywołać Release po wykonaniu usługi? Co się stanie, jeśli instancja nadrzędna otrzyma GC? – Rushino

+0

Dzięki temu bardzo ładny przykład. Zastanawiam się ... co się stanie, jeśli korzeń kompozycji nie jest dokładnie taki? Mam na myśli, że nie mam dostępu do jądra ... chyba że korzystam z lokalizatora usług. Dowolny pomysł ? Wszystko zaczyna się od singleton .. Integruję moją architekturę z inną biblioteką .. i jedynym punktem wejścia, jaki znalazłem, są komendy (zapytania serwera, w które wstrzykuję swoje usługi). Więc w zasadzie wszystkie są instancied raz .. i wnioski są przekierowywane do nich. – Rushino

+0

@Rushino Nie jesteś pewien co masz na myśli. Być może być w porządku oddzielne pytanie z ilustracją twoich ograniczeń. Po pierwsze, możesz użyć https://github.com/ninject/ninject.extensions.factory/wiki, aby odpowiednio utrudnić dostęp do tworzenia Command Managera itp. W odpowiednim momencie twojego cyklu przetwarzania. na przykład 'kernel.Get ();' może stać się posiadaniem 'ICommandFactory' (z' ICommand CreateCommand() ') dostarczonym do twojej implementacji serwera" ServerQueryProcessor ". Innymi słowy, twój procesor kwerendy staje się głównym składem. –

2

Jak omówiono w the other answer,. InCallScope nie jest dobrym podejściem do rozwiązania tego problemu:

Na razie zrzucam kod działający przeciwko najnowszemu NuGet Unstable/Include PreRelease/Instal-Package -Pre wydania Ninject.Web.Common bez jasnego wyjaśnienia. I przetłumaczy to na artykuł na stronie Ninject.Extensions.NamedScope wiki na pewnym etapie zaczęli pisać opis tej techniki w the Ninject.Extensions.NamedScope wiki's CreateNamedScope/GetScope article.

Prawdopodobnie niektóre bity staną się także żądaniami Pull na pewnym etapie (końcówka kapelusza do @Remo Gloor, który dostarczył mi konspekt). The associated tests and learning tests are in this gist for now), w oczekiwaniu na opakowanie w odpowiednim zwolnionym formacie TBD.

Luskusowy podsumowanie jest załadować moduł poniżej w jądro i używać .InRequestScope() na wszystko, co chcesz utworzonego/Dispose D na handler wywołania, a następnie karmić żądań poprzez poprzez IHandlerComposer.ComposeCallDispose.

przypadku korzystania z następujących modułów:

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

, które przewody w fabryce [1] i NinjectHttpApplicationPlugin który eksponuje:

public interface INinjectRequestHandlerScopeFactory 
{ 
    NamedScope CreateRequestHandlerScope(); 
} 

Następnie można użyć tego kompozytora uruchomić Request InRequestScope():

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

Zaimplementowane jako:

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 rzeczy Infrastruktura:

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() 
     { 
     } 
    } 
} 
Powiązane problemy