C# EF - 在 HTTP 请求期间创建模型异常时无法使用上下文

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12118562/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 22:08:32  来源:igfitidea点击:

EF - The context cannot be used while the model is being created exception during HTTP requests

c#asp.net-mvc-3entity-frameworkentity-framework-4.1

提问by contactmatt

I am receiving "The context cannot be used while the model is being created." issue in my web application in one of my webpages. This particular webpage POSTs to the server every 2-3 seconds to refresh the screen. From my testing I found that If I have 2 or more browser instances open to this page, after several minutes I receive a "The context cannot be used while the model is being created" exception from deep in the repository.

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

This code calls a "service" to retrieve the needed data. This code is executed in an custom authorization attribute of the MVC Controller class.

此代码调用“服务”来检索所需的数据。此代码在 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

Here is the "RoomStationModel"

这是“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; }
}

Here is the code for the service call above:

这是上面服务调用的代码:

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

Here's the repository....where the error occurs

这是存储库....发生错误的地方

    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: Screenshot of error occurring

错误截图: 发生错误的屏幕截图

Stacktrace:

堆栈跟踪

  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 Version: 4.1 (Code first)

EF 版本:4.1(代码优先)

回答by JTMon

This seems as one of two things, a race condition of some sorts or a "context scoping" issue. You should make sure that the context is being initialized in a thread safe way and that the context is not being accessed by different threads to prevent race conditions. A hard to catch cause of this error is also the accessing of the model itself in the OnModelCreation override.

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

回答by Kit

Your repository is short-lived (you create it for each call to GetRoomStation()but your actual context appears to be long-lived (RoomServiceStation.Contextproperty). This means that every call to your web application is going to use the same context.

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

This is the "EF in an N-tier" scenario where you're trying to keep something stateful around (the context) in the architecturally stateless model of a web application. All those requests are being channeled to the same context on different threads and you're getting a race condition.

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

One thread could be kicking off first-time initialization of your context in response to a request, and another comes in attempting to use the context. The second request thinks the context is ready for use and you get this exception. You may even get this if you have multiple contexts trying to "spin up" at the same time as suggested in another SO thread.

一个线程可能会启动上下文的第一次初始化以响应请求,而另一个线程则试图使用上下文。第二个请求认为上下文已准备好使用,您会收到此异常。如果您有多个上下文试图按照另一个 SO 线程中的建议同时“启动”,您甚至可能会得到这个。

You can do a few things. You could try pessimistic locking around access to your context, but you're putting in an unnecessary bottleneck. You could try creating some sort of "before clients are calling me, initialize the context" code, but you have to find a good place to do this, perhaps using the "brute force" method suggested in an MSDN thread.

你可以做几件事。您可以尝试围绕对上下文的访问进行悲观锁定,但您会遇到不必要的瓶颈。您可以尝试创建某种“在客户呼叫我之前,初始化上下文”代码,但您必须找到一个好地方来执行此操作,也许使用MSDN 线程中建议的“蛮力”方法。

A better thing to do is to simply create a new context for everyrequest to your back-end service. There is some overhead, yes, but minimal. The overhead is probably less likely to kill performance than pessimistic locking, and won't be subject to app pool recycle events scaling out your web app on a farm and so on.

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

If you're relying on change tracking or other stateful nature of a context, you will lose this benefit. In this case, you're going to have to come up with a different mechanism for tracking and minimizing database hits.

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

From an MSDN articlethis is summed up (emphasis mine):

MSDN 文章中总结了这一点(强调我的):

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, and Jorge's blog post #5talks about EF in N-Tiers (the whole series might be a good read). And by the way, I've run into the exact same thing: many clients hitting the context at the same time, resulting in this issue.

关于 EF/WCF/N-tier 的一个主题也可能会给您一些见解,Jorge 的博客文章 #5讨论了 N-Tiers 中的 EF(整个系列可能是一个很好的读物)。顺便说一句,我遇到了完全相同的事情:许多客户端同时访问上下文,导致了这个问题。

回答by Matt Broyles

I encountered this error and have appeared to have solved it by providing an override to the Dispose() method in the controller. It would appear that force closing the database connection before attempting to open an new one subverts this error.

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

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

回答by Peter Morris

I experienced this problem today. The problem was that I was accidentally using the same instance of my DbContext across requests. The first request would create the instance and start to build up the model, and the second request would come in and try to retrieve data whilst it was still building up.

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

My mistake was silly. I accidentally used HttpContext.Current.Cache instead of HttpContext.Current.Items :)

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