2012-08-25 28 views
19

我正在接收「正在創建模型時無法使用上下文。」在我的網頁應用程序中出現問題。這個特定的網頁每2-3秒發送一次到服務器刷新屏幕。從我的測試中,我發現如果我有2個或更多瀏覽器實例打開到此頁面,幾分鐘後,我會收到存儲庫中深處的「創建模型時無法使用上下文」異常。EF - 在HTTP請求期間創建模型異常時無法使用上下文

該代碼調用「服務」來檢索所需的數據。此代碼在MVC控制器類的自定義授權屬性中執行。

// 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 

這裏是 「RoomStationModel」

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; } 
} 

這裏是上面的服務電話代碼:

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; 
} 

這裏的倉庫....當錯誤發生時

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屬性),這意味着你的web應用程序每次調用將使用相同的上下文

這是「N層中的EF」場景,您嘗試在Web應用程序的架構無狀態模型中保持有狀態(上下文),所有這些請求都被引導到相同在不同的線程上下文,你會得到一個競爭條件。

一個線程可能會啓動首次初始化您的上下文在respo一個請求,另一個來試圖使用上下文。第二個請求認爲上下文已準備好使用,並且您得到此異常。如果您有多個環境試圖在建議in another SO thread的同時「旋轉」,您甚至可能會得到這個結果。

你可以做一些事情。你可以試着對你的上下文進行悲觀的鎖定,但是你正在陷入不必要的瓶頸。你可以嘗試創建某種「在客戶打電話給我之前,初始化上下文」的代碼,但是你必須找到一個合適的地方來做這件事,也許使用「蠻力」方法suggested in an MSDN thread

要做的更好的事情是簡單地創建一個新的上下文每個請求到您的後端服務。有一些開銷,是的,但很小。與悲觀鎖定相比,開銷可能會降低性能,並且不會受到應用程序池回收事件擴展服務器場上的Web應用程序等影響。

如果您依賴更改跟蹤或上下文的其他有狀態特性,則會失去此優勢。在這種情況下,你將不得不想出一個跟蹤和最小化數據庫命中的不同機制。

MSDN article這是總結了(重點煤礦):關於EF的N-層級

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,和Jorge的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來避免這兩個問題;他們是鎖定和數據陳舊。 –

1

我遇到了這個錯誤,並且似乎已經通過向控制器中的Dispose()方法提供覆蓋來解決它。在嘗試打開新數據庫之前強制關閉數據庫連接似乎會破壞此錯誤。

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

今天我遇到了這個問題。問題是我不小心在請求中使用了同一個DbContext實例。第一個請求會創建實例並開始建立模型,第二個請求將進入並嘗試在數據建立時檢索數據。

我的錯誤是愚蠢的。我不小心使用HttpContext.Current.Cache而不是HttpContext.Current.Items :)

相關問題