Custom Model Binder for Complex composite objects HELP
Asked Answered
M

2

7

I am trying to write a custom model binder but I'm having great difficulty trying to figure how to bind complex composite objects.

this is the class I'm trying to bind to:

public class Fund
{
        public int Id { get; set; }
        public string Name { get; set; }
        public List<FundAllocation> FundAllocations { get; set; }
}

and this is how my attempt at writing the custom binder looks like:

public class FundModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        throw new NotImplementedException();
    }

    public object GetValue(ControllerContext controllerContext, string modelName, Type modelType, ModelStateDictionary modelState)
    {
        var fund = new Fund();

        fund.Id = int.Parse(controllerContext.HttpContext.Request.Form["Id"]);
        fund.Name = controllerContext.HttpContext.Request.Form["Name"];

        //i don't know how to bind to the list property :(
        fund.FundItems[0].Catalogue.Id = controllerContext.HttpContext.Request.Form["FundItem.Catalogue.Id"];
        return fund;
    }
}

Any Ideas

thanks Tony

Monday answered 3/4, 2009 at 16:14 Comment(1)
I just posted a question and then eventually figured out a solution to this same type of issue. For those interested, you may want to check out the below link: https://mcmap.net/q/752001/-how-do-i-pass-a-dictionary-as-a-parameter-to-an-actionresult-method-from-jquery-ajaxEthyne
T
8

Do you really need to implement a custom ModelBinder here? The default binder may do what you need (as it can populate collections and complex objects):

Lets say your controller action looks like this:

public ActionResult SomeAction(Fund fund)
{
  //do some stuff
  return View();
}

And you html contains this:

<input type="text" name="fund.Id" value="1" />
<input type="text" name="fund.Name" value="SomeName" />

<input type="text" name="fund.FundAllocations.Index" value="0" />
<input type="text" name="fund.FundAllocations[0].SomeProperty" value="abc" />

<input type="text" name="fund.FundAllocations.Index" value="1" />
<input type="text" name="fund.FundAllocations[1].SomeProperty" value="xyz" />

The default model binder should initialise your fund object with 2 items in the FundAllocations List (I don't know what your FundAllocation class looks like, so I made up a single property "SomeProperty"). Just be sure to include those "fund.FundAllocations.Index" elements (which the default binder looks at for it's own use), that got me when I tried to get this working).

Toady answered 22/4, 2009 at 10:43 Comment(3)
JonoW - do you have a link for good documentation for the standard model binder, or did you just look at the source?Fungosity
Will, sorry don't have a link to any offical documentation, I was really just going on the advice of Phil Haack on his blog, as I had a similar problem - haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx. There may be a better way to do it in 1.0, not sure...Toady
He is correct, you do not need a custom model binder. You can do this by manipulating the name field as demonstrated above.Sherri
S
3

I have been spending too much on this exact same thing lately!

Without seeing your HTML form, I am guessing that it is just returning the results of selection from a multi select list or something? If so, your form is just returning a bunch of integers rather than returning your hydrated FundAllocations object. If you want to do that then, in your custom ModelBinder, you're going to need to do your own lookup and hydrate the object yourself.

Something like:

fund.FundAllocations = 
      repository.Where(f => 
      controllerContext.HttpContext.Request.Form["FundItem.Catalogue.Id"].Contains(f.Id.ToString()); 

Of course, my LINQ is only for example and you obviously can retrieve the data anyway that you want. Incidentally, and I know that it doesn't answer your question but after much faffing around I have decided that for complex objects, I am best to use a ViewModel and have the default ModelBinder bind to that and then, if I need to, hydrate the model which represents my entity. There are a number of issues that I ran into which made this the best choice, I won't bore you with them now but am happy to extrapolate if you wish.

The latest Herding Code podcast is a great discussion of this as are K Scott Allen's Putting the M in MVC blog posts.

Signory answered 22/4, 2009 at 10:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.