asp.netasp.net-mvc-3html-helpermodelmetadata

ASP.NET MVC 3 HtmlHelper Exception does not recognize ModelMetadata on inherited interface


After upgrading to MVC 3 RTM I get an exception where it previously worked.

Here is the scenario. I have several objects that use the same underlying interfaces IActivity and IOwned.

IActivity implements IOwned (another interface)

public interface IActivity:IOwned {...}

public interface IOwned 
{
    int? AuthorId {get;set;}
}

I have a partial view that uses IActivity for reuse from other concrete partials.

Here is the definition of the Activity Partial.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IActivity>" %>
<%: Html.HiddenFor(item => item.AuthorId) %>

However, it throws an exception. It can not find AuthorId in the ModelMetadata.

I am guessing that in the previous version it looked at the interfaces IActivity implemented.

Any ideas, suggestions, short of duplicating similar interfaces everywhere?

Copied the stack trace below.

[ArgumentException: The property IActivity.AuthorId could not be found.]
   System.Web.Mvc.AssociatedMetadataProvider.GetMetadataForProperty(Func`1 modelAccessor, Type containerType, String propertyName) +498313
   System.Web.Mvc.ModelMetadata.GetMetadataFromProvider(Func`1 modelAccessor, Type modelType, String propertyName, Type containerType) +101
   System.Web.Mvc.ModelMetadata.FromLambdaExpression(Expression`1 expression, ViewDataDictionary`1 viewData) +393
   System.Web.Mvc.Html.InputExtensions.HiddenFor(HtmlHelper`1 htmlHelper, Expression`1 expression, IDictionary`2 htmlAttributes) +57
   System.Web.Mvc.Html.InputExtensions.HiddenFor(HtmlHelper`1 htmlHelper, Expression`1 expression) +51
   ASP.views_shared_activity_ascx.__Render__control1(HtmlTextWriter __w, Control parameterContainer) in c:\Users\...\Documents\Visual Studio 2010\Projects\ngen\trunk\...\Views\Shared\Activity.ascx:3
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +109
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8
   System.Web.UI.Control.Render(HtmlTextWriter writer) +10
   System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +208
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8
   System.Web.UI.Page.Render(HtmlTextWriter writer) +29
   System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer) +43
   System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3060

Solution

  • There has been a breaking change/bug in ASP.NET MVC 3 in the System.Web.Mvc.ModelMetadata. FromLambdaExpression method which explains the exception you are getting:

    ASP.NET MVC 2.0:

    ...
    case ExpressionType.MemberAccess:
    {
        MemberExpression body = (MemberExpression) expression.Body;
        propertyName = (body.Member is PropertyInfo) ? body.Member.Name : null;
        containerType = body.Member.DeclaringType;
        flag = true;
        break;
    }
    ...
    

    ASP.NET MVC 3.0

    ...
    case ExpressionType.MemberAccess:
    {
        MemberExpression body = (MemberExpression) expression.Body;
        propertyName = (body.Member is PropertyInfo) ? body.Member.Name : null;
        containerType = body.Expression.Type;
        flag = true;
        break;
    }
    ...
    

    Notice how the containerType variable is assigned a different value. So in your case in ASP.NET MVC 2.0 it was assigned the value of IOwned which is the correct declaring type of the AuthorId property whereas in ASP.NET MVC 3.0 it is assigned to IActivity and later when the framework tries to find the property it crashes.

    That's the cause. As far as the resolution is concerned I would wait for some official statement from Microsoft. I can't find any relevant information about this in the Release Notes document. Is it a bug or some feature which needs to be workarounded here?

    For now you could either use the non-strongly typed Html.Hidden("AuthorId") helper or specify IOwned as type for your control (I know both suck).