R – S#arp Architecture model binder enumeration error

asp.net-mvcs#arp-architecture

I am receiving an error from the SharpArchitecture model binder. "Collection was modified; enumeration operation may not execute." (Stack trace at the bottom).

My MVC pages to create and edit my SettingsModel throw the error. This started happening when we upgraded to the release version of S#arp Architecture 1.0. My class has a few Lists as properties. One of the Lists contains classes with other another List as a property. I'm not sure which List is throwing the error. Can anyone provide direction as to how to troubleshoot this or things to look for in my models that might cause it?

This is my SettingsModel class:

public class SettingsModel : Entity
 {
  public SettingsModel() 
  {
   AttributeSettingsList = new List<AttributeSettingsModel>();
  }

  public virtual void AddAttributeSettings(AttributeSettingsModel attSettings)
  {
   AttributeSettingsList.Add(attSettings);
   attSettings.Settings = this;
  }


  [NotNullNotEmpty(Message = "Description must be provided")]
  public virtual string Description { get; set; }

  [DomainSignature]
  [Range(0, 100, Message = "ModelAPercentage must be between 0 and 100")]
  public virtual int ModelAPercentage { get; set; }

  [DomainSignature]
  [Range(0, 100, Message = "ModelBPercentage must be between 0 and 100")]
  public virtual int ModelBPercentage { get; set; }

  public virtual IList<AttributeSettingsModel> AttributeSettingsList { get; set; }

  public virtual IList<EntityMappingModel> EntityMappingList { get; set; }

  public SettingsModel(string Description, int ModelAPercentage, int ModelBPercentage)
   : this() 
  {
   this.Description = Description;
   this.ModelAPercentage = ModelAPercentage;
   this.ModelBPercentage = ModelBPercentage;
  }

 }

This is the SharpArchitecture method throwing the error:

    private void SetEntityCollectionProperty(ModelBindingContext bindingContext,
        PropertyDescriptor propertyDescriptor, object value) {

        if (value as IEnumerable != null &&
            IsSimpleGenericBindableEntityCollection(propertyDescriptor.PropertyType)) {

            object entityCollection = propertyDescriptor.GetValue(bindingContext.Model);
            Type entityCollectionType = entityCollection.GetType();

            foreach (object entity in (value as IEnumerable)) {
                entityCollectionType.InvokeMember("Add",
                    BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, entityCollection,
                    new object[] { entity });
            }
        }
    }

Here is the stack trace:

[InvalidOperationException: Collection was modified; enumeration operation may not execute.]
System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) +51
System.Collections.Generic.Enumerator.MoveNextRare() +7661017
System.Collections.Generic.Enumerator.MoveNext() +61
SharpArch.Web.ModelBinder.SharpModelBinder.SetEntityCollectionProperty(ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, Object value) +358
SharpArch.Web.ModelBinder.SharpModelBinder.SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor, Object value) +61
System.Web.Mvc.DefaultModelBinder.BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) +265
SharpArch.Web.ModelBinder.SharpModelBinder.BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) +225
System.Web.Mvc.DefaultModelBinder.BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) +125
System.Web.Mvc.DefaultModelBinder.BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Object model) +293
System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +772
System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +345
SharpArch.Web.ModelBinder.SharpModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +39
System.Web.Mvc.DefaultModelBinder.UpdateCollection(ControllerContext controllerContext, ModelBindingContext bindingContext, Type elementType) +408
System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +756
System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +345
SharpArch.Web.ModelBinder.SharpModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +39
System.Web.Mvc.DefaultModelBinder.BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) +231
SharpArch.Web.ModelBinder.SharpModelBinder.BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor) +225
System.Web.Mvc.DefaultModelBinder.BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext) +125
System.Web.Mvc.DefaultModelBinder.BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Object model) +293
System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +772
System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +345
SharpArch.Web.ModelBinder.SharpModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) +39
System.Web.Mvc.ControllerActionInvoker.GetParameterValue(ControllerContext controllerContext, ParameterDescriptor parameterDescriptor) +219
System.Web.Mvc.ControllerActionInvoker.GetParameterValues(ControllerContext controllerContext, ActionDescriptor actionDescriptor) +109
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +399
System.Web.Mvc.Controller.ExecuteCore() +126
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +27
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +7
System.Web.Mvc.MvcHandler.ProcessRequest(HttpContextBase httpContext) +151
System.Web.Mvc.MvcHandler.ProcessRequest(HttpContext httpContext) +57
System.Web.Mvc.MvcHandler.System.Web.IHttpHandler.ProcessRequest(HttpContext httpContext) +7
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +181
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75

Best Answer

There appears to be a bug in the sharp architecture SetEntityCollectionProperty method. Its in the last four lines:

        foreach (object entity in (value as IEnumerable)) {
            entityCollectionType.InvokeMember("Add",
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, entityCollection,
                new object[] { entity });
        }

Its intent seems to be to populate the model collection class (entityCollection) from the values in the IEnumerable passed in as value. However, these two objects are the same reference. It's already populated. When the "Add" method is invoked, the entityCollection is modified which is the same object that is being iterated. Then the exception is thrown.

Comment or delete that code block and your code should run.

Related Topic