Scott Hanselman

I'm Just a Caveman - The Hanselman Corollary to the Clarke/Wheeler Laws

March 13, '08 Comments [21] Posted in ASP.NET | ASP.NET MVC | DevCenter | Learning .NET | Programming
Sponsored By

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.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Thursday, March 13, 2008 9:00:15 PM UTC
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 9:04:32 PM UTC
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 9:04:55 PM UTC
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 9:44:25 PM UTC
@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 10:15:15 PM UTC
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 10:22:50 PM UTC
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 10:37:18 PM UTC
>>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 11:08:11 PM UTC
I like CopyTo, especially because it doesn't imply merge logic or funkiness.
Friday, March 14, 2008 12:40:59 AM UTC
How about

Request.TakePostedValuesAndSetPropertiesOfTheObjectWithTheSameNameToThePostedValueUsingReflection(product);

???
Friday, March 14, 2008 1:01:22 AM UTC
Phil, scratch my previous suggestion. That's perfect! ;-)
Friday, March 14, 2008 1:27:39 AM UTC
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.)
Friday, March 14, 2008 1:29:33 AM UTC
@Phil:

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

Sincerely,

Kevin AKA "Bruce Leroy"
Friday, March 14, 2008 4:26:08 AM UTC
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
Friday, March 14, 2008 5:51:41 AM UTC
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 8:24:06 AM UTC
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 8:52:56 AM UTC
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 1:36:33 PM UTC
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 1:38:43 PM UTC
Still looks like magic to me. Just a bit whiter.
Mike Woodhouse
Friday, March 14, 2008 2:10:05 PM UTC
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
Saturday, March 15, 2008 1:48:59 AM UTC
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 8:04:45 PM UTC
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
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.