Model, View Model, View – No way (for now)…

I get all the Model <—> ViewModel <—> View stuff.  It’s great theory, but come on! A View Model class that has the same exact properties as the stinkin’ Model that it is wrapping, just so we can say “I used MVVM”.

My little Asp.Net CRUD app is binding the UI controls directly the data properties on a BusinessObject.Entity, where the Entity is a Typed class from a LinqToSql query. (My BO’s are based on the West Wind wwLinqToSql Business Object Wrapper from the West Wind Web Tools for Asp.Net ).  And guess, what… It works! It loads and saves data when the user clicks the buttons.  Imagine that. I’ll be darned if I’m gonna wrap a ViewModel class around that data Entity so I can bind my UI controls to that ViewModel, rather than hooking straight to the Entity properties.  Just too much stinkin’ code required.

I’ve also added a few additionally needed "presentation properties" into my Business Objects to *supplement* the Entity properties that came from the data base row. And, yes, when working with my BO’s, you have to access some properties right off the BO (the custom presentation properties I added) and some come from the main BO.Entity (the real data from the database row). So, you do have to know where to get the data you are after. By doing this, most of my UI binding is against the BusinessObject.Entity properties, and a few UI bindings go against the supplemental presentation properties I’ve added right on the BO.

So, I don’t mind some bit of a hybrid ViewModel approach, but not a separate class where I have to re-host every single property that is on the Entity. And, since this is done on the Business Object class level, all these supplemental  "presentation properties" can still be accessed by other UI apps that work with the same Business Object wrapper classes.

From a theoretical standpoint, sure, it breaks a few rules. But from a real world impact, we are talking about only a handful of supplemental properties. In all the BOs I’ve built (Customer, Invoice, Quote, Shipper, User, Employee, etc.), maybe I’ve added 3 to 5 additional properties on each one to serve what is usually one view, but sometimes two or three different views at the most. If things really get crazy, there might be as many as 10 presentation properties to handle all the views. It still seems like less of an overall burden on the application as a whole to add a few presentation properties right on the BO, rather than dropping an entire Model-to-ViewModel Mapper implementation on my app (homegrown or commercial). Talk about a burden.

Now look, these additional properties will be implemented somewhere. Why not stick them onto something that already exists (the BO) as little light-weight tag-a-longs, rather than create an entirely free-standing ViewModel object, where I put these innocent properties and then I have to REPLICATE all the real properties from the Model too. Is that how this magic ViewModel character makes things "cleaner"? Yuck! Oh, wait, I almost forgot… but look how clean my BO is! No thanks.

Here’s a use case from my app… I want to bind a UI control to the Customer Name for an Invoice Object that I have pulled from the database. Well, rather than attempting to bind through the main Invoice Entity and into the Customer sub-Entity (which LinqToSql handles nicely for me BTW) I’ve added a presentation property on the BusinessObject to access the Customer Name easily, and also guard against several null object cases that can arise.

Example: Attempting to bind a UI control to directly to this property (see below) could crash the UI generation if the Entity object or Customer Entity is null:

busInvoice.Entity.CustomerEntity.Name

Since it’s possible that the busInvoice object’s .Entity property is not even saturated yet, or that the CustomerEntity is null, this could cause problems generating the UI.  And, I certainly don’t want to test for all the potential problems in my UI pages every time I want to display that property. So, I added a property on the busInvoice object to host that value and deal will all the required testing.

Something like:

public string CustomerName
{
get
   {
     if (this.Entity != null && this.Entity.CustomerEntity != null)
       return this.Entity.CustomerEntity.Name;
     else
       return "";
   }
}

Anyway, back to my main rant about going overboard with ViewModels…

So look at this sample I found from a notable Asp.Net guru.  It is a ProductView class that returns a ProductView object from a passed in Product (that you got from your DAL somewhere), and it can return a Product object from a passed in ProductView object, basically by unwrapping the ProductView object and casting the string properties back to the original data types. Look at all this code! Especially look at the ToProduct() method. Wow!  ( code from http://weblogs.asp.net/craigshoemaker/archive/2009/11/03/vm-workshop-model-view-viewmodel-mvvm-and-the-presentation-model-pattern-in-5-ui-platforms.aspx )

It looks like a maintenance nightmare.  I need more convincing that this is the way the pros do it. Perhaps I’ll be a rookie just a little bit longer.

    public class ProductEditView : IUrlSerializer
    {
        public string ProductId { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public string QuantityOnHand { get; set; }
        public string Price { get; set; }
        public string ReleaseDate { get; set; }

        public string SerializeToUrlString()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("productId={0}&", this.ProductId);
            sb.AppendFormat("title={0}&", this.Title);
            sb.AppendFormat("description={0}&", this.Description);
            sb.AppendFormat("quantityonhand={0}&", this.QuantityOnHand);
            sb.AppendFormat("price={0}&", this.Price);
            sb.AppendFormat("releasedate={0}", this.ReleaseDate);
            return sb.ToString();
        }

        public static ProductEditView ToProductEditView(Product product)
        {
            ProductEditView view = new ProductEditView();
            view.ProductId = product.ProductId.ToString();
            view.Title = product.Title;
            view.Description = product.Description;
            view.Price = string.Format("{0:c}",product.Price);
            view.QuantityOnHand = product.QuantityOnHand.ToString();
            view.ReleaseDate = product.ReleaseDate.ToShortDateString();
            return view;
        }
        public static Product ToProduct(ProductEditView view)
        {
            double db;
            int num;
            DateTime dt;

            Product product = new Product();
            product.Title = view.Title;
            product.Description = view.Description;

            if (double.TryParse(view.Price.TrimStart(‘$’), out db))
            {
                product.Price = db;
            }
            else
            {
                throw new ArgumentException("Price is malformed or not a valid value.");
            }

            if (int.TryParse(view.ProductId, out num))
            {
                product.ProductId = num;
            }
            else
            {
                throw new ArgumentException("ProductId is malformed or not a valid value.");
            }

            if (int.TryParse(view.QuantityOnHand, out num))
            {
                product.QuantityOnHand = num;
            }
            else
            {
                throw new ArgumentException("QuantityOnHand is malformed or not a valid value.");
            }

            product.ProductId = Convert.ToInt32(view.ProductId);
            product.QuantityOnHand = Convert.ToInt32(view.QuantityOnHand);

            product.ReleaseDate = Convert.ToDateTime(view.ReleaseDate);
            if (DateTime.TryParse(view.ReleaseDate, out dt))
            {
                product.ReleaseDate = dt;
            }
            else
            {
                throw new ArgumentException("ReleaseDate is malformed or not a valid value.");
            }

            return product;
        }

4 thoughts on “Model, View Model, View – No way (for now)…”

  1. Hey Matt,

    Maybe I haven't done enough reading lately, but this is the first time I've run across the term "saturated" used in this manner:

    "Since it's possible that the Invoice object Entity is not even saturated yet, or that the CustomerEntity is null this could cause problems."

    Used in this context, I'm assuming it means something along the lines of "instantiated and filled with data", but I'm just taking a SWAG. Could you enlighten me? ;0)

    As far as the MVVM pattern goes, my only experience with it so far has been research, I haven't actually done anything substantial with it, so I really don't have much of an opinion one way or the other yet.

  2. Matt, I agree it may seem a bit much but consider OOP and n-tier architecture. You wouldn't use the same CRUD approach in a VFP n-tier application so you shouldn't expect to use it here either, right?

  3. Matt, I too am trying to learn the MVVM pattern and from what I've learnt thus far, the pros seems to think that it is a perfect fit for WPF and Silverlight applications. Now I have to take their word on this because I haven't actually tried to cut any code yet. The thing is, you and I are only just dipping our toes into the pattern and I'd have to think that the seasoned veterans know what they're doing and MVVM must be worth whatever pain is required.

  4. Matt, I believe there's a thin line between Model and the View where the Viewmodel actually resides. And some people out there think the VM should be closer to the Model (a view of the model) while others put it near the view (The Model of a view). Unfortunately… both are correct :).. but hey, isn't this a good thing, being free to get out of a pattern-box that limits your options?

    Although I agree with you that Viewmodel should not double the properties on the VM, there are cases when a property on the model must be transformed in something the user can chew better. Why asking a user to fill in a complete datetime field, when you can simulate something like the new Appointment feature in Outlook?

    Furthermore, binding directly to the model is not always sufficient. There are valuable pieces of information that aren't stored in the underlying tables rather than calculated based on other entities. A typic LOB example is the product stock for a warehousing system. For sure you don't maintain a Stock field in the Product table. How do you transport this kind of extra fields to the UI? As a sepparate entity. Good enough, but also a maintenance nightmare. From what you wrote above I'm tempted to think that you are willing to add these extra properties to the underlying model classes (and care for loading them in a generic way). Well, by doing this you just created a sort of a viewmodel…

    One last thing, which is currently more interresting for the WPF/SL world: Command binding. Another purpose for the viewmodel is to host the counterparts for the actions users perform on the UI – command implementations.

Leave a Reply

Your email address will not be published. Required fields are marked *