First time here? Check out the site's "greatest hits" or read a post from the archives. Feel free to leave a comment or ask a question, and consider subscribing to the latest posts via RSS or e-mail. Thanks for visiting!
« The Weekly Source Code 19 - LINQ and Mor... | Main | List of .NET Dependency Injection Contai... »

cavemen-unfrozen-lawyer-web Any problem in computer science can be solved with one additional layer of indirection. But that usually will create another problem. - David Wheeler

Any sufficiently advanced technology is indistinguishable from magic. - Arthur C. Clarke.

These are two classic truisms. Recently while staring at some code trying to figure out what the heck was going on, I realized the obvious.

One additional layer of indirection is indistinguishable from magic. - Scott Hanselman, this morning in a rare moment of clarity while hopped up on Diet Coke.

In recent talk at Mix on ASP.NET MVC (there's video there also) I mentioned that a certain line of code was magic:

      public void Update(int id)
      {
         try
         {
            viewData.Product = db.Products.Single(p => p.ProductID == id);
            //MAGIC STARTS HERE
Binding.UpdateFrom(viewData.Product, Request.Form);
//END MAGIC db.SubmitChanges(); RedirectToAction("List"); } catch (InvalidOperationException err) { viewData.ErrorMessage = err.Message; RenderView("edit", viewData); } }

Why did it feel like magic? A few reasons.

  • It does a LOT. It takes all the values from a Form POST and lines them up with the public Properties in an object instance. This is done in the context of a Form POST to a Controller Action in ASP.NET MVC.
  • It isn't named well. Update is a verb, so that's cool, but the "From" makes me feel inverted.
  • The parameters are (possibly) in the wrong order. Given the name, I'd have expected UpdateFrom(Form, Product), but even then it doesn't feel write.

All of this adds up to an impedance mismatch, IMHO. It's too confusing and I'm just a caveman (video). As such, I declare it as black magic.

This magic was brought up to the team (I think I remember hitting someone, there may have been some swearing. ;) ) and Rob changed it in a number of good ways.

  • It's discoverable. He hung it off of the Request where you can actually stumble upon in.
  • It's named directly. Rather than the inverted UpdateFrom, it is now DeserializeTo which reads better left to right as in Request.DeserializeTo(product).
  • It's simpler. Because it's hanging off the object that was previously a parameter, it has one less parameter and again, reads nicely left to right.
//FEELS LESS MAGICAL 
Request.DeserializeTo(viewData.Product);

Jeff Moser's (see I told you I'd mention him twice) fine post talks about how cognitive "chunks" that represent solutions to problems can be more easily stored in our brains if they flow. Scott Bellware has called this concept "solubility." Code that uses frameworks with some balanced between focus on aesthetic and usability is more easily grokked.

I don't know if this is the final method, it's probably not, but I really enjoy discussions like this that aim to make things less like magic and more obvious and discoverable.



Thursday, March 13, 2008 1:00:15 PM (Pacific Standard Time, UTC-08:00)
That partial quote is really annoying - but less annoying to propagate it in its partial form, which takes it out of context.

"Any problem in computer science can be solved with another layer of indirection, but that usually will create another problem." - David Wheeler

Layers of indirection aren't cures. They invite thinking about problems from a different angle by creating new ways of expressing it.

Offering just the first part of the quote suggests that layers of indirection are a solution. That's not only a contradiction of the quote, but a statement that should be self-evidently inconsistent with experience. If you offer this quote in its incomplete form to folks who can't measure it against experience due to a lack of experience, you're likely causing more harm than good, and putting arbitrary obstructions in the way of their learning.
Thursday, March 13, 2008 1:04:32 PM (Pacific Standard Time, UTC-08:00)
I like that much better. Now I think I need a function that hangs off of the object to copy properties to another object so I could do this:

var custBusinessObject = CustomerModel.GetCustomerById(custID);
custBusinessObject.CopyTo(CustomerViewData);

That could be handy for not exposing too much to the views. But I keep on wondering if it matters whether the views see all of the business model data or not. I mean that's their purpose right? But by not giving them the full objects it explicitly disallows the views from updating the model. What do you think?
Thursday, March 13, 2008 1:04:55 PM (Pacific Standard Time, UTC-08:00)
I like your post, as well as your MIX08 MVC presentation and the direction that this MVC offering is heading.

How about:

Request.ApplyFormDataTo(viewData.Product)


To my knowledge this extension method takes the form data and updates the product with whatever values map to form field names.

Or perhaps:

Request.DeserializeInto(viewData.Product)


Note use of the "Into" as opposed to "To". I feel this better indicates that the form will be deserialized, then those form values that are mappable or resolvable with respect to the product will be dumped into the product, effectively overwriting some but not all of the values. We're not creating a new product here, we're updating some of an existing one, right?

Or even:

Request.ApplyFormValuesTo(viewData.Product)


All in the name of grokkability.
David
Thursday, March 13, 2008 1:44:25 PM (Pacific Standard Time, UTC-08:00)
@David: I guess 'form' shouldn't be a part of the name since requests could come from the XmlHttpRequest object in Ajax scenarios.
Thursday, March 13, 2008 2:15:15 PM (Pacific Standard Time, UTC-08:00)
Hey Scott, ever since I heard you say this at Boise Code Camp I've been struggling to internalize it. I think Scott Bellware's comment captures the essence of my question, but I'll ask it a bit differently. (I'm seriously not trolling here, this has been rolling around my head all week.)

Is indirection the same thing as abstraction? And, how does that relate to Ockham's razor, which is essentially "all things being equal, the simplest solution is best"?

Sometimes I think we get "abstraction crazy" and think every piece of our app should be replaceable. So we end up with more "frameworks" than "libraries" and we are too often programming against the lowest common denominator.

Michael Cote has a great article talking about this in the Java world.
Thursday, March 13, 2008 2:22:50 PM (Pacific Standard Time, UTC-08:00)
DeserializeTo sounds poorly named based on your own description as to what the method does.

David's suggestion of ApplyFormDataTo is much closer to what I would be looking for. I would probably shorten it to ApplyTo.

Another option would be to use CopyTo as that's really what it sounds like you're doing there -- copying the values from the request to the data object. But, I tend to associate CopyTo to working with collections so I much rather prefer something like ApplyTo.
Thursday, March 13, 2008 2:37:18 PM (Pacific Standard Time, UTC-08:00)
>>cognitive "chunks" that represent solutions to problems can be more easily stored in our brains if they flow...>>

I'll never forget \rnc (Range Name Create) menu shortcut (and plenty of others) from Lotus 123 in the late 80's.
Mark
Thursday, March 13, 2008 3:08:11 PM (Pacific Standard Time, UTC-08:00)
I like CopyTo, especially because it doesn't imply merge logic or funkiness.
Thursday, March 13, 2008 4:40:59 PM (Pacific Standard Time, UTC-08:00)
How about

Request.TakePostedValuesAndSetPropertiesOfTheObjectWithTheSameNameToThePostedValueUsingReflection(product);

???
Thursday, March 13, 2008 5:01:22 PM (Pacific Standard Time, UTC-08:00)
Phil, scratch my previous suggestion. That's perfect! ;-)
Thursday, March 13, 2008 5:27:39 PM (Pacific Standard Time, UTC-08:00)
Thanks for correcting the quote ScottB, I am used to the partial one and I agree it's taken out of context without the kicker ending..

I like ApplyTo and CopyTo, but technically Deserialize is also correct. That's a tough call. I would say that if the source were XML rather than name/value pairs we wouldn't object to the term deserialize. What do you think?

As for the question about Indirection vs. Abstraction, I think they are pretty darn different, but often they are used incorrectly as homonyms in colloquial speech. Indirection is use to reduce coupling between classes, while abstraction hides complexity (presumably making things simple, but not magic.)
Thursday, March 13, 2008 5:29:33 PM (Pacific Standard Time, UTC-08:00)
@Phil:

- Sorry to break up the flow here but I love the "Sho Nuff" avatar! LOL

Sincerely,

Kevin AKA "Bruce Leroy"
Thursday, March 13, 2008 8:26:08 PM (Pacific Standard Time, UTC-08:00)
How about:

viewData.Product.UpdateFrom(Request.Form)

via an extension method?

static public void UpdateFrom<T>(this T obj, HtmlForm form);

Or:

Binding.Update(viewData.Product).From(Request.Form);

Where Binding.Update(T) returns a generic type that has a From(HtmlForm form) method?

Binding.cs:
Updateable<T> Binding.Update(T obj);

Updateable.cs:
Updateable<T> From<T>(T obj);

Just a couple crazy ideas.
Joe Chung
Thursday, March 13, 2008 9:51:41 PM (Pacific Standard Time, UTC-08:00)
I just loved the line:
"but even then it doesn't feel write"
That was just so subtly "slipped" in, yet so aptly applied. Bravo!
Dan Gilleland
Friday, March 14, 2008 12:24:06 AM (Pacific Standard Time, UTC-08:00)
I don't like any of them!

I was using this automatic databinding stuff in MonoRail. At first I missed it when I moved to the new MS WebEx stuff but I soon got used to stuffing information into ViewData using LINQ and anonymouse types I started to prefer it.

01: Someone else writes my views. If I specifically fill the view data with a subset of data I can not only prevent them from accidentally showing sensitive information but I can also have the structure of my view totally independant from my business objects' structure.

02: The opposite of 1. I can take post data in any format and post it to multiple objects, the data I accept might not even go directly into properties. E.g. a text box asking how old someone is today might change only the year part of Person.DateOfBirth, or their full name might get split up into givenname/familyname etc.

Personally I keep my UI completely separate from my business objects. You will never find a business object in my viewdata. The view is there to allow the user to enter information about a task they are performing, not edit objects as if they were rows in a table!
Peter Morris
Friday, March 14, 2008 12:52:56 AM (Pacific Standard Time, UTC-08:00)
The problem with Request.DeserializeTo is that we don’t know if it will take the values from the QueryString or the Form. In this case it will probably take from both. But we probably want a solution where we can decide if it should take the values from the QueryString or the Form. So in this case something like this need to be added:

Request.Form.DeserializeTo

Reuqst.QueryString.DeserializeTo

The nice thing with this solution is that we need to extend the NameValueCollection type or NameObjectCollectionBase. In that case we have a nice extension method that we can use to map a key/value pair to an object, isn’t that cool ;)

I’m not a fan about the name Deserialize in this context based on how the data structure looks like, but out from a “less magic” perspective I like the name. I like the word ApplyTo, even if name ApplyTo will probably be more magic than DeserializeTo, in my opinion.
Friday, March 14, 2008 5:36:33 AM (Pacific Standard Time, UTC-08:00)
I mentioned to Rob at the Mix talk that:

Binding.UpdateFrom(viewData.Product, Request.Form);

is vulnerable to an injection attack if your target object (viewData.Product) has more properties than the form you coded is exposing. This is an important point to consider when designing this API and reinforce when showing/explaining it to developers out there. All input is evil and helper APIs like this that hide details are sometimes also evil ;)
Brock
Friday, March 14, 2008 5:38:43 AM (Pacific Standard Time, UTC-08:00)
Still looks like magic to me. Just a bit whiter.
Mike Woodhouse
Friday, March 14, 2008 6:10:05 AM (Pacific Standard Time, UTC-08:00)
My personal favorite version of Clarke's 3rd law comes from Dilbert creator Scott Adams: "In my house, any sufficiently advanced technology is broken, and no one knows how to fix it."

Currently the pain point in my own house is the Oregon Scientific weather station I was given for Xmas.
greg
Friday, March 14, 2008 5:48:59 PM (Pacific Standard Time, UTC-08:00)
I like the change, but surely Request.Form.DeSerializeTo([type]) makes more sense?

Brock you make a good point, but usually when we're accepting values from a form we take care to catch naughty input at other points? We have the option of validation logic prior to processing the form as well as a second validation before persisting to a data store. To my mind it's just a nice helper class. Liking it.
Monday, March 17, 2008 12:04:45 PM (Pacific Standard Time, UTC-08:00)
We've used "Scatter" and "Gather" as method names that do similar work in WebForms.

Scatter takes a class instance, and matches property names with form IDs, pushing data from the class instance to the controls.

Gather does the opposite - takes values from controls (or Request.Form in this case) and pushes them to a class instance.

My old boss did this in Clipper a couple decades ago, and we used to do it in classic ASP, matching Request.Form with stored procedure parameter names. I think it's a brilliant and powerful idea.

Programmers are information plumbers, moving bits from point DB to point USER, and back again to DB.
Todd Price
OpenID
Please login with either your OpenID above, or your details below.
Name
E-mail
(will show your gravatar icon)
Home page

Comment (Some html is allowed: a@href@title, b, blockquote@cite, em, i, pre, strike, strong, sub, super, u) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Live Comment Preview

Contact

Sponsors

On this page...

Tags

Calendar

<May 2008>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

Archives

Google Ads