EF - 而在HTTP请求的模型正在创建异常上下文无法使用(EF - The context ca

2019-06-27 09:17发布

我收到“正在创建的模型,而上下文无法使用。” 问题在我的网页我的一个Web应用程序。 这种特殊的网页发送到服务器每隔2-3秒刷新屏幕。 从我的测试中,我发现,如果我有2个或更多的浏览器实例中打开这个页面,几分钟后,我收到来自深的库异常的“而在创建模型的情况下,不能使用”。

此代码调用“服务”来获取所需的数据。 这个代码在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);
    }
}

错误的截图:

堆栈跟踪

  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(代号第一)

Answer 1:

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

这是方案中的“在N层EF”,其中你想保持在一个Web应用程序的体系结构模型无国籍状态的东西左右(上下文)。 所有这些请求都被引导到在不同的线程相同的背景和你得到的竞争条件。

一个线程可以拉开你的上下文的第一次初始化响应请求,另一个进来尝试使用的上下文。 第二个请求认为,上下文是准备就绪,你会得到这个例外。 你甚至可能会得到这个,如果你有多个环境努力的建议,同时为“旋转起来” 在另一个线程SO 。

你可以做一些事情。 你可以尝试悲观锁周围访问您的上下文,但你在不必要的瓶颈推杆。 你可以尝试建立某种形式的“客户端给我打电话之前,初始化上下文”的代码,但你必须找到一个好地方要做到这一点,或许用“蛮力”的方法在MSDN线程建议 。

一个更好的事情做的是简单地创建的每个请求后端服务的新环境。 有一些开销,是的,但微乎其微。 开销大概是不太可能杀死比悲观锁的性能,并且不会受到应用程序池回收事件向外扩展您的网络应用程序在一个农场等。

如果你依靠的变化跟踪或上下文的其他状态特性,你将失去这项福利。 在这种情况下,你将不得不拿出一个不同的机制,以跟踪和减少数据库命中。

从MSDN的文章 ,这是总结了(重点煤矿):

如果你从一个层到另一个序列化实体,推荐模式是保持上下文周围端市场足够长只为单个服务方法调用。 后续调用将旋转了一个新的实例的上下文的完成每项任务。

在EF / WCF / N级的线程也可能会给你一些启发 ,和Jorge的博客文章#5关于EF的N-层级(全系列可能是一个很好看的)会谈。 顺便说一句,我碰到同样的事情:很多客户打的情况下,在同一时间,导致这个问题。



Answer 2:

我遇到了这个错误,并已出现通过提供覆盖到控制器的Dispose()方法已经解决了它。 这样看来,力试图打开一个新的颠覆这个错误之前关闭数据库连接。

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


Answer 3:

这似乎是两件事情之一,某些种类或“上下文范围界定”的问题中的竞争条件。 你应该确保该上下文是在线程安全的方式被初始化和上下文没有被不同的线程访问,以防止竞争条件。 硬赶上这个错误的原因也是模型本身在OnModelCreation覆盖的访问。



Answer 4:

我今天遇到这个问题。 问题是,我不小心用我的跨请求的DbContext的同一个实例。 第一个请求将创建实例,并开始建立起来的模型,而第二个请求会进来,并尝试获取数据,同时它仍在建设。

我的错误是愚蠢的。 我不小心用HttpContext.Current.Cache代替HttpContext.Current.Items :)



文章来源: EF - The context cannot be used while the model is being created exception during HTTP requests