8

Używam ASP.NET MVC 5 i Identity Framework. Po wywołaniu UserManager.UpdateAsync (...) uruchomione zostaną moje funkcje obsługi zdarzeń w ApplicationDbContext() SaveChanges. Tutaj używam HttpContext.Current do różnych celów (logowanie i audyt), więc muszę powiedzieć, aktualny użytkownik. Jednak cała metoda działa w wątku roboczym, a tutaj HttpContext.Current ma wartość null.HttpContext.Current ma wartość null w metodach Identity Framework

Największy problem związany z metodami "synchronizacji" UserManagera dotyczą tylko wersji asynchronicznej, więc wywołania są serializowane, ale metody (i sterowniki zdarzeń) nadal działają w innym wątku roboczym.

Uwaga: ten problem nie ma nic wspólnego z kontekstem async/await. W kontrolerze po oczekiwaniu (lub wywołaniu wersji "sync") zwróciłem poprawny HttpContext, nawet metoda kontrolera kontynuuje się w innym wątku. W porządku.

Problem tkwi w module async, który będzie działał zarówno w wersjach "synchronizacji", jak i asynchronizacji. Myślę, że rozumiem te zjawiska (ale nie jestem zadowolony z fałszywej wersji metody "synchronizacji", prawdziwe metody synchronizacji nie wykazałyby tego problemu.) Po prostu nie wiem, jak sobie z tym poradzić/obejść.

[btw: Czy nie byłoby bardziej naturalne wdrożenie operacji UserManager jako prostych wersji czystej synchronizacji, a następnie zawinięcie ich przez asynchroniczne wielowątkowe owijki ?. Jeśli będziemy kontynuować tę asynchroniczną modę bez zastanowienia, wkrótce wymyślimy operatora asynchronicznego przydziału. Kosztuje mnie to kilkadziesiąt godzin (tylko ten numer) i kosztuje miliard dolarów na całym świecie, jestem pewien, że w wielu przypadkach jest to mniejszy zwrot niż jego cena.]

Bonus: Mówimy o UserManager, który ma wpływ marginalny, ale taki sam zasady i problemy mogą dotyczyć każdej z bibliotek pudełkowych (czarna skrzynka dla ciebie), której autorzy nie implementują wersji synchronizacji i lub nie dbają o kontekst wątku kontrolera. A co z EF, to nie jest tak marginalne ... a co z infrastrukturą tworzenia instancji kontenerów DI, jak "zakres żądania" lub "zakres sesji". Z pewnością źle się zachowują, jeśli rozdzielanie odbywa się w wątku bez HttpContext.Current. Niedawno odświeżyłem SendGrid NuGet i (jako zmiana zerwania) metoda Deliver() odeszła, a teraz istnieje tylko DeliverAsync() ...

Chciałbym mieć bezpieczny i niezawodny sposób, w jaki sposób uzyskać dostęp do HttpContext wewnątrz tego pracownika do celów rejestrowania i audytu.

Przykładowy kod, sterownik 'sync' wersja:

[AcceptVerbs(HttpVerbs.Post)] 
public virtual ActionResult Edit(ApplicationUser user) 
{ 
    // validation etc 
    // Update() seems to be only a poor wrapper around the async version, still uses a worker thread. 
    var result = UserManager.Update(user); 
    // Note: HttpContext is correct here so it is not an async/await problem 

    // error handling, creating ActionResult etc. 
} 

Przykładowy kod, wersja kontrolera asynchroniczny:

[AcceptVerbs(HttpVerbs.Post)] 
public virtual async Task<ActionResult> Edit(ApplicationUser user) 
{ 
    // validation etc 
    var result = await UserManager.UpdateAsync(user); 
    // Note: HttpContext is correct here so it is not an async/await problem 

    // error handling, creating ActionResult etc. 
} 

i obsługi zdarzeń gdzie HttpContext jest zerowy:

public ApplicationDbContext() : base("DefaultConnection", false) 
{ 
    InitializeAudit(); 
} 

private void InitializeAudit() 
{ 
    var octx = ((IObjectContextAdapter) this).ObjectContext; 

    octx.SavingChanges += 
     (sender, args) => 
     { 
      // HttpContext.Current is null here 
     }; 
} 

Jakieś pomysły?

+0

Rozważ wyświetlenie kodu ... –

+0

Czy możesz wyświetlić konkretny kod, o który pytasz? – i3arnon

+1

Jak o przechowywaniu 'HttpContext.Current' w zmiennej lokalnej przed utworzeniem jakichkolwiek zadań i przekazywanie go do zadań/wątków w jakiś sposób. – EZI

Odpowiedz

4

Tak jak powiedziałeś, dzieje się tak z powodu wątków. Uczestnik działa w innym wątku, co uniemożliwia dostęp do HttpContext.

Możesz przenieść zmienną poza delegata, co spowoduje jej zamknięcie.

private void InitializeAudit() 
{ 
    var octx = ((IObjectContextAdapter) this).ObjectContext; 
    HttpContext context = HttpContext.Current; 

    octx.SavingChanges += 
     (sender, args) => 
     { 
      // context is not null 
     }; 
} 
+1

Dzięki, że coś podobnego. Jednak to nie ja, który tworzy instancję ApplicationDbContext, jest to także Identity Framework, więc nie ma gwarancji, że zostanie utworzony w wątku kontrolera, więc w jego konstruktorze nie jest konieczne mamy HttpContext. –

1

Używasz tożsamość asp.net przez owin, więc jedna instancja dbcontext jest tworzony na zamówienie, i można dostać to odwołanie z dowolnego miejsca na żądanie rurociągu.

nb. jest to przydatne, ale myślę, że dbcontext nie powinien być dostępny poza menedżerem. W projektowaniu tożsamości asp.net, tylko kierownik powinien być świadomy sklepu. Uważam, że dbcontext jest narażony, ponieważ kilka middleware asp.net tożsamości ma na nim zależność.

Ale to może pomóc rozwiązać Ci problem:

Pozwól zwyczaj dbcontext obsługi należy ustawić poza klasą:

public EventHandler SavingChangesEventHandler 
     { 
      set 
      { 
       (((System.Data.Entity.Infrastructure.IObjectContextAdapter)this).ObjectContext).SavingChanges += value; 
      } 
     } 

zadeklarować klasę zwyczaj ActionFilter i zarejestrować go, a następnie zastąpić OnActionExecuting:

Filtrowanie w ASP.NET MVC https://msdn.microsoft.com/en-us/library/gg416513(VS.98).aspx

public class CustomizeAppDbcontextFilter : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     var dbcontext = HttpContext.GetOwinContext().Get<ApplicationDbContext>();  
     var currentuser = HttpContext.Current.User; 

     dbcontext.SavingChangesEventHandler = (sender, args) => 
      { 
       // use currentuser 
      }; 
    }  
} 

może trzeba te użyciu instrukcji, aby móc wywołać identity.owin metod rozszerzeń:

pomocą Microsoft.AspNet.Identity;

przy użyciu Microsoft.AspNet.Identity.Owin;

Powinieneś być w wątku kontrolera, ponieważ OnActionExecuting owija kontroler akcji.

Nie przetestowałem go, więc może wymagać trochę polerowania, ale koncepcja powinna działać.

Powiązane problemy