Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC Display Template for strings is used for integers

I recently hit an issue with ASP.NET MVC display templates. Say this is my model:

public class Model
{
    public int ID { get; set; }
    public string Name { get; set; }
}

this is the controller:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new Model());
    }
}

and this is my view:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<DisplayTemplateWoes.Models.Model>" %>

<!DOCTYPE html>

<html>
<head runat="server">
    <title>Index</title>
</head>
<body>
    <div>
        <%: Html.DisplayForModel() %>
    </div>
</body>
</html>

If I need for some reason a display template for all strings I will create a String.ascx partial view like this:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<string>" %>

<%: Model %> (<%: Model.Length %>)

And here is the problem - at runtime the following exception is thrown: "The model item passed into the dictionary is of type 'System.Int32', but this dictionary requires a model item of type 'System.String'"

It seems that String.ascx is used for both the integer and string property of the Model class. I expected it to be used only for the string property - after all it is named String.ascx not Object.ascx or Int32.ascx.

Is this by design? If yes - is it documented somewhere? If not - can it be considered a bug?

like image 797
Atanas Korchev Avatar asked Feb 18 '11 07:02

Atanas Korchev


People also ask

What is MVC template used for?

When you create an ASP.NET MVC application in Visual Studio, you have the option to create controllers and views that support data scaffolding. This walkthrough shows how to create a simple MVC application that takes advantage of the data templates for controllers and views that Visual Studio supports for MVC.

What is display template MVC?

Display templates A DisplayTemplate is a Razor file placed in the DisplayTemplates folder: For Razor Pages apps, in the Pages/Shared/DisplayTemplates folder. For MVC apps, in the Views/Shared/DisplayTemplates folder or the Views/ControllerName/DisplayTemplates folder.


1 Answers

This seem to be by design. You will have to make string template more general. String template works as default template for every non-complex model that doesn't have it's own template.

Default template for string (FormattedModelValue is object):

internal static string StringTemplate(HtmlHelper html) {
    return html.Encode(html.ViewContext.ViewData.TemplateInfo.FormattedModelValue);
}

Template selection looks like this:

foreach (string templateHint in templateHints.Where(s => !String.IsNullOrEmpty(s))) {
    yield return templateHint;
}

// We don't want to search for Nullable<T>, we want to search for T (which should handle both T and Nullable<T>)
Type fieldType = Nullable.GetUnderlyingType(metadata.RealModelType) ?? metadata.RealModelType;

// TODO: Make better string names for generic types
yield return fieldType.Name;

if (!metadata.IsComplexType) {
    yield return "String";
}
else if (fieldType.IsInterface) {
    if (typeof(IEnumerable).IsAssignableFrom(fieldType)) {
        yield return "Collection";
    }

    yield return "Object";
}
else {
    bool isEnumerable = typeof(IEnumerable).IsAssignableFrom(fieldType);

    while (true) {
        fieldType = fieldType.BaseType;
        if (fieldType == null)
            break;

        if (isEnumerable && fieldType == typeof(Object)) {
            yield return "Collection";
        }

        yield return fieldType.Name;
    }
}

So if you want to create template only for string, you should do it like this (String.ascx):

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<object>" %>

<% var model = Model as string; %>
<% if (model != null) { %>
    <%: model %> (<%: model.Length %>)
<% } else { %>
    <%: Model %>
<% } %>
like image 182
Lukáš Novotný Avatar answered Oct 14 '22 10:10

Lukáš Novotný