2012-08-25 1 views
19

"모델을 만드는 동안 컨텍스트를 사용할 수 없습니다."라는 메시지가 나타납니다. 내 웹 페이지 중 하나에서 내 웹 응용 프로그램의 문제. 이 특정 웹 페이지는 2-3 초마다 서버에 POST되어 화면을 새로 고칩니다. 내 테스트에서 나는이 페이지에 2 개 이상의 브라우저 인스턴스가 열려있는 경우 몇 분 후에 리포지토리의 깊은 곳에서 "모델을 만드는 동안 컨텍스트를 사용할 수 없습니다"라는 메시지가 나타납니다.EF - HTTP 요청 중에 모델을 만들 때 컨텍스트를 사용할 수 없습니다.

이 코드는 필요한 데이터를 검색하기 위해 "서비스"를 호출합니다. 이 코드는 MVC Controller 클래스의 사용자 정의 권한 특성에서 실행됩니다. 여기

public RoomStationModel GetRoomStation(int? roomStationId) 
{ 
    RoomStationModel roomStationModel = null; 
    if (roomStationId.HasValue) 
    { 
     using (IRepository<RoomStationModel> roomStationRepo = new Repository<RoomStationModel>(Context)) 
     { 
      roomStationModel = roomStationRepo.FirstOrDefault(rs => rs.RoomStationId == roomStationId.Value, false, new string[] { "Room" }); 
     } 
    } 

    return roomStationModel; 
} 

저장소입니다 .... 오류가

발생 : 여기
// Code in custom "Authorization" attribute on the controller 
int? stationId = stationCookieValue; // Read value from cookie 
RoomStationModel roomStationModel = RoomStationService.GetRoomStation(stationId); // Error occurs inside this call 

여기
public class RoomStationModel 
{ 
    [Key] 
    public int RoomStationId { get; set; } 

    public int? RoomId { get; set; } 
    [ForeignKey("RoomId")] 
    public virtual RoomModel Room { get; set; } 
    /* Some other data properties.... */ 
} 

public class RoomModel 
{ 
    [Key] 
    public int RoomId { get; set; } 

    public virtual ICollection<RoomStationModel> Stations { get; set; } 
} 

위의 서비스 호출에 대한 코드 인 "RoomStationModel"입니다
public class Repository<TObject> : IRepository<TObject> where TObject : class 
    { 
     protected MyContext Context = null; 

     public Repository(IDataContext context) 
     { 
      Context = context as MyContext; 
     } 

     protected DbSet<TObject> DbSet { get { return Context.Set<TObject>(); } } 

    public virtual TObject FirstOrDefault(Expression<Func<TObject, bool>> predicate, bool track = true, string[] children = null) 
    { 
     var objectSet = DbSet.AsQueryable(); 

     if (children != null) 
      foreach (string child in children) 
       objectSet = objectSet.Include(child); 

     if (track) 
      return objectSet.Where(predicate).FirstOrDefault<TObject>(predicate); 

     return objectSet.Where(predicate).AsNoTracking().FirstOrDefault<TObject>(predicate); 
    } 
} 

오류의 스크린 샷 : Screenshot of error occurring

스택 트레이스 :

at System.Data.Entity.Internal.LazyInternalContext.InitializeContext() 
    at System.Data.Entity.Internal.InternalContext.Initialize() 
    at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType) 
    at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize() 
    at System.Data.Entity.Internal.Linq.InternalSet`1.Include(String path) 
    at System.Data.Entity.Infrastructure.DbQuery`1.Include(String path) 
    at System.Data.Entity.DbExtensions.Include[T](IQueryable`1 source, String path) 
    at Vanguard.AssetManager.Data.Repository`1.FirstOrDefault(Expression`1 predicate, Boolean track, String[] children) in C:\Work\VanguardAssetManager\Main\Vanguard.AssetManager.Data\Repository.cs:line 100 
    at Vanguard.AssetManager.Services.Business.RoomStationService.GetRoomStation(Nullable`1 roomStationId) in C:\Work\VanguardAssetManager\Main\Vanguard.AssetManager.Services\Business\RoomStationService.cs:line 61 
    at Vanguard.AssetManager.Web.Attributes.RoomStationAuthorizeAttribute.OnAuthorization(AuthorizationContext filterContext) in C:\Work\VanguardAssetManager\Main\Vanguard.AssetManager.Web\Attributes\RoomStationAuthorizeAttribute.cs:line 52 
    at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor) 
    at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) 

EF 버전 : 4.1 (코드 첫 번째)

이 보인다
+0

이런 일은 일어나지 않습니다. 일반적으로 컨텍스트가 처음 사용될 때 모델이 한 번만 생성되기 때문에 코드가 실제로 잘못된 것입니다. 요청이있을 때마다 응용 프로그램이 응용 프로그램 풀을 재활용하지 않는 것이 확실합니까? –

+0

믿을 수 없다. 새로 고침 후 앱 풀을 재활용하면 어떻게 알 수 있습니까? 그게 IIS 일 것인가 아니면 코드 어딘가에 있을까요? – contactmatt

+0

재미있는 점 중 하나는 내 컨트롤러에서 사용자 지정 인증 특성을 사용할 때만이 오류가 발생한다는 것입니다. 맞춤 인증을 제거하면 오류가 사라집니다. – contactmatt

답변

0

두 가지 중 하나, 일부 종류 또는 "컨텍스트 범위 지정"문제의 경쟁 조건으로 . 컨텍스트가 스레드로부터 안전하게 초기화되고 컨텍스트가 경쟁 조건을 방지하기 위해 다른 스레드에 의해 액세스되지 않도록해야합니다. 이 오류의 원인을 찾기 어렵 기 때문에 OnModelCreation 재정의에서 모델 자체에 액세스 할 수도 있습니다.

30

리포지토리가 수명이 짧습니다. GetRoomStation()을 호출 할 때마다 생성되지만 실제 컨텍스트는 오래 지속됩니다 (RoomServiceStation.Context 속성). 즉, 웹 응용 프로그램을 호출 할 때마다 동일한 컨텍스트가 사용됩니다

웹 응용 프로그램의 구조적으로 상태가없는 모델에서 상태 (컨텍스트)를 유지하려고하는 "N 계층의 EF"시나리오입니다. 이러한 요청은 모두 동일한 컨텍스트가 다른 스레드에 있고 경쟁 조건이 발생했습니다.

한 스레드가 respo에서 컨텍스트를 처음 초기화 할 수 있습니다 nse는 요청에, 다른 하나는 컨텍스트를 사용하려고 시도합니다. 두 번째 요청은 컨텍스트를 사용할 준비가되었다고 생각하고이 예외를받습니다. in another SO thread과 같이 동시에 "스핀 업"을 시도하는 여러 컨텍스트가있는 경우에도이 문제가 발생할 수 있습니다.

몇 가지 작업을 수행 할 수 있습니다. 컨텍스트에 대한 액세스를 비관적으로 잠글 수는 있지만 불필요한 병목 현상을 유발할 수 있습니다. "클라이언트가 나에게 전화하기 전에 컨텍스트"코드를 작성하기 전에 "brute force"메서드 suggested in an MSDN thread을 사용하여 이것을 수행 할 수있는 좋은 곳을 찾아야한다.

더 좋은 방법은 에 대한 새 컨텍스트를 만들어 백 큐 서비스에 요청할 때마다 백 엔드 서비스에 요청하는 것입니다. 약간의 오버 헤드가 있습니다. 예, 그렇지만 최소한입니다.오버 헤드는 비관적 인 잠금보다 성능을 떨어 뜨릴 가능성이 적으며 팜에서 웹 응용 프로그램을 확장하는 응용 프로그램 풀 재활용 이벤트의 영향을받지 않습니다.

변경 내용 추적이나 컨텍스트의 다른 상태에 의존하는 경우이 이점을 잃게됩니다. 이 경우 데이터베이스 히트 추적 및 최소화를위한 다른 메커니즘을 찾아야합니다. 이 표현되는 MSDN article (강조 광산)에서

: N-계층에서 EF에 대한

If you serialize entities from one tier to another, the recommended pattern is to keep the context around on the mid-tier only long enough for a single service method call. Subsequent calls will spin up a new instance of the context to complete each task.

A thread on EF/WCF/N-tier may also give you some insights, 호르헤의 blog post #5 회담 (전체 시리즈가 좋은 읽을 수 있습니다). 그런데 저는 똑같은 문제에 봉착했습니다. 많은 고객이 동시에이 컨텍스트에 부딪혀이 문제가 발생했습니다.

+0

나는 동의한다. 나는 이것을 해결하기 위해 의존성 주입을 조사 할 것을 제안한다. 나는 최근 Ninject (http://www.ninject.org/)를 사용하여 MVC 프로젝트에 좋은 결과를 얻었습니다. – Gromer

+3

Unity에 내 컨텍스트를 등록 할 때 ContainerControlledLifetimeManager를 사용하고있었습니다. PerThreadLifetimeManager로 변경하고이 오류를 해결했습니다. – oldegreyg

+0

@ezycheez : 제 경우에는 HierarchicalLifetimeManager를 사용하여 컨텍스트를 등록했습니다. 그것은 같은 문제를 일으켰습니다. HierarchicalLifetimeManager 및 ContainerControlledLifetimeManager의 두 번째 문제점은 수동으로 백엔드 데이터베이스 데이터를 수정 한 경우입니다. 그런 변화가 내 프런트 엔드 웹 앱에 반영되지 않았습니다. 이는 HierarchicalLifetimeManager와 ContainerControlledLifetimeManager의 경우 Unity가 등록 된 유형 또는 객체의 동일한 인스턴스 (또는 싱글 톤 수명)를 반환하기 때문입니다. 대신 PerThreadLifetimeManager를 사용하여 2 가지 문제를 피하십시오. 잠금 및 데이터가 오래되었습니다. –

1

이 오류가 발생하여 컨트롤러의 Dispose() 메서드에 재정의를 제공하여 문제를 해결 한 것으로 보입니다. 새 데이터베이스를 열기 전에 데이터베이스 연결을 닫으면 강제로이 오류가 표시됩니다.

protected override void Dispose(bool disposing) 
{ 
    if(disposing) 
    { 
     _fooRepository.Dispose(); 
    } 
    base.Dispose(disposing); 
} 
0

오늘이 문제가 발생했습니다. 문제는 실수로 내 DbContext의 동일한 인스턴스를 요청간에 사용하고 있다는 것이 었습니다. 첫 번째 요청은 인스턴스를 만들고 모델을 빌드하기 시작하고 두 번째 요청은 들어 와서 데이터가 여전히 생성되는 동안 데이터를 검색하려고 시도합니다.

내 실수는 바보 같았습니다. 실수로 HttpContext.Current.Cache 대신 HttpContext.Current.Items를 사용했습니다.

관련 문제