CSLA 3.8 MVC ModelBinder

CSLA 3.8 MVC ModelBinder

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


xAvailx posted on Tuesday, December 01, 2009

Hi,

Does the new MVC ModelBinder work when using a ViewModel class?

I can get it to work if my view uses a BO directly. But when I introduce a ViewModel class and using TryUpdateModel, it doesn't seem to update the properties.

RockfordLhotka replied on Tuesday, December 01, 2009

Using MVVM in a web app? Really? MVVM is a pattern designed around the binding characteristics of XAML, why would you use it in a web app, especially a MVC web app?

Does the DefaultModelBinder work with a ViewModel? If so, I'll certainly look at making the CSLA one do so as well, but as far as I know it doesn't.

xAvailx replied on Wednesday, December 02, 2009

Haven't worked with XAML, but I am guessing they share some of the same benefits as in MVC. You can create custom shapped ViewModel specific to a view, allows you to have strongly typed views which is nice when sharing more than one model with the view.

Scott Gu - has an explanation here on NerdDinner:

http://nerddinnerbook.s3.amazonaws.com/Part6.htm

And actually, the CSLA MVC Contribute project also has some ViewModels :)

>> Does the DefaultModelBinder work with a ViewModel?

I believe that is the case.

RockfordLhotka replied on Wednesday, December 02, 2009

Can you show a code example of how this works?

There are two kinds of viewmodel (in general). There are view model objects that re-implement all the properties of the contained model object (lots of work, seems silly to me). And there are view model objects that expose the model as a property (makes a lot more sense, and is what I chose to do with the XAML MVVM support in CSLA).

As I understand the modelbinder approach, they just reflect against the target object (whatever type you provide) and set the properties of the target object based on the names of the items in the postback dictionary. In other words they do essentially the same thing DataMapper.Map() does.

CslaModelBinder doesn't actually do any of that work - it inherits from DefaultModelBinder - which is the reason for my question. CslaModelBinder should work exactly like DefaultModelBinder except in how it handles validation errors, because that's the only part of the base behavior I override.

So my guess is that DefaultModelBinder wouldn't work either :)

rasupit replied on Wednesday, December 02, 2009

RockfordLhotka:
There are two kinds of viewmodel (in general). There are view model objects that re-implement all the properties of the contained model object (lots of work, seems silly to me). And there are view model objects that expose the model as a property (makes a lot more sense, and is what I chose to do with the XAML MVVM support in CSLA).

Agree with Rocky on this.  The shape of CSLA objects most of the time come out to be very close to the view, there is no or very little impedance mismatch between CSLA object and VM (which is not true in DDD).  Therefore the first kind seems kinda silly to me too.

RockfordLhotka:
As I understand the modelbinder approach, they just reflect against the target object (whatever type you provide) and set the properties of the target object based on the names of the items in the postback dictionary. In other words they do essentially the same thing DataMapper.Map() does

The data mapping actually do a little more.  It support mapping to a list, so the formcollection dictionary may contain the following keys: product[1].name, product[2].name....
There is also a quirk in DefaultModelBinder. When binding an ICollection property, DefaultModelBinder will try to rebuild the collection by creating new collection object and add items to the new collection. The same thing happen for all complex type property. Binding CSLA object that have child collection (or child object) won't work.  This is why I created DefaultCslaModelBinder which address any of these issues.

Ricky

xAvailx replied on Wednesday, December 02, 2009

And there are view model objects that expose the model as a property


That is the one I am referring to.

CslaModelBinder doesn't actually do any of that work - it inherits from DefaultModelBinder - which is the reason for my question. CslaModelBinder should work exactly like DefaultModelBinder except in how it handles validation errors, because that's the only part of the base behavior I override.

So my guess is that DefaultModelBinder wouldn't work either :)



I see your point, maybe has something to do with MVC 2 Beta. I will look into it a bit more. What is strange to me is that when I use a Csla Model it works correctly, but when I introduce a ViewModel with BO properties it fails to do the Sets on post. I will play around with it some more.

RockfordLhotka replied on Wednesday, December 02, 2009

I guess I should double-check - we are talking about the new CslaModelBinder in the Csla.Web.Mvc namespace right? Not something in a contrib project?

xAvailx replied on Wednesday, December 02, 2009

Correct, I am referring to the default CslaModelBinder. Again, I may have something to do with MVC 2 Beta, I will download the MVC 2 beta source and figure out what is going on...

RockfordLhotka replied on Thursday, December 03, 2009

When you call UpdateModel() you must pass in the model, not the viewmodel. Is this what you are doing?

xAvailx replied on Thursday, December 03, 2009

Oh man this is strange...

No, I was actually passing in the Model.

But just for giggles, I try passing in the ViewModel and that worked!!!! ?

xAvailx replied on Thursday, December 03, 2009

This is what my code looks like for reference and it works...



[HttpPost]
public ActionResult Edit(int? id, FormCollection collection) {
ConsumerName name;
if (id.HasValue) {
try {
name = ConsumerName.Get(id.Value);
}
catch (NotFoundException) {
return View("NotFound");
}
}
else {
name = ConsumerName.New(mConsumerId);
}

if (!TryUpdateModel(ToViewModel(name)) || !name.IsValid) {
return View(ToViewModel(name));
}

try {
name.Save();
SetInfoMessage("Saved");
}
catch (MvcPrototype.Library.Base.CCPBaseException ex) {
ModelState.AddModelError("error", ex.Message);
return View(ToViewModel(name));
}

return RedirectToAction("Index");
}

Copyright (c) Marimer LLC