views:

1505

answers:

4

I am interested to know what you guys feel should be deemed "correct behaviour" in terms of the UpdateModel method in ASP.NET MVC.

The reason I ask here is perhaps if this functionality is "by design" somebody could clarify as to why it is the way it is, and perhaps a way to call it differently to achieve desired functionality, which I would imagine would be the way 90% of folk would want this to work?

In essence, my gripe lies with the behaviour of the binding process within UpdateModel.

Supposing you wish to update a form via a simple Save action method for which the data fields on the form reflects a model in your database, initially to go about saving the request, we might get the existing model from the DB, and then update relevant fields which which were changed, sent via FormCollection and then updated by UpdateModel to our existing model. This functions, however it appears any of the existing properties on this DB-populated object are being "reset"; and by that I mean, are being set to null or initialisation defaults just as if it was a brand new object, except for obviously those which match those in the FormCollection.

This is a problem because any existing properties which exist on the object, but not necessarily exist on the form, such as any child collections or objects, dates or any non-UI facing fields are empty, leaving you with a half-populated, more or less unusable object which can't be saved to the DB because of all the missing data including probably a stack of ID's now set to 0.

I believe this is not desirable behaviour, and UpdateModel should only update properties where it finds a property match in FormCollection. This would mean all your existing properties would be untouched, but your updates would be set. However, from what has been deduced so far, obviously this is not the case - it appears it instantiates a brand new copy of the object updates the properties from the form, then returns the new object.

Finally, to put it in perspective of how much of a burden this is, the only way really around it to save a half-complex form and keep all your existing object data is to manually marry up each property with the corresponding form property to absolutely guarantee only properties that exist in the form are being updated.

I guess,

  1. Those who agree this is by design, is my approach of form marrying the best way?
  2. Or, how have you tackled this in this?

Please feel free to offer your thoughts on this guys, Thanks.

Here is another instance of somebody suffering from this problem:
http://stackoverflow.com/questions/1207991/calling-updatemodel-with-a-collection-of-complex-data-types-reset-all-non-bound-v

+5  A: 

I believe you are right about the behavior of UpdateModel.

However, ASP.NET MVC follows a "round-trip" model, meaning that your form should already contain all of the fields it needs to produce a complete record, either because you pushed values for all of the fields into the view, or you are asking for all of the fields from the user.

This round-trip concept is very important. Remember that in a true MVC model there is no concept of state. You retrieve data from a database table, push this data to a view, the data is displayed to the user, and the program stops. The user edits the data, clicks your post button, the view posts data to a controller method, the data is saved into the database, and the program stops. There are no dependencies at all from one operation to the next.

This practice of not keeping partial state of records and data structures makes it very straightforward to write applications that scale well, and are well-behaved (particularly with respect to things like the back button in the browser).

Robert Harvey
But I feel if I followed this concept, I would have a much weightier view and include a lot of redundant data not necessary, and especially not necessary to be posted as it is not to be changed. Plus no idea how this would be handled for collections and other relationships mapped via LINQ to SQL.
GONeale
ASP.NET MVC views are much lighter weight than conventional ASP.NET. The markup is much cleaner, and you don't have to contend with ViewState. How much data do you really have anyway? Is it an online job application, or something similar with lots of form fields?
Robert Harvey
No, it's a simple administration tool. I wouldn't expect this problem to be only with complicated systems either; as soon as you're talking 1+ child lists on your model, as Levi explains, you already enter a world of problems, so any programmer will encounter this problem, and probably have already.
GONeale
Respectfully, I think you're calling something a problem that isn't. I have pages running in an application that have 100K of data in them, and they load up almost instantly.
Robert Harvey
OK, that's fine re. size. But I still struggle with the idea of representing my entire object model in a form, especially if say I had 2 child objects and a couple of lists, I'm not sure how this would easily be mapped through; A stack of hidden fields depicting the entire object map? Just seems odd.
GONeale
Also in re. of "round-trip", unfortunately I would challenge that as well, I don't think one should have to soely guarantee any data you wish to update to your DB model, should exist entirely on the form. I don't think I have one db table which could facilitate this, consider a "creation date" for example, or "updated date", I don't think it would be ideal to store this in a hidden field on the form.
GONeale
PS. I respect your opinions and I'm glad you are taking the time to write back :) I am just debating this through to understand where we stand with MVC.
GONeale
GONeale, see my new answer below.
Robert Harvey
If I have an `Employee` details form that I want to present on the web for employees to be able to update themselves, such as for changing their emergency contact information, phone number, etc., I definitely do _not_ want them to be able to update their Salary. It should be entirely possible to update only part of such a record, and not require the entire thing to be passed around in HTTP Posts.
Funka
+12  A: 

The behavior you're experiencing with UpdateModel() sounds like you're list binding, in which case UpdateModel() will blow away the contents of the list and repopulate it. See Hanselman's blog for a discussion on this. If you're updating a single object, UpdateModel() will update that individual object, leaving properties that don't have a corresponding form value as-is.

Many of these problems boil down to that UpdateModel() is really meant to repopulate view models - not domain models - based on form input. (I'm slightly simplifying things by saying that a view model is just a contract between a controller and the view, while your domain model might be a LINQ2SQL or EF model object.) All of the MVC tutorials and demos show UpdateModel() being used against database objects, which I feel is unfortunate since it's somewhat misleading as to the intended purpose of model binding. Robert's post is more indicative of the actual intent of UpdateModel().

Levi
+1 excellent answer.
womp
Ah excellent, just the type of answer I was after. Thanks for clearing this up somewhat, and I would agree most tutorials and demos lead you to believe UpdateModel is the "tool to use" for updating your DB model, I guess not so much - so is there something for us? Or I wonder if there will be in the future. I believe I've seen something in EF to take a model for updating, perhaps EF could be a better suited ORM over L2S for MVC.. I will also check out this article thanks.
GONeale
I have read the article, Thanks again Levi (Senior MVC dev?), although it doesn't clear up my specific requirement, it does explain how the model binding works quite neatly.
GONeale
I just got like the biggest aha experience.
Martin
+2  A: 

I use UpdateModel quite happily for non-list types. I'm always careful to specify the includeProperties array (not because of the potential for this problem, but for security -- do you want the user to be able to hack the form (very easy) and submit dates, etc?).

That's not to say it can't be improved further.

Also, a practical point to keep in mind when setting the requirements: to a webserver receiving a POST, an empty field is the same as a non-existent field. That means that if UpdateModel were designed such that it did not "reset" non-existent form fields (such as date), the same behavior would mean that if the user removes the text in your date field and posts, it would not get updated with empty (or null).

james

James S
True, but they could detect if the form element exists in the array, and if it's empty, that could make the distinction.
GONeale
+2  A: 

But I still struggle with the idea of representing my entire object model in a form, especially if say I had 2 child objects and a couple of lists, I'm not sure how this would easily be mapped through; A stack of hidden fields depicting the entire object map? Just seems odd.

For this, you need to look into things like SubControllers and RenderAction. You can Google those. I use RenderAction a lot. It allows me to inject a widget into a page from its own controller method, without having to push separate data into the ViewData.

I don't think one should have to soely guarantee any data you wish to update to your DB model, should exist entirely on the form. I don't think I have one db table which could facilitate this, consider a "creation date" for example, or "updated date", I don't think it would be ideal to store this in a hidden field on the form.

You are right about that. Things like CreationDate, UpdatedBy should be handled in the Controller (actually the repository, if you are using repositories). By that time, you should already have all of the fields you need from the view model to update your database.

You may need to use "strongly-typed view model" objects. If you are not, or are not sure, review this page: http://nerddinnerbook.s3.amazonaws.com/Part6.htm

Robert Harvey
Thanks Rob. I am using strongly-typed view model objects already to customise and include multiple object sets. No problem and thanks for all your help, I would assume NerdDinner would have the same problem as they use FormCollection and UpdateModel, perhaps the model's aren't complex enough. I'll have to do some investigating.
GONeale
I'll have to respectfully disagree. Just create a DTO ViewModel and map your data back to your Domain Objects in the controller (if large, in a separate mapper class).
Jess
@LuckyLindy -- That is one way to do it, but it is endlessly repetitive if you have a single bit of functionality that you want to repeat in many views.
Robert Harvey