Ugh ModelBinding?

Rate It (2)

Last post 10-24-2008 11:17 AM by CraigALebowitz. 16 replies.

Sort Posts:

  • Re: Ugh ModelBinding?

    09-08-2008, 11:42 PM
    • Contributor
      4,372 point Contributor
    • tgmdbm
    • Member since 12-17-2007, 2:08 PM
    • Posts 883
    • ASPInsiders
      TrustedFriends-MVPs

    levib:

    Arrays of complex data types (e.g. MyModel[]) don't yet work; we're looking into this.

     

    I've been using the MvcContrib NameValueDeserializer for arrays of complex objects but there's still one scenario that i've had to fudge to get working. It would be great if you could concider supporting this scenario.

    When updating a one-to-many relationship in a single post, i go through the usual Retrieve from DB > Update with form values > Validate > Save to DB routine as with any other entity, but several things should happen to the collection, entries which no longer exist need to be removed, new ones added, and changed ones updated.

    I've named my form elements in the following way

    Order.OrderDetails[(OrderDetailId}].Property

    When using the NameValueDeserializer from MvcContrib, if you pass in the collection retrieved from the DB, it will simply add new objects to the list (duplicating them). So you first need to deserialize it into a new list, iterate over that, and deserialize each OrderDetail again, but this time passing the corresponding OrderDetail from the collection from the DB (updating it with the values from the form).

    It works but it requires you to deserialize each OrderDetail twice which doesn't sit well.

    If there was some way of telling the framework that the bit between the brackets is the Id of the OrderDetail and that it's unique (in this context) then not only could the deserialization take this into account (and perform the removal, addition, and upate of elements in the collection), but you could also use it in your helper methods to generate the form element names correctly.

    Cheers

  • Re: Ugh ModelBinding?

    10-24-2008, 11:17 AM
    • Member
      2 point Member
    • CraigALebowitz
    • Member since 10-24-2008, 11:11 AM
    • Washington, DC
    • Posts 1

    Here is some code that provides a helper to rebind arrays of complex types.  Any class T which has a parameterless constructor can be enumerated on the page as

    name="Person[-1].Id"
    name="Person[-1].Name"

    name="Person[0].Id"
    name="Person[0].Name"

    name="Person[1].Id"
    name="Person[1].Name"

    and then rebound by calling ModelBinderHelper.GetEnumerable<Person>(bindingContext)

    Note that this depends on the IModelBinder interface from Asp.Net Mvc Beta.  We were using MvcContrib's NameValueDeserializer before the advent of IModelBinder 

    1        /// <summary>
    2        /// Helper class for model binding
    3        /// </summary>
    4        public class ModelBinderHelper
    5        {
    6            /// <summary>
    7            /// Get an array of a complex type, T, from the form.  
    8            /// Convention on form is TypeName[-1], TypeName[0], TypeName[1], etc.
    9            /// The type must have a parameterless public constructor.
    10           /// </summary>      
    11           public static IEnumerable GetEnumerable(ModelBindingContext parentBindingContext)
    12               where T : class, new()
    13           {                        
    14               string typeName = typeof(T).Name;
    15               var prefixRegex = new Regex(string.Format(@"^{0}\[(-?\d+)\]", typeName));
    16   
    17               return parentBindingContext.HttpContext.Request.Form.AllKeys
    18                   .Select(s => prefixRegex.Match(HttpUtility.UrlDecode(s)))
    19                   .Where(m => m != Match.Empty)
    20                   .Select(m => long.Parse(m.Groups[1].Value))
    21                   .Distinct()
    22                   .Select(
    23                   index => ModelBinders.GetBinder(typeof (T)).BindModel(GetContext(index, parentBindingContext)).Value as T);
    24           }
    25   
    26           private static ModelBindingContext GetContext(long index, ModelBindingContext bindingContext) where T : class, new()
    27           {
    28               string typeName = typeof(T).Name;
    29               return new ModelBindingContext(bindingContext.Controller.ControllerContext,
    30                                              bindingContext.ValueProvider,
    31                                              typeof (T),
    32                                              string.Format("{0}[{1}]", typeName, index),
    33                                              () => new T(),
    34                                              bindingContext.ModelState,
    35                                              (property) => true);
    36           }
    37       }
    
     
     
Page 2 of 2 (17 items) < Previous 1 2