System.Web.Routing.RouteCollection.GetRouteData 中的异常

发布于 2024-08-14 08:26:06 字数 1720 浏览 7 评论 0原文

我在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

深空失忆 2024-08-21 08:26:06

就我而言,它最终成为一个 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.

森林迷了鹿 2024-08-21 08:26:06

该错误与 .Net 中的集合不是线程安全的一致。

根据 MSDN 文章 RouteCollection.GetWriteLock()RouteTable.Routes 不是线程安全的。此问题是因为某些代码尝试以非线程安全的方式修改 RouteTable.Routes

最佳实践是修改 Application_Start 中的 RouteTable.Routes。如果您必须以可由多个线程同时访问的方式修改它,请确保以线程安全的方式进行修改。检查 MSDN 中的示例,复制如下以便于参考:

using (RouteTable.Routes.GetWriteLock())
{
    Route newRoute = new Route("{action}/{id}", new ReportRouteHandler());
    RouteTable.Routes.Add(newRoute);
}

从 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 the RouteTable.Routes in a manner which was not thread-safe.

The best practice is to modify the RouteTable.Routes in the Application_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:

using (RouteTable.Routes.GetWriteLock())
{
    Route newRoute = new Route("{action}/{id}", new ReportRouteHandler());
    RouteTable.Routes.Add(newRoute);
}

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.

好听的两个字的网名 2024-08-21 08:26:06

其他答案解释了发生了什么,但没有提供有关如何实际追踪的详细信息。造成所有这些麻烦的可能不是您自己的代码,因此 Ctrl-F 是不够的。

这个想法

使得解决这个问题变得困难,因为您通常会在与实际修改 RouteCollection 不同的请求上收到错误。对 UrlHelper.GenerateUrl() 等通常安全的调用可能会在看似随机的时间失败。为了追踪这个问题,我选择通过在初始设置后禁止更改路线来将例外情况从受害者转移到罪魁祸首。

第 1 步:实现自定义 RouteCollection

如果设置了标志,则当有人试图更改路线时大声喊叫。这是一个粗略的实现,但你明白了。

public class RestrictedRouteCollection : RouteCollection
{
    public Boolean EnableRestrictions { get; set; }

    protected override void InsertItem(Int32 index, RouteBase item)
    {
        if (EnableRestrictions) { throw new Exception("Unexpected route added."); }
        base.InsertItem(index, item);
    }

    protected override void SetItem(Int32 index, RouteBase item)
    {
        if (EnableRestrictions) { throw new Exception("Unexpected change of RouteCollection item."); }
        base.SetItem(index, item);
    }

    protected override void RemoveItem(Int32 index)
    {
        if (EnableRestrictions) { throw new Exception("Unexpected removal from RouteCollection, index: " + index); }
        base.RemoveItem(index);
    }

    protected override void ClearItems()
    {
        if (EnableRestrictions) { throw new Exception("Unexpected clearing of routecollection."); }
        base.ClearItems();
    }
}

步骤 2:替换默认集合

在 Global.asax 的 Application_start 中,在设置路由之前将默认 RouteCollection 替换为您的实例。由于似乎没有 API 可以替换它,因此我们通过私有字段上的反射来强制它:

var routeTable = new RestrictedRouteCollection();
var field = typeof(RouteTable)
    .GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic);
if (field == null) { throw new Exception("Expected field _instance was not found."); }
field.SetValue(null, routeTable);

Debug.Assert(RouteTable.Routes == routeTable);

步骤 3:设置预期路由后限制更改

RouteConfig.RegisterRoutes(routeTable);
routeTable.EnableRestrictions = true;

全部完成! 现在观察错误请求亮起在异常日志中。

可能的罪魁祸首: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.

public class RestrictedRouteCollection : RouteCollection
{
    public Boolean EnableRestrictions { get; set; }

    protected override void InsertItem(Int32 index, RouteBase item)
    {
        if (EnableRestrictions) { throw new Exception("Unexpected route added."); }
        base.InsertItem(index, item);
    }

    protected override void SetItem(Int32 index, RouteBase item)
    {
        if (EnableRestrictions) { throw new Exception("Unexpected change of RouteCollection item."); }
        base.SetItem(index, item);
    }

    protected override void RemoveItem(Int32 index)
    {
        if (EnableRestrictions) { throw new Exception("Unexpected removal from RouteCollection, index: " + index); }
        base.RemoveItem(index);
    }

    protected override void ClearItems()
    {
        if (EnableRestrictions) { throw new Exception("Unexpected clearing of routecollection."); }
        base.ClearItems();
    }
}

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:

var routeTable = new RestrictedRouteCollection();
var field = typeof(RouteTable)
    .GetField("_instance", BindingFlags.Static | BindingFlags.NonPublic);
if (field == null) { throw new Exception("Expected field _instance was not found."); }
field.SetValue(null, routeTable);

Debug.Assert(RouteTable.Routes == routeTable);

Step3: Restrict changes once intended routes are set up

RouteConfig.RegisterRoutes(routeTable);
routeTable.EnableRestrictions = true;

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 internal MvcRoutingShimPlugin 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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文