System.Web.Routing.RouteCollection.GetRouteData 中的异常
我在 iis7 上运行的 asp.net mvc 代码中随机出现两个异常:
Exception type: InvalidOperationException
Exception message: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Collections.Generic.List'1.Enumerator.MoveNextRare()
at System.Collections.Generic.List'1.Enumerator.MoveNext()
at System.Web.Routing.RouteCollection.GetRouteData(HttpContextBase httpContext)
at System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context)
at System.Web.Routing.UrlRoutingModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
并且
Exception type: NullReferenceException
Exception message: Object reference not set to an instance of an object.
at System.Web.Routing.RouteCollection.GetRouteData(HttpContextBase httpContext)
at System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context)
at System.Web.Routing.UrlRoutingModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
它不是一致可重现的,但我认为它正在改变(或损坏)RouteTable.Routes
。我在项目中访问 RouteTable.Routes
的唯一位置是 Global.asax.cs
,我知道那里的代码只被调用一次,所以这不是问题。关于如何追踪它有什么想法吗?
I've been getting two exceptions at random times in my asp.net mvc code running on iis7:
Exception type: InvalidOperationException
Exception message: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Collections.Generic.List'1.Enumerator.MoveNextRare()
at System.Collections.Generic.List'1.Enumerator.MoveNext()
at System.Web.Routing.RouteCollection.GetRouteData(HttpContextBase httpContext)
at System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context)
at System.Web.Routing.UrlRoutingModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
and
Exception type: NullReferenceException
Exception message: Object reference not set to an instance of an object.
at System.Web.Routing.RouteCollection.GetRouteData(HttpContextBase httpContext)
at System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context)
at System.Web.Routing.UrlRoutingModule.OnApplicationPostResolveRequestCache(Object sender, EventArgs e)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
It's not consistently reproducible, but I assume it's something changing (or corrupting) RouteTable.Routes
. The only place I access RouteTable.Routes
in my project is in Global.asax.cs
and I know that the code there is only being called once, so it's not the problem. Any idea on how to track it down?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
就我而言,它最终成为一个 HttpModule: Ext.Direct.Mvc< /a>(用于 ASP.NET MVC 的 Ext.Direct)。该模块有一个错误(在版本 0.8.0 中已修复),每次为 IHttpModule 调用 Init() 时都会重新注册路由。 (可能会被多次调用)。如果时机正确,它会破坏 RouteTable.Routes 集合,并导致上述两个异常之一。
In my case, it ended up being a HttpModule: Ext.Direct.Mvc (Ext.Direct for ASP.NET MVC). This module had a bug (Fixed in version 0.8.0) which registered routes again every time Init() was called for the IHttpModule. (which might be called multiple times). If the timing was right, it would corrupt the
RouteTable.Routes
collection, and cause one of the two exceptions above.该错误与 .Net 中的集合不是线程安全的一致。
根据 MSDN 文章 RouteCollection.GetWriteLock()、
RouteTable.Routes
不是线程安全的。此问题是因为某些代码尝试以非线程安全的方式修改RouteTable.Routes
。最佳实践是修改
Application_Start
中的RouteTable.Routes
。如果您必须以可由多个线程同时访问的方式修改它,请确保以线程安全的方式进行修改。检查 MSDN 中的示例,复制如下以便于参考:从 RouteTable 读取时同样适用 - RouteCollection.GetReadLock()。
如果通过删除 HttpModule 来解决此问题,可能是因为该 HttpModule 没有实现此类锁定机制。
That error is consistent with a collection not being thread-safe in .Net.
As per the MSDN article on RouteCollection.GetWriteLock(),
RouteTable.Routes
is not thread-safe. This issue is because some piece of code tried to modify theRouteTable.Routes
in a manner which was not thread-safe.The best practice is to modify the
RouteTable.Routes
in theApplication_Start
. If you have to modify it in a way which can be accessed by multiple threads at the same time, make sure you make it in a thread-safe way. Check the example from MSDN, copied below for ease of reference:Same applies for when reading from the RouteTable - RouteCollection.GetReadLock().
Probably, if this was fixed by removing the HttpModule, it is because this HttpModule does not implement such locking mechanisms.
其他答案解释了发生了什么,但没有提供有关如何实际追踪的详细信息。造成所有这些麻烦的可能不是您自己的代码,因此 Ctrl-F 是不够的。
这个想法
使得解决这个问题变得困难,因为您通常会在与实际修改 RouteCollection 不同的请求上收到错误。对
UrlHelper.GenerateUrl()
等通常安全的调用可能会在看似随机的时间失败。为了追踪这个问题,我选择通过在初始设置后禁止更改路线来将例外情况从受害者转移到罪魁祸首。第 1 步:实现自定义
RouteCollection
如果设置了标志,则当有人试图更改路线时大声喊叫。这是一个粗略的实现,但你明白了。
步骤 2:替换默认集合
在 Global.asax 的 Application_start 中,在设置路由之前将默认 RouteCollection 替换为您的实例。由于似乎没有 API 可以替换它,因此我们通过私有字段上的反射来强制它:
步骤 3:设置预期路由后限制更改
全部完成! 现在观察错误请求亮起在异常日志中。
可能的罪魁祸首:ImageResizer
在我的例子中,它是一个名为
ImageResizer
(v4.0.4) 的第三方组件,其内部MvcRoutingShimPlugin
擅自添加/删除路线,而无需锁定。这个特定的错误已经报告并修复(尽管目前尚未正式发布) 。Other answers explained WHAT happens but provided little details on how to actually track it down. It may not be your own code causing all this trouble, so Ctrl-F is not enough..
The idea
What makes solving it difficult, is the fact that you usually get errors on a different requests than the ones actually modifying RouteCollection. A generally safe call to
UrlHelper.GenerateUrl()
or the like would fail at seemingly random times. To track it down, I chose to move exceptions from victims to the culprit by forbidding changing routes after the initial set up.Step 1: Implement custom
RouteCollection
IF a flag is set then yell hard when someone tries to change routes. It's a crude implementation but you get the idea.
Step 2: Replace the default collection
In Application_start in Global.asax replace the default RouteCollection with your instance before the routes are set up. As there does not seem to be an API to replace it then we force it with reflection on the private fields:
Step3: Restrict changes once intended routes are set up
All done! Now watch the bad request light up in exception logs.
A possible culprit: ImageResizer
In my case it was a 3-rd party component named
ImageResizer
(v4.0.4) whose internalMvcRoutingShimPlugin
took the liberty of adding/removing a route without locking. This specific bug was already reported and fixed (though at this time not yet officially released).