2009-07-19 18 views
44

Kilka dni temu miałem ten problem z gwintowaniem ASP.Net. Chciałem mieć obiekt singleton na żądanie internetowe. Potrzebuję tego dla mojej jednostki pracy. Chciałem utworzyć jednostkę pracy na żądanie internetowe, aby mapa tożsamości była ważna przez żądanie. W ten sposób mógłbym użyć IoC do przezroczystego wstrzyknięcia mojej własnej klasy IUnitOfWork do moich klas repozytorium i mogłem użyć tej samej instancji do zapytania, a następnie aktualizacji moich encji.Singleton Per Call Context (Web Request) w Unity

Ponieważ używam Unity, błędnie użyłem PerThreadLifeTimeManager. Wkrótce zdałem sobie sprawę, że model gwintowania ASP.Net nie obsługuje tego, co chcę osiągnąć. Zasadniczo używa on theadpool i przetwarza wątki, a to oznacza, że ​​otrzymuję jeden UnitOfWork na wątek !! Jednak potrzebowałem jednej jednostki pracy na żądanie strony internetowej.

Trochę googlowania dało mi this great post. Dokładnie tego chciałem; z wyjątkiem części jedności, która była dość łatwa do osiągnięcia.

To moja realizacja dla PerCallContextLifeTimeManager jedności:

public class PerCallContextLifeTimeManager : LifetimeManager 
{ 
    private const string Key = "SingletonPerCallContext"; 

    public override object GetValue() 
    { 
     return CallContext.GetData(Key); 
    } 

    public override void SetValue(object newValue) 
    { 
     CallContext.SetData(Key, newValue); 
    } 

    public override void RemoveValue() 
    { 
    } 
} 

I oczywiście to wykorzystać, aby zarejestrować jednostkę pracy z kodem podobnym do tego:

unityContainer 
      .RegisterType<IUnitOfWork, MyDataContext>(
      new PerCallContextLifeTimeManager(), 
      new InjectionConstructor()); 

nadzieję, że ratuje kogoś trochę czasu.

+0

Ładne rozwiązanie. Jeśli mogę, polecam zmianę nazwy tego na "CallContextLifetimeManager", ponieważ żądania internetowe są prawdopodobnie tylko jedną z potencjalnych aplikacji. –

+0

To prawda, zaktualizowałem tekst i kod, aby to odzwierciedlić. Dzięki. –

+0

+1 Bardzo pomocny. – MrDustpan

Odpowiedz

25

rozwiązanie Neat, ale każde wystąpienie LifetimeManager należy używać unikalnego klucza zamiast stałej:

private string _key = string.Format("PerCallContextLifeTimeManager_{0}", Guid.NewGuid()); 

W przeciwnym razie, jeśli masz więcej niż jeden obiekt zarejestrowany PerCallContextLifeTimeManager, oni mają ten sam klucz dostępu CallContext i nie odzyskasz oczekiwanego obiektu.

Warto również wdrażanie RemoveValue aby zapewnić obiekty są czyszczone:

public override void RemoveValue() 
{ 
    CallContext.FreeNamedDataSlot(_key); 
} 
+0

-1 CallContext jest odtwarzany na każde żądanie. Klucz nie musi być unikalny między różnymi instancjami twojego pojedynczego żądania. –

+8

+1 Właściwie to musi być dokładnie tak, jak powiedziano sześć razy. Jeśli nie przypiszesz unikatowych kluczy, wszystkie obiekty zostaną zarejestrowane pod jednym kluczem, a rzeczy zostaną pomieszane. –

+0

-1 Zobacz odpowiedź Stevena Robbinsa i komentarz Micah Zoltu na pytanie: pod obciążeniem 'CallContext' nie jest zachowywany dla pojedynczego żądania. –

20

Mimo to jest w porządku Wywołanie tej PerCallContextLifeTimeManager, jestem całkiem pewien, że to nie „bezpieczny” należy uznać za ASP.Net Per-request LifeTimeManager.

Jeśli ASP.Net stara nitka-swap to Jedyną rzeczą podjęte w poprzek do nowego wątku przez CallContext jest obecny HttpContext - cokolwiek innego można przechowywać w CallContext zniknie. Oznacza to, że pod dużym obciążeniem powyższy kod może mieć niezamierzone skutki - i wyobrażam sobie, że prawdziwym trudem byłoby wyśledzić dlaczego!

Jedynym „bezpieczny” sposób to zrobić z HttpContext.Current.Items lub robi coś takiego:

public class PerCallContextOrRequestLifeTimeManager : LifetimeManager 
{ 
    private string _key = string.Format("PerCallContextOrRequestLifeTimeManager_{0}", Guid.NewGuid()); 

    public override object GetValue() 
    { 
     if(HttpContext.Current != null) 
     return GetFromHttpContext(); 
     else 
     return GetFromCallContext(); 
    } 

    public override void SetValue(object newValue) 
    { 
     if(HttpContext.Current != null) 
     return SetInHttpContext(); 
     else 
     return SetInCallContext(); 
    } 

    public override void RemoveValue() 
    { 
    } 
} 

To oczywiście oznacza podejmowanie zależności System.Web :-(

Znacznie więcej informacji na ten temat pod adresem:

http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html

3

Dzięki za wkład,

Ale pytanie proponowana realizacja ma dwie wady, z których jeden to poważny błąd jak już stwierdził Steven Robbins w his answer i Micah Zoltu in a comment.

  1. kontekst sygn jest t gwarantowane do zachowania przez asp.net dla pojedynczego żądania. Pod obciążeniem może przełączyć się na inny, powodując przerwanie proponowanej implementacji.
  2. Nie obsługuje uwalniania zależności po zakończeniu żądania.

Obecnie pakiet Uniti.Mvc Nuget dostarcza PerRequestLifetimeManager do wykonania pracy. Nie zapomnij zarejestrować powiązanego kodu UnityPerRequestHttpModule w przeciwnym razie zwolnienie zależności nie będzie obsługiwane.

za pomocą instalacji programów

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule)); 

lub w web.config system.webServer/modules

<add name="UnityPerRequestHttpModule" type="Microsoft.Practices.Unity.Mvc.UnityPerRequestHttpModule, Microsoft.Practices.Unity.Mvc" preCondition="managedHandler" /> 

Wydaje obecną realizację nadaje się również do formularzy internetowych. I nie zależy nawet od MVC. Niestety, jego montaż ma miejsce, ponieważ zawiera inne klasy.

Uwaga: w przypadku użycia niestandardowego modułu http przy użyciu rozwiązanych zależności mogą one być już umieszczone w module EndRequest. Zależy to od kolejności wykonania modułu.

Powiązane problemy