14

Używam SimpleInjector jako mojej biblioteki IoC. Rejestruję DbContext zgodnie z zapytaniem sieciowym i działa poprawnie. Ale jest jedno zadanie, które uruchamiam w wątku tła. Tak więc mam problem z utworzeniem instancji DbContext. na przykładMieszany styl życia dla każdego wątku i żądania sieci z prostym wtryskiwaczem

  1. Service1 ma instancję DbContext
  2. Service2 ma instancję DbContext
  3. Service1 i Service2 perspektywie z nici tła.
  4. Service1 pobiera podmiot i przekazać go do Service2
  5. Service2 korzysta podmiot, ale jednostka jest odłączona od DbContext

Właściwie problem jest tutaj: Service1.DbContext jest różnica od Service2.DbContext.

Wydaje się, że po uruchomieniu zadania w osobnym wątku w środowisku ASP.NET MVC, SimpleInjector tworzy nowe wystąpienie DbContext dla każdego połączenia. Podczas gdy niektóre biblioteki IoC (na przykład StructureMap) mają mieszany styl życia dla każdego wątku na zgłoszenie na stronę internetową, wydaje się, że nie ma ani jednego. Czy mam rację?

Masz pomysł na rozwiązanie tego problemu w SimpleInjector? Z góry dzięki.

EDIT:

Moje usługi są tutaj:

class Service1 : IService1 { 
    public Service1(MyDbContext context) { } 
} 

class Service2 : IService2 { 
    public Service2(MyDbContext context, IService1 service1) { } 
} 

class SyncServiceUsage { 
    public SyncServiceUsage(Service2 service2) { 
     // use Service2 (and Service1 and DbContext) from HttpContext.Current 
    } 
} 

class AsyncServiceUsage { 
    public AsyncServiceUsage(Service2 service2) { 
     // use Service2 (and Service1 and DbContext) from background thread 
    } 
} 

public class AsyncCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand> where TCommand : ICommand { 

    private readonly Func<ICommandHandler<TCommand>> _factory; 

    public AsyncCommandHandlerDecorator(Func<ICommandHandler<TCommand>> factory) { 
     _factory = factory; 
    } 

    public void Handle(TCommand command) { 
     ThreadPool.QueueUserWorkItem(_ => { 
      // Create new handler in this thread. 
      var handler = _factory(); 
      handler.Handle(command); 
     }); 
    } 
} 

void InitializeSimpleInjector() { 
    register AsyncCommandHandlerDecorator for services (commands actually) that starts with "Async" 
} 

I użytkownik Service2 czasami i AsyncService2 inne czasy.

+0

Powiązane: http://stackoverflow.com/questions/11041601/simple-injector-multi-threading-in-mvc3-asp-net – Steven

+0

Powiązane: http://stackoverflow.com/questions/10304023/simpleinjector-is -to-right-way-to-registerermanyforopengeneric-when-i-have – Steven

Odpowiedz

17

Wydaje kiedy uruchomić zadanie w osobnym wątku w ASP.NET MVC, SimpleInjector tworzy nową instancję DbContext dla każdego połączenia.

Zachowanie RegisterPerWebRequest życia Simple wtryskiwacza v1.5 i poniżej jest powrót przejściową przypadek, gdy wystąpienia są o poza ramach żądania sieci (gdzie HttpContext.Current jest zerowa). Zwrócenie przejściowej instancji było wadą projektową w prostym wtryskiwaczu, ponieważ ułatwia to ukrycie niewłaściwego użycia. Version 1.6 of the Simple Injector wygeneruje wyjątek zamiast niepoprawnego zwrócenia instancji przejściowej, aby wyraźnie stwierdzić, że źle skonfigurowałeś kontener.

Chociaż niektóre biblioteki IoC (na przykład StructureMap) mają mieszane życia dla per-thread-per-WebRequest wydaje Proste Injector ma nie jeden

Prawdą jest, że prosty Injector nie ma wbudowanej obsługi mieszanego stylu życia z kilku powodów. Przede wszystkim jest to dość egzotyczna funkcja, której nie potrzebuje wiele osób. Po drugie, możesz łączyć ze sobą dwa lub trzy style życia, dzięki czemu będzie to prawie nieskończona kombinacja hybryd.I na koniec łatwo zarejestrować się samemu.

Chociaż można mieszać Per Web Request z Per Thread życia, prawdopodobnie byłoby lepiej, kiedy wymieszać Per internetowej Zamówienie z Per Lifetime Scope, ponieważ z zakresem całokształt jawnie rozpoczęcia i zakończenia zakresu (i może wyrzucać DbContext gdy zakres kończy) .

Od Simple Injector 2 można łatwo łączyć dowolną liczbę stylów życia, korzystając z metody . Oto przykład:

var hybridLifestyle = Lifestyle.CreateHybrid(
    () => HttpContext.Current != null, 
    new WebRequestLifestyle(), 
    new LifetimeScopeLifestyle()); 

// Register as hybrid PerWebRequest/PerLifetimeScope. 
container.Register<DbContext, MyDbContext>(hybridLifestyle); 

Jest jeszcze inna kwestia Stackoverflow, że idzie do tego tematu nieco głębiej, może chcesz przyjrzeć: Simple Injector: multi-threading in MVC3 ASP.NET

UPDATE

słów o aktualizacji. Jesteś prawie na miejscu. Polecenia działające w wątku w tle muszą być uruchamiane w zasięgu Lifetime Scope, więc będziesz musiał uruchomić go jawnie. Sztuką jest tutaj wywołanie BeginLifetimeScope w nowym wątku, ale przed faktycznym tworzeniem obsługi komend (i jej zależności). Innymi słowy, najlepszym sposobem na to jest wewnątrz dekoratora.

Najprostszym rozwiązaniem jest, aby zaktualizować AsyncCommandHandlerDecorator dodać zakresu:

public class AsyncCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand> where TCommand : ICommand 
{ 
    private readonly Container _container; 
    private readonly Func<ICommandHandler<TCommand>> _factory; 

    public AsyncCommandHandlerDecorator(Container container, 
     Func<ICommandHandler<TCommand>> factory) 
    { 
     _container = container; 
     _factory = factory; 
    } 

    public void Handle(TCommand command) 
    { 
     ThreadPool.QueueUserWorkItem(_ => 
     { 
      using (_container.BeginLifetimeScope()) 
      { 
       // Create new handler in this thread 
       // and inside the lifetime scope. 
       var handler = _factory(); 
       handler.Handle(command); 
      } 
     }); 
    } 
} 

purystów że popierają zasady SOLID będzie krzyczeć, że ta klasa jest naruszenie Single Responsibility Principle, ponieważ dekorator zarówno uruchamia polecenia w nowym wątku i rozpoczyna nowy zakres życia. Nie martwiłbym się tym zbytnio, ponieważ uważam, że istnieje bliski związek między rozpoczynaniem wątku tła a uruchamianiem zakresu życia (nie używałbyś go bez drugiego). Ale nadal można łatwo opuścić AsyncCommandHandlerDecorator nietknięty i utworzyć nowy LifetimeScopedCommandHandlerDecorator następująco:

public class LifetimeScopedCommandHandlerDecorator<TCommand> 
    : ICommandHandler<TCommand> where TCommand : ICommand 
{ 
    private readonly Container _container; 
    private readonly Func<ICommandHandler<TCommand>> _factory; 

    public LifetimeScopedCommandHandlerDecorator(Container container, 
     Func<ICommandHandler<TCommand>> factory) 
    { 
     _container = container; 
     _factory = factory; 
    } 

    public void Handle(TCommand command) 
    { 
     using (_container.BeginLifetimeScope()) 
     { 
      // The handler must be created inside the lifetime scope. 
      var handler = _factory(); 
      handler.Handle(command); 
     } 
    } 
} 

Kolejność, w jakiej te dekoratorzy są zarejestrowane jest oczywiście niezbędny, ponieważ AsyncCommandHandlerDecoratormusi owinąć LifetimeScopedCommandHandlerDecorator. Oznacza to, że rejestracja LifetimeScopedCommandHandlerDecorator musi pochodzić pierwszy:

container.RegisterDecorator(typeof(ICommandHandler<>), 
    typeof(LifetimeScopedCommandHandlerDecorator<>), 
    backgroundCommandCondition); 

container.RegisterDecorator(typeof(ICommandHandler<>), 
    typeof(AsyncCommandHandlerDecorator<>), 
    backgroundCommandCondition); 

This old Stackoverflow question rozmowy na ten temat bardziej szczegółowo. Powinieneś zdecydowanie rzucić okiem.

+1

Dzięki + Steven przeczytałem drugi link i jego przydatne i pomocne. Ponadto głosowałem za odpowiedzią i zadawałem pytania wszystkim, aby poprawić reputację SimpleInjector. Powodzenia –

+0

WTF Jestem zdezorientowany! Edytuję moje pytanie. Czy możesz mi powiedzieć o użyciu? Obiekty 'Service1' i' Service2' są używane zarówno w głównym wątku (żądanie sieciowe), jak i wątku tła. Chcę móc to zrobić. I wstrzyknę im "DbContext" przez wstrzyknięcie ctor. Czy powinienem zmienić swoją implementację? –

+1

@Javad_Amiry: Przykro mi z powodu mojej spóźnionej odpowiedzi, ale nawet programiści potrzebują czasem odpoczynku ;-). Zaktualizowałem swoją odpowiedź. Mam nadzieję, że to pomoże. – Steven

Powiązane problemy