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

C# System.Web.Routing.RouteCollection.GetRouteData中的异常,c#,asp.net-mvc,iis-7,iis-6,asp.net-mvc-routing,C#,Asp.net Mvc,Iis 7,Iis 6,Asp.net Mvc Routing,我在iis7上运行的asp.net mvc代码中随机出现了两个异常: Exception type: InvalidOperationException Exception message: Collection was modified; enumeration operation may not execute. at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) a

我在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)


它并不总是可复制的,但我认为它是在改变(或破坏)
RouteTable.Routes
。我在项目中访问
RouteTable.Routes
的唯一位置是
Global.asax.cs
,我知道那里的代码只被调用一次,所以这不是问题所在。有没有关于如何追踪它的想法?

在我的例子中,它最终成为了一个HttpModule:(Ext.Direct for ASP.NET MVC)。此模块有一个bug(在版本0.8.0中已修复),每当为IHttpModule调用Init()时,该bug都会再次注册路由。(). 如果计时正确,则会损坏
RouteTable.Routes
集合,并导致上述两种异常之一。

该错误与.Net中不具有线程安全性的集合一致

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

最佳做法是修改
应用程序启动
中的
RouteTable.Routes
。如果必须以多个线程可以同时访问的方式对其进行修改,请确保以线程安全的方式进行修改。检查MSDN中的示例,为便于参考,复制如下:

using (RouteTable.Routes.GetWriteLock())
{
    Route newRoute = new Route("{action}/{id}", new ReportRouteHandler());
    RouteTable.Routes.Add(newRoute);
}
当从RouteTable读取时,同样适用


如果通过移除HttpModule解决了这个问题,可能是因为这个HttpModule没有实现这种锁定机制。

其他答案解释了发生的情况,但提供了一些关于如何实际跟踪它的细节。可能不是您自己的代码导致了所有这些问题,所以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:替换默认集合 在应用程序中,\u start In Global.asax在设置路由之前,用实例替换默认RouteCollection。由于似乎没有API来替代它,因此我们在私有字段上强制使用反射:

var routeTable = new LockableRouteCollection();
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:一旦设置了预期路线,则限制更改 全部完成现在观察异常日志中的错误请求亮起

可能的罪魁祸首:ImageResizer
在我的例子中,它是一个名为
ImageResizer
(v4.0.4)的第三方组件,其内部
MvcRoutingShimPlugin
可以在不锁定的情况下随意添加/删除路由。这个特定的bug已经发布了(虽然目前还没有正式发布)。

这条评论真是救命稻草!我遇到了与OP相同的问题。我确信我自己的代码没有修改关于路由表的任何内容,我为什么要这样做呢。所以我对此一无所知,所以我决定使用这种方法。事实证明,如果是ImageResizer插件以及在我的情况下(但它可能是其他东西)。
var routeTable = new LockableRouteCollection();
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);
RouteConfig.RegisterRoutes(routeTable);
routeTable.EnableRestrictions = true;