Tuesday, February 9, 2010 5:06 AM Kazi Manzur Rashid

ASP.NET MVC Complex Object ModelMetaData Issue

One of the exciting feature of ASP.NET MVC is(?) auto generating the view of the model with DisplayForModel/EditorForModel statement, it gives a nice extension point for the application developers to customize the view. But in the latest release (MVC2 RC2), it looks like the feature is no longer available. No, those methods are still available, but they are not working the same way as they were doing in the earlier versions. In rc2, if your model has any complex object, it will not render it . I know some of you like myself thought, one of the major changes in rc2 is the Input Validation vs. Model Validation in ASP.NET MVC and rest of it is bug fixing and some improvements. I did check the release note doc, where it is mentioned as

Templated helpers such as Html.EditorFor and Html.DisplayFor show only simple properties by default. If you need to show complex properties, you can create a custom template to show any set of properties.

Check the last line, it says to show the complex properties we have to create a custom template. But interestingly enough it does not work. Let me show you a very simple example, lets say that we have product and category class like the following:

public class Category
{
    public int Id { get; set; }

    public string Name { get; set; }
}

public class Product
{
    public int Id { get; set; }

    public string Name { get; set; }

    public decimal Price { get; set; }

    [UIHint("CategoryTemplate")]
    public Category Category { get; set; }
}

Check that we are using a custom template (CategoryTemplate) to show the category property.

And in the Controller:

public ActionResult Index()
{
    return View(new Product
                 {
                     Id = 1,
                     Name = "Product 1",
                     Price = 10.0m,
                     Category = new Category { Id = 2, Name = "Category 2" }
                 });
}

And in the View we want to render the product:

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<ComplexObject.Models.Product>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Home Page
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <p>
        <%= Html.DisplayForModel() %>
    </p>
    <p>
        To learn more about ASP.NET MVC visit <a href="http://asp.net/mvc" title="ASP.NET MVC Website">http://asp.net/mvc</a>.
    </p>
</asp:Content>

But when the view is rendered, there is no category:

CO1

I posted this issue in the Gu’s RC2 announcement post as well as in the CodePlex, but the answer that I got is somewhat confusing. I do not think this is a right decision to hide the complex object in the default implementation, I never hard anybody is having some issues with this features and I do not think it is the right time for this change as the MVC2 is in in RC, anyway feature is broken and needs to be resolved in the first place.

So, till there is a fix, what is the workaround? Lets write some Haacky code. First, lets see where the ASP.NET MVC take the decision to show/hide the model property. If you open the DefaultDisplayTemplates.cs and DefaultEditorTemplates.cs under the Html folder of the ASP.NET MVC Source code, you will find both of it has a method called ShouldShow.

private static bool ShouldShow(ModelMetadata metadata, TemplateInfo templateInfo)
{
    return
        metadata.ShowForEdit
        && metadata.ModelType != typeof (EntityState)
        && !metadata.IsComplexType
        && !templateInfo.Visited(metadata);
}

The only problematic checking is the IsComplexType which in turns check whether the type can be converted from the string type :

public virtual bool IsComplexType
{
    get { return !(TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof (string))); }
}

So the workaround is either we override the ModelMetadata which is somewhat an overkill or we can create a TypeConverter for the complex object for which we have custom template to fool the ASP.NET MVC Framework. In this case it is the Category. So lets write a converter.

public class CategoryConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) ? true : base.CanConvertFrom(context, sourceType);
    }
}

Now, decorate the Category with this converter:

[TypeConverter(typeof(CategoryConverter))]
public class Category
{
    public int Id { get; set; }

    public string Name { get; set; }
}

when you run the project you will find the category is shown like the following:

CO2

And that’s it. You can download the complete code from the following link.

Source Code: ComplexObject.zip

Shout it
Filed under: , , ,

Comments

# re: ASP.NET MVC Complex Object ModelMetaData Issue

Monday, February 8, 2010 7:59 PM by Martin Aatmaa

Hi Kazi,

Thank you for posting the details on this.

Are there any negative consequences of attributing your Category class with the custom TypeConverter (CategoryConverter)?

# re: ASP.NET MVC Complex Object ModelMetaData Issue

Tuesday, February 9, 2010 1:26 AM by msony

Yeah, i got this as well, thats why i needed to use my own template instead of EditorForModel

# re: ASP.NET MVC Complex Object ModelMetaData Issue

Tuesday, February 9, 2010 11:11 AM by bradwils

The documentation is correct. You made the custom template for the wrong object. :) You need to make a custom template for Product, because it's Product that has the thing inside of it that normally wouldn't be shown.

# re: ASP.NET MVC Complex Object ModelMetaData Issue

Tuesday, February 9, 2010 12:20 PM by Eric

Can you give an example of where this would be used?

I can't see how dumping a raw C# business object to a page would ever be useful in a web application outside of maybe debugging?

# re: ASP.NET MVC Complex Object ModelMetaData Issue

Tuesday, February 9, 2010 2:23 PM by Arnis L.

Isn't it easier to flatten your viewmodel?

# re: ASP.NET MVC Complex Object ModelMetaData Issue

Tuesday, February 9, 2010 7:30 PM by Kazi Manzur Rashid

@Arnis L: Flattern does not help in complex view, for ex try to create an order entry form of Northwind database.

If you have hierarchical object model you can have to create template for the root object and as per the mvc team the child cannot have its own template which is some what unacceptable.

# re: ASP.NET MVC Complex Object ModelMetaData Issue

Tuesday, February 16, 2010 10:41 AM by felipe_csl

Thanks for sharing. I agree that it is an error to ignore complex objects in the default EditorForModel implementation.

Felipe

# re: ASP.NET MVC Complex Object ModelMetaData Issue

Monday, February 22, 2010 5:36 AM by ahsanm.m

thanks for sharing, Would you please give me a guide line to understand it clearly.

I am newbie in MVC architecture.

Thanks