Была изменена коллекция фильтров действий MVC; операция перечисления может не выполняться

Эта ошибка возникает очень часто в приложении MVC 5.1.3, когда это происходит, пользователь должен обновить страницу, и она уходит, поэтому ее проблема прерывистая.

Мы обнаружили, что это сложно диагностировать, поскольку, как представляется, это происходит в самой структуре. Любые идеи о том, где искать?

Это полный стек:

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at System.Web.Mvc.FilterProviderCollection.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
   at System.Web.Mvc.ControllerActionInvoker.GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state)
   at System.Web.Mvc.Controller.<BeginExecuteCore>b__1c(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
   at System.Web.Mvc.Async.AsyncResultWrapper.Begin[TState](AsyncCallback callback, Object callbackState, BeginInvokeDelegate`1 beginDelegate, EndInvokeVoidDelegate`1 endDelegate, TState invokeState, Object tag, Int32 timeout, SynchronizationContext callbackSyncContext)
   at System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
   at System.Web.Mvc.Async.AsyncResultWrapper.Begin[TState](AsyncCallback callback, Object callbackState, BeginInvokeDelegate`1 beginDelegate, EndInvokeVoidDelegate`1 endDelegate, TState invokeState, Object tag, Int32 timeout, SynchronizationContext callbackSyncContext)
   at System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state)
   at System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout)
   at System.Web.Mvc.Async.AsyncResultWrapper.Begin[TState](AsyncCallback callback, Object callbackState, BeginInvokeDelegate`1 beginDelegate, EndInvokeVoidDelegate`1 endDelegate, TState invokeState, Object tag, Int32 timeout, SynchronizationContext callbackSyncContext)
   at System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state)
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

Ранее был задан аналогичный вопрос: Коллекция была изменена; операция перечисления не может выполняться, но это было разрешено путем обновления до MVC 5, мы уже на 5.

Ответы

Ответ 1

Как отмечает @Erik в комментарии, очевидно, что сбор фильтров изменяется во время выполнения запросов, где он должен быть заполнен только во время запуска приложения.

Одна из возможностей заключается в том, что вы зарегистрировали пользовательский IFilterProvider, который разбивает код. Здесь исходный код, из которого происходит ошибка (из FilterProviderCollection)

        for (int i = 0; i < providers.Length; i++)
        {
            IFilterProvider provider = providers[i];
            foreach (Filter filter in provider.GetFilters(controllerContext, actionDescriptor))
            {
                filters.Add(filter);
            }
        }

provider.GetFilter(...) возвращает List, который позже изменен во время перечисления в блоке FilterProviderCollection foreach.

Вот что приходит мне на ум, чтобы проверить:

  • Прежде всего, убедитесь, что вы не регистрируете пользовательский IFilterProvider (либо вы, либо библиотеку, которую вы можете использовать). Если это так, это основной подозреваемый.

  • Посмотрите на все применения классов GlobalFilterCollection и GlobalFilters в вашем коде. Любые изменения в этой коллекции должны достигаться только от Application_Start в классе Global.asax или Startup (если вы используете OWIN)

  • Ищите подсказки, где вы создаете фильтры или получаете доступ к ним.

  • Попробуйте перейти на MVC 5.2. Если это проблема с каркасом (что кажется маловероятным), это может исправить это. (Я использую 5.2.2.0 и не вижу подобных проблем в разработке или производстве)

  • Отлаживайте приложение и устанавливайте точку прерывания в начале Application_BeginRequest и в действии контроллера. Попробуйте проверить коллекцию глобальных фильтров и посмотреть, есть ли какие-либо изменения.

  • Может быть, может быть полезно зарегистрировать пользовательский IFilterProvider и проверить поведение путем отладки или ведения журнала.