2013-04-09 14 views
8

Niedawno wprowadziłem zmiany w mojej aplikacji MVC3, próbując prawidłowo pozbyć się obiektów DbContext [1]. To zadziałało świetnie, ale kiedy aplikacja została zepchnięta na mój serwer produkcyjny, zacząłem sporadycznie uzyskiwać zabawne wyjątki, które utrzymały się do czasu, aż AppPool zostanie poddany recyklingowi. Wyjątkiem może być wstecz do kodu w moim zwyczaju AuthorizeAttribute i wyglądać tak:Problemy po usunięciu DbContext

System.InvalidOperationException: The 'Username' property on 'User' could not be set to a 'Int32' value. You must set this property to a non-null value of type 'String'. 

System.InvalidOperationException: The 'Code' property on 'Right' could not be set to a 'String' value. You must set this property to a non-null value of type 'Int32'. 

(schematu bazy danych wygląda następująco: Użytkownicy: [Guid, String ...] Prawa: [Guid, Int32,. ..])

To tak, jakby niektóre "przewody zostały skrzyżowane", a aplikacja miesza wyniki z bazy danych: próbując zmaterializować wynik Right jako User i vice versa.

Aby zarządzać wyrzucaniem DbContext, wstawiam kod, aby zapisać go na poziomie kontrolera. Po usunięciu kontrolera, dysponuję również DbContext. Wiem, że to jest hacky, ale AuthorizeAttribute używa tego samego kontekstu przez filterContext.Controller.

Czy jest coś nie tak z obchodzeniem się z cyklem życia obiektu DbContext w tej posiadłości? Czy są jakieś logiczne wyjaśnienia, dlaczego otrzymuję powyższe wyjątki krzyżowe?

[1] Chociaż rozumiem, że nie jest konieczne dysponowanie obiektami DbContext, niedawno natknąłem się na wiele źródeł stwierdzających, że była to najlepsza praktyka niezależnie.

Edit (za @ komentarzu MikeSW za)

Właściwość z AuthorizeAttribute reprezentujących DbContext jest ustawiany w metodzie OnAuthorization, gdy AuthorizationContext jest w zasięgu. Ta właściwość jest później używana w metodzie AuthorizeCore.

+0

Czy możesz udostępnić niektóre z odpowiednich kodów niestandardowego AuthorizeAttribute? Zauważ, że atrybut jest używany jako singleton przez asp.net mvc. Czy używasz również pojemnika DI? – MikeSW

+0

@MikeSW Dodałem informację o powyższym użytkowaniu. Nie używam pojemnika DI. Dzięki informacjom podanym powyżej wydaje się, że te błędy występują ze względu na współbieżność: w czasie między 'OnAuthorization' i' AuthorizeCore', inne żądanie wyzwala 'OnAuthorization' i blokuje właściwość' DbContext'. Czy to następuje? –

+0

Tak, to twój problem. [Upoważnij] to w zasadzie singleton i zmieniasz właściwość dbcontext przy każdym żądaniu. Proponuję użyć DI Container, zarejestrować DbContext z HttpPerInstance lifetime następnie użyć DependencyResolver.Current.GetService () w metodzie OnAuthorization. Kontener powinien obsługiwać także DbContext – MikeSW

Odpowiedz

0

Najpierw zalecam, aby "naprawdę" zapoznać się z ASP.NET Application Life Cycle Overview for IIS 7.0, ponieważ jest to podstawą dobrego projektowania aplikacji MVC.

teraz, aby spróbować i „naśladować” baza kodu

Powiedzmy masz podobny niestandardowy MembershipProvider jak opisano tutaj https://stackoverflow.com/a/10067020/1241400

wtedy trzeba tylko zwyczaj Authorize atrybut

public sealed class AuthorizeByRoles : AuthorizeAttribute 
    { 
     public AuthorizeByRoles(params UserRoles[] userRoles) 
     { 
      this.Roles = AuthorizationHelper.GetRolesForEnums(userRoles); 
     } 
    } 

public static class AuthorizationHelper 
{  
    public static string GetRolesForEnums(params UserRoles[] userRoles) 
    { 
     List<string> roles = new List<string>(); 
     foreach (UserRoles userRole in userRoles) 
     { 
      roles.Add(GetEnumName(userRole)); 
     } 
     return string.Join(",", roles); 
    } 

    private static string GetEnumName(UserRoles userRole) 
    { 
     return Enum.GetName(userRole.GetType(), userRole); 
    }   
} 

które można użyć na dowolnym kontrolerze lub działaniu specyficznym

[AuthorizeByRoles(UserRoles.Admin, UserRoles.Developer)] 
public class MySecureController : Controller 
{ 
     //your code here 
} 

Jeśli chcesz, możesz również subskrybować zdarzenie PostAuthorizeRequest i odrzucić wyniki na podstawie niektórych kryteriów.

protected void Application_PostAuthorizeRequest(Object sender, EventArgs e) 
     { 

      //do what you need here 
     } 

chodzi o DbContext, nigdy nie biegać w sytuacji i tak per request to właściwe podejście, dzięki czemu można wyrzucać go w sterowniku lub w repozytorium.

Oczywiście zaleca się użycie filters, a następnie dodanie atrybutu [AllowAhnonymous] do swoich działań.

1

Czy rzeczywiście trzeba pozbyć się kontekstu?

Według this post Jon Gallant, który w kontakcie z zespołem Microsoft ADO.NET Entity Framework:

Czy zawsze muszę zadzwonić Dispose() na moich przedmiotów DbContext? Nie.

Zanim rozmawiałem z twórcami w zespole EF, moja odpowiedź zawsze brzmiała "oczywiście!". Ale nie jest to prawdą w przypadku DbContext. Nie musisz być religijny w kwestii wywoływania Pozbywaj się obiektów DbContext. Mimo że implementuje IDisposable, implementuje go tylko po to, aby można było wywołać Dispose jako zabezpieczenie w niektórych szczególnych przypadkach. Domyślnie DbContext automatycznie zarządza połączeniem za Ciebie.

Powiązane problemy