0

I am trying to handle the http-post from a web form using MVC 2s built in model binding. From what I have been searching through for the past few hours I have figured it is a bit finicky with "objects within objects."

I'm looking for any kind of answers or links to resources that may help me figure this out. I'm still in the early stages of this also so if there is a better way - I'm all ears. I have been writing the Linq-SQL myself and not using the code generation. I think the closest answer I'm looking for is here, but I'm still not getting it.

Model Client (my best guess for where the issue is):

public class Client
{

    public int ClientID { get; set; }

    ...

    [Column(Name = "Address_id")]
    internal int AddressID { get; set; }

    internal EntityRef<Address> _address;
    [System.Data.Linq.Mapping.Association(ThisKey = "AddressID", Storage = "_address")]
    public Address Address
    {
        get { return _address.Entity; }
        internal set { _address.Entity = value; AddressID = value.AddressID; }
    }
}

Model Address (within client entity)

public class Address
{
    [Column] public string Address1 { get; set; }

    [Column] public string Address2 { get; set; }

    [Column] public string City { get; set; }

    ...

View Model:

    public class ClientFormViewModel
{
    public Client Client { get; set; }
    ...
}

View:

        <!-- id is hidden -->
        <%: Html.EditorFor(m => m.Client.ClientID) %>

        ...

        <%: Html.EditorFor(m => m.Client.Address.AddressID) %>

        <%: Html.LabelFor(m => m.Client.Address.Address1) %>
        <%: Html.EditorFor(m => m.Client.Address.Address1) %><br />           

        <%: Html.LabelFor(m => m.Client.Address.Address2) %>
        <%: Html.EditorFor(m => m.Client.Address.Address2) %><br />                   

        ...  

Controller:

    public ViewResult Edit(int clientId)
    {
        var client = clientsRepository.Clients.First(x => x.ClientID == clientId);
        ...

        // create view model
        var viewModel = new ClientFormViewModel
        {
            Client = client,
            ...
        };

        return View(viewModel);
    }

    [HttpPost]
    public ActionResult Edit(ClientFormViewModel clientForm)
    {
        if (ModelState.IsValid)
        {
            clientsRepository.SaveClient(clientForm.Client);
            return RedirectToAction("List");
        }
        else // validation error, so redisplay the same view
            ...
    }

So my issue is... when I get into the HttpPost action, the clientForm.Client.Address is always null. Although, when I look into the ModelState (which is valid), or use Request.["key"], all of the keys match the structure of my object. For example, I see ModelState["Client.Address.Address1"], "Client.Address.Address2", etc.

All other basic properties are filled in fine, which make me think the linq-sql code is breaking the model binding. But how? And is there a way to fix it? If those keys are within the Request/ModelState dictionary, why are they not being mapped to the object? Am I totally missing something obvious?

Community
  • 1
  • 1
SlimCharles
  • 55
  • 1
  • 7
  • Something you might want to try and do is pass your viewmodel to the view to render then bind your model in the post action. I don't think you are making it easier for yourself by swapping that around :). – Shaun Apr 01 '11 at 04:40
  • Maybe it is nested too deep? I do something similar, but my EditorFor lambda is model.Email.EmailAddress, so I am successfully using one less level of nesting. Also, can you post the html that is rendered for your form? Maybe the Ids/Names aren't getting rendered correctly. – jlnorsworthy Apr 01 '11 at 04:46
  • I don't think the level of nesting would be an issue. You will most likely find that the issue is due to the values being passed to the default model binder not matching the model being bound for whatever reason. You can always try and register your own model binder to check exactly what is being passed to the binder (or just look at the HTML as jlnors suggested :)) – Shaun Apr 01 '11 at 04:58
  • @Shaun I added the edit action that is being passed the simple view model I've created to create the view (see comment to Darin below) to the post. The only reason I was trying to bind to the view model in the httppost (I had tried just binding to "Client" before with the same issue with null child objects) was so I could redisplay the view upon validation error. @jlnorsworthy I am also going to check out the html later today. Although, I think my issue may be in the view model since I am passing my domain object. It makes more sense to me to have another layer between the domain/view model. – SlimCharles Apr 01 '11 at 15:01

2 Answers2

0

The Address setter must be public if you want the model binder to be able to successfully bind it:

internal EntityRef<Address> _address;
[System.Data.Linq.Mapping.Association(ThisKey = "AddressID", Storage = "_address")]
public Address Address
{
    get { return _address.Entity; }
    set { _address.Entity = value; AddressID = value.AddressID; }
}

Personally I would recommend you using view models instead of passing those models to and from the views.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • I am using a viewModel that is similar to "Pattern 2" in this [post](http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx). Basically my view model _contains_ the domain model. The "Pattern 3" is most likely what you're getting at? I think this is what I think I am most likely looking for. In this post he also links to [this post](http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/) which explains it a bit better, but also includes this "AutoMapper" thing. Is that a good thing to look into in this case? – SlimCharles Apr 01 '11 at 14:40
0

After @Darin Dimitrov mentioned using a view model instead of passing the domain model (which I thought I was already doing), I ended up figuring out a solution.

Instead of relying on the default model binding picking up my Linq-SQL mapping, I created a flattened view model.

What helped me the most were these two posts:

http://geekswithblogs.net/michelotti/archive/2009/10/25/asp.net-mvc-view-model-patterns.aspx

http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/

I had some issues with AutoMapper at first, but now it works pretty well. I would recommend it!

SlimCharles
  • 55
  • 1
  • 7