EF - 在HTTP请求期间创建模型异常时,不能使用上下文

时间:2012-08-25 02:00:38

标签: c# asp.net-mvc-3 entity-framework entity-framework-4.1

我收到“在创建模型时无法使用上下文。”在我的一个网页中的Web应用程序中出现问题。此特定网页每2-3秒POST一次,以刷新屏幕。根据我的测试,我发现如果我有2个或更多浏览器实例打开到此页面,几分钟后我收到“在创建模型时无法使用上下文”异常来自存储库中的深层。

此代码调用“服务”来检索所需的数据。此代码在MVC Controller类的自定义授权属性中执行。

// 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(代码优先)

4 个答案:

答案 0 :(得分:30)

您的存储库是短暂的(您为每次调用GetRoomStation()创建它,但您的实际上下文似乎是长期存在的(RoomServiceStation.Context属性)。这意味着每次调用您的Web应用程序将使用相同的上下文。

这是“N层中的EF”场景,您尝试在Web应用程序的架构无状态模型中保留有状态(上下文)。所有这些请求都被引导到不同线程上的相同上下文中,并且您正在获得竞争条件。

一个线程可能会启动您的上下文的第一次初始化以响应请求,另一个线程可能会尝试使用上下文。第二个请求认为上下文已准备就绪,您将获得此异常。如果您有多个上下文试图在建议in another SO thread的同时“旋转”,您甚至可能会得到此信息。

你可以做一些事情。您可以尝试使用悲观锁定来访问您的上下文,但是您会遇到不必要的瓶颈。您可以尝试创建某种“在客户端呼叫我之前,初始化上下文”代码,但您必须找到一个好的地方,可能使用“强力”方法suggested in an MSDN thread

更好的做法是为您的后端服务创建每个请求的新上下文。有一些开销,是的,但很少。与悲观锁定相比,开销可能不太可能导致性能下降,并且不会受到应用程序池回收事件的影响,这些事件会扩展到服务器场中的Web应用程序等等。

如果您依赖于更改跟踪或上下文的其他有状态性质,您将失去此优势。在这种情况下,您将不得不提出一种不同的机制来跟踪和最小化数据库命中。

MSDN article总结一下(强调我的):

  

如果您将实体从一个层序列化到另一个层,建议使用   模式是保持中间层的上下文只够长   对于单个服务方法调用。后续调用将启动一个新的   完成每项任务的上下文实例。

A thread on EF/WCF/N-tier may also give you some insights和Jorge的blog post #5谈论N-Tiers中的EF(整个系列可能是一个很好的阅读)。顺便说一下,我遇到了同样的事情:许多客户同时触及上下文,导致了这个问题。

答案 1 :(得分:1)

我遇到了这个错误,似乎已经通过为控制器中的Dispose()方法提供覆盖来解决它。似乎在尝试打开新数据库之前强制关闭数据库连接会破坏此错误。

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

答案 2 :(得分:0)

这似乎是两件事之一,某种竞争条件或“背景范围”问题。您应该确保以线程安全的方式初始化上下文,并且不同的线程不访问上下文以防止竞争条件。很难理解这个错误也是在OnModelCreation覆盖中访问模型本身。

答案 3 :(得分:0)

我今天遇到了这个问题。问题是我不小心在请求中使用了相同的DbContext实例。第一个请求将创建实例并开始构建模型,第二个请求将进入并尝试在数据仍在构建时检索数据。

我的错误很愚蠢。我不小心使用了HttpContext.Current.Cache而不是HttpContext.Current.Items:)