views:

316

answers:

1

I have a page that is made up of many user controls. The view model for this page is rather complex.

public class ComplexViewModel
{
    public ObjectA ObjectAProperty { get; set; }
    public List<Things> ListOfThings { get; set; }
    public List<ThingCategories> ListOfThingCategories { get; set; }
    public List<ThingTypes> ListOfThingTypes { get; set; }
    public List<ThingOptions> ListOfThingOptions { get; set; }
    public int ChosenThingCategoryId { get; set; }
    public int ChosenThingTypeId { get; set; }
    public int ChosenThingOptionId { get; set; }
    public OtherObject ObjectData { get; set; }
}

This page also has an PostModel that contains information for filtering, sorting, etc.

    public class SimplePostModel
{
    public int ChosenThingCategoryId { get; set; }
    public int ChosenThingTypeId { get; set; }
    public int ChosenThingOptionId { get; set; }
    public int ChosenThingFilterTypeId { get; set; }
    public int ChosenThingSortTypeId { get; set; }
    public int ChosenThingOtherId { get; set; }
    public int ChosenThingMoreId { get; set; }
    public int ChosenThingOMGId { get; set; }
}

The simple PostModel is validated and then the controller opens 3+ repositories making multiple calls into each and builds the view model. To say the least my controller action has gotten quite large.

This is by far the most complex page I've worked on and I'm having a hard time deciding how to make it simpler.

My first thought was to create a view model factory that, after binding validation, would call into the repositories and return the ViewModel.

Then I thought about creating a custom model binder that would validate the PostModel and then hydrate the ViewModel in one step.

So my question is how do you hydrate a complex view model?

And while I write this I had the idea of using Html.RenderAction and creating a model for each of the user controls that make up this beast of a page.

Update:

The repositories make calls into WCF services, the application is part of a larger SOA arch.

+1  A: 

Some general tips. Data can be separated into several categories: system-scope, session-scope, request-scope.

System scope data is data that needs to be presented to the user but is the same for each user. An example for a blog application would be a tag cloud, or a list of categories. I would argue that this data doesn't need to flow through the controller or action because it has nothing to do with user interaction. The View itself can call a HtmlHelper (or a LayoutDataHelper) that knows how to get (and preferably cache) this data.

Session scope data can be handled with ActionFilters that populate fields on the ViewData.Model. It is not directly related to the parameters of the action. For example, username. I prefer an attribute of the form

public class SessionDataFilter : ActionFilter 
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (filterContext.Result is ViewResult)
        {
            var view = filterContext.Result as ViewResult;

            // hydrate session data on view.ViewData.Model
        }
    }
}

Everything else that is request-scope/specific must be populated in the Action. However, this doesn't mean you have to have one massive action method to do it. I would look at how your ViewModels are composed. As you suggested, if you have controls that need populating, it's likely that the information in the ViewModel can be grouped into related sets.So you might have a ViewModel that just composes other smaller view models ("partial view models"). I would then decompose the logic to populate each partial view model (and any other complex logic) each into its own re-usable and isolated method.

Similar abstraction applies when dealing with posts, though I would worry about the usability of pages that post lots of unrelated data. You should be able to use ActionFilters (OnActionExecuting) to parse related sets of incoming data (and optionally validating) and assign them to action parameters. I prefer filters over binders for posted data unless the same set of data is posted to multiple actions and the shape of the incoming data is always the same.

Good luck.

gWiz