CSLA 4 - using the CslaModelBinder with MVC 2

CSLA 4 - using the CslaModelBinder with MVC 2

Old forum URL: forums.lhotka.net/forums/t/9357.aspx


davekaro posted on Wednesday, August 11, 2010

Hi,

I've setup a new MVC 2 application and want to use the new CslaModelBinder which is part of CSLA 4. I'd like to use the ViewModel pattern (as described here http://nerddinnerbook.s3.amazonaws.com/Part6.htm). So, I've created a ViewModel class:

public class AssetEditModel : ModelBase, IViewModel
    {
        public object ModelObject { get; set; }
        public SelectList Locations { get; set; }
        public SelectList Statuses { get; set; }
    }

Notice it implements IViewModel - since I noticed this in the 4.0 change log. My controller action looks like this:

        [HttpPost]
        public ActionResult Create(Asset asset)
        {
            var model = new AssetEditModel();

            asset = AssetService.AssetSave(asset); // calls asset.Save() if IsValid

            if (asset.IsValid)
            {
                return this.RedirectToAction("Index");
            }
           
            model.ModelObject = asset;
            LoadModel(model); // populates the select lists

            return this.View(model);
        }

My problem is, the validations don't work - at least the error messages. My CSLA object only requires a Name and Description. But, if I leave the form blank and submit, it tells me all the date fields are required (about 5 of them) and 2 integer fields. If I simply fill in the Name and Description, the form submits without a problem. I'm not sure I've set things up correctly to enable using a ViewModel with the CslaModelBinder. Am I missing something? Where are all these validations coming from? Also, if I add validations to the CSLA object for the integer fields, then I get duplicated validation messages.

xAvailx replied on Wednesday, August 11, 2010

Shouldn't your ModelObject be of a business class type, not "Object".

 

 

RockfordLhotka replied on Wednesday, August 11, 2010

The CslaModelBinder is designed primarily to enable the view to bind directly to a CSLA business object. In my opinion, a properly designed business object will meet the needs of the user scenario - which almost always means it is shaped correctly for the view. Since the controller handles all actions, this means you really shouldn't need a viewmodel.

Obviously not everyone agrees with that viewpoint Big Smile

So some additional work was done to CslaModelBinder to allow a viewmodel to be placed between the view and model, as long as it implements IViewModel - which means the viewmodel will expose the model (the CSLA object) as a property called ModelObject.

This follows the same basic philosophy CSLA uses to support MVVM in Silverlight/WPF as well.

The basic assumption remains that a properly designed business domain object will be shaped according to the use case/user scenario/story - which means it should be very close to the UI requirement. Additionally, CSLA ensures that model objects are fully bindable to all the UI technologies in .NET - so it is perfectly acceptable to bind a business object to the UI (the view).

In this context, the viewmodel becomes a way of extending the model. It shouldn't replace the model, and you shouldn't waste your time and bloat your code by duplicating all the properties from the model into the viewmodel (what a waste of time/effort!!).

The thing is this: in the final analysis CslaModelBinder is designed to streamline binding the view to the CSLA business domain object. If you aren't binding the view to your business object, then CslaModelBinder has no place in your application.

davekaro replied on Wednesday, August 11, 2010

So then you would just populate all the dropdown lists and other such things and dump them in ViewData?

RockfordLhotka replied on Wednesday, August 11, 2010

I use a unit of work object to manage these things. That way I make one data portal call to get everything I need for my use case.

http://forums.lhotka.net/forums/p/8535/40584.aspx#40584

This is also covered in the Core 3.8 video series.

garybr replied on Wednesday, August 11, 2010

You can use the view model as the container for both the business object itself as well as related lists of values that contribute to the view. This way you bind the view fields to the viewmodel.ModelObject members and the drop down list (or other list) sources to other viewmodel members. Here's a sample:

    public class CustomerViewModel : ViewModelBase<Customer>, IViewModel
    {
        //value list (use private backing field to load on-demand, not every view needs list
        private List<string> states;
        public List<string> StateList
        {
            get 
            {
                if (states == null)
                {
                    //can also be CSLA ROL, NVL or other collection
                    states = new List<string>();
                    states.Add("IL");
                    states.Add("WA");
                    states.Add("XX");
                    states.Add("YY");
                    states.Add("ZZ");
                }
                return states; 
            }
        }

        //default
        public CustomerViewModel()
        {
            ModelObject = Customer.NewCustomer();
        }

        //convenience
        public CustomerViewModel(int id)
        {
            ModelObject = Customer.GetCustomer(id);
        }
    }
You then bind the view fields to members of Customer, and list like this:

  <div class="editor-field">
      <%= Html.DropDownListFor(model => model.ModelObject.State, new SelectList(Model.StateList))%>
      <%= Html.ValidationMessageFor(model => model.ModelObject.State)%>
  </div>
If the CSLA object has validation rules they will be honored by the CslaModelBinder.

xAvailx replied on Thursday, August 12, 2010

This is the approach we take, although we are not using IViewModel (not on CSLA 4.0x)

Copyright (c) Marimer LLC