C# System.Web.Routing.RouteCollection.GetRouteData中的异常
我在iis7上运行的asp.net mvc代码中随机出现了两个异常: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
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;