diff --git a/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs b/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs
index bf3586a174..e1b57709f3 100644
--- a/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs
+++ b/src/Umbraco.Web/Macros/PartialViewMacroEngine.cs
@@ -143,7 +143,7 @@ namespace Umbraco.Web.Macros
//bubble up the model state from the main view context to our custom controller.
//when merging we'll create a new dictionary, otherwise you might run into an enumeration error
// caused from ModelStateDictionary
- controller.ModelState.Merge(new ModelStateDictionary(viewContext.ViewData.ModelState));
+ controller.ModelState.MergeSafe(new ModelStateDictionary(viewContext.ViewData.ModelState));
controller.ControllerContext = new ControllerContext(request, controller);
//call the action to render
var result = controller.Index();
diff --git a/src/Umbraco.Web/ModelStateExtensions.cs b/src/Umbraco.Web/ModelStateExtensions.cs
index 08868e4e5f..29f4eff120 100644
--- a/src/Umbraco.Web/ModelStateExtensions.cs
+++ b/src/Umbraco.Web/ModelStateExtensions.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
@@ -8,24 +7,51 @@ namespace Umbraco.Web
{
internal static class ModelStateExtensions
{
+ ///
+ /// Safely merges ModelState
+ ///
+ ///
+ ///
+ /// The MVC5 System.Web.Mvc.ModelStateDictionary.Merge method is not safe
+ public static void MergeSafe(this ModelStateDictionary state, ModelStateDictionary dictionary)
+ {
+ if (dictionary == null)
+ return;
+ // Need to stuff this into a temporary new dictionary that we're allowed to alter,
+ // if we alter "state" in this enumeration, it fails with
+ // "Collection was modified; enumeration operation may not execute"
+ var tempDictionary = new ModelStateDictionary(state);
+ foreach (var entryKey in dictionary.Keys)
+ {
+ tempDictionary[entryKey] = dictionary[entryKey];
+ }
+ // Update state with updated dictionary
+ state = tempDictionary;
+ }
- ///
- /// Merges ModelState that has names matching the prefix
- ///
- ///
- ///
- ///
- public static void Merge(this ModelStateDictionary state, ModelStateDictionary dictionary, string prefix)
+ ///
+ /// Merges ModelState that has names matching the prefix
+ ///
+ ///
+ ///
+ ///
+ public static void Merge(this ModelStateDictionary state, ModelStateDictionary dictionary, string prefix)
{
- if (dictionary == null)
- return;
- foreach (var keyValuePair in dictionary
+ if (dictionary == null)
+ return;
+ // Need to stuff this into a temporary new dictionary that we're allowed to alter,
+ // if we alter "state" in this enumeration, it fails with
+ // "Collection was modified; enumeration operation may not execute"
+ var tempDictionary = new ModelStateDictionary(state);
+ foreach (var keyValuePair in dictionary
//It can either equal the prefix exactly (model level errors) or start with the prefix. (property level errors)
.Where(keyValuePair => keyValuePair.Key == prefix || keyValuePair.Key.StartsWith(prefix + ".")))
{
- state[keyValuePair.Key] = keyValuePair.Value;
+ tempDictionary[keyValuePair.Key] = keyValuePair.Value;
}
- }
+ // Update state with updated dictionary
+ state = tempDictionary;
+ }
///
/// Checks if there are any model errors on any fields containing the prefix
diff --git a/src/Umbraco.Web/Mvc/ControllerExtensions.cs b/src/Umbraco.Web/Mvc/ControllerExtensions.cs
index 03e31f34c4..28626e70fb 100644
--- a/src/Umbraco.Web/Mvc/ControllerExtensions.cs
+++ b/src/Umbraco.Web/Mvc/ControllerExtensions.cs
@@ -140,7 +140,7 @@ namespace Umbraco.Web.Mvc
{
//when merging we'll create a new dictionary, otherwise you might run into an enumeration error
// caused from ModelStateDictionary
- result.ViewData.ModelState.Merge(new ModelStateDictionary(controller.ViewData.ModelState));
+ result.ViewData.ModelState.MergeSafe(new ModelStateDictionary(controller.ViewData.ModelState));
// Temporarily copy the dictionary to avoid enumerator-modification errors
var newViewDataDict = new ViewDataDictionary(controller.ViewData);
diff --git a/src/Umbraco.Web/Mvc/MergeModelStateToChildActionAttribute.cs b/src/Umbraco.Web/Mvc/MergeModelStateToChildActionAttribute.cs
index 7d2df43962..28eee44939 100644
--- a/src/Umbraco.Web/Mvc/MergeModelStateToChildActionAttribute.cs
+++ b/src/Umbraco.Web/Mvc/MergeModelStateToChildActionAttribute.cs
@@ -32,7 +32,7 @@ namespace Umbraco.Web.Mvc
{
if (filterContext.Controller.ControllerContext.IsChildAction)
{
- filterContext.Controller.ViewData.ModelState.Merge(
+ filterContext.Controller.ViewData.ModelState.MergeSafe(
filterContext.Controller.ControllerContext.ParentActionViewContext.ViewData.ModelState);
}
}
diff --git a/src/Umbraco.Web/Mvc/UmbracoPageResult.cs b/src/Umbraco.Web/Mvc/UmbracoPageResult.cs
index 1c12e7e498..70200c627a 100644
--- a/src/Umbraco.Web/Mvc/UmbracoPageResult.cs
+++ b/src/Umbraco.Web/Mvc/UmbracoPageResult.cs
@@ -110,7 +110,7 @@ namespace Umbraco.Web.Mvc
tempDataDictionary.Save(context, new SessionStateTempDataProvider());
var viewCtx = new ViewContext(context, new DummyView(), new ViewDataDictionary(), tempDataDictionary, new StringWriter());
- viewCtx.ViewData.ModelState.Merge(context.Controller.ViewData.ModelState);
+ viewCtx.ViewData.ModelState.MergeSafe(context.Controller.ViewData.ModelState);
foreach (var d in context.Controller.ViewData)
viewCtx.ViewData[d.Key] = d.Value;
@@ -124,7 +124,7 @@ namespace Umbraco.Web.Mvc
///
private static void CopyControllerData(ControllerContext context, ControllerBase controller)
{
- controller.ViewData.ModelState.Merge(context.Controller.ViewData.ModelState);
+ controller.ViewData.ModelState.MergeSafe(context.Controller.ViewData.ModelState);
foreach (var d in context.Controller.ViewData)
controller.ViewData[d.Key] = d.Value;