Binding conflict between a property named Title in my Model and View.Title in my View (in MVC)

前端 未结 4 925
野性不改
野性不改 2021-01-05 01:50

My Model contains a property named Title, and in my Create view I set the page title using ViewBag.Title.

This creates the fol

相关标签:
4条回答
  • 2021-01-05 02:20

    I found partial solution myself.

    Just use:

        @Html.EditorForModel()
    

    instead of:

        @foreach (var property in Model.GetMetadata().Properties)
        {
                <div class="editor-label">
                    @Html.Label(property.PropertyName)
                </div>
                <div class="editor-field">
                    @Html.Editor(property.PropertyName) 
                    @Html.ValidationMessage(property.PropertyName)
                </div>
    
        }
    

    Html.EditorForModel() method return same results, but without described problem.

    0 讨论(0)
  • 2021-01-05 02:30

    As suggested by the other answers, using EditorFor instead of Editor seems to work around the problem. However, using EditorFor requires knowledge of the model type and property type at compile-time, which isn't the case for Object.cshtml.

    You can still do this by building up and calling the correct generically-constructed EditorFor method using reflection. The code to do this is really messy, so here are some re-usable extension methods to do it for you.

    Use them like this in Object.cshtml where prop is an instance of ModelMetadata like in the question:

    @Html.DisplayFor(prop)
    @Html.LabelFor(prop)
    @Html.EditorFor(prop)
    @Html.ValidationMessageFor(prop)
    

    Here are the extension methods:

    using System;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Web.Mvc;
    using System.Web.Mvc.Html;
    using System.Web.Routing;
    
    namespace ASP
    {
        public static class NonStronglyTypedStronglyTypedHtmlHelpers
        {
            public static MvcHtmlString DisplayFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop)
            {
                return StronglyTypedHelper(html, h => h.DisplayFor, prop);
            }
    
            public static MvcHtmlString EditorFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop)
            {
                return StronglyTypedHelper(html, h => h.EditorFor, prop);
            }
    
            public static MvcHtmlString LabelFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop)
            {
                return StronglyTypedHelper(html, h => h.LabelFor, prop);
            }
    
            public static MvcHtmlString ValidationMessageFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop)
            {
                return StronglyTypedHelper(html, h => h.ValidationMessageFor, prop);
            }
    
            private static MvcHtmlString StronglyTypedHelper(HtmlHelper html, Func<HtmlHelper<object>, GenericHelper<object>> accessMethod, ModelMetadata prop)
            {
                var constructedMethod = MakeStronglyTypedHelper(html, accessMethod, prop);
                var genericPropertyExpression = MakePropertyExpression(prop);
                var typedHtmlHelper = MakeStronglyTypedHtmlHelper(html, prop.ContainerType);
    
                return (MvcHtmlString)constructedMethod.Invoke(null, new object[] { typedHtmlHelper, genericPropertyExpression });
            }
    
            private static MethodInfo MakeStronglyTypedHelper(HtmlHelper html, Func<HtmlHelper<object>, GenericHelper<object>> accessMethod, ModelMetadata prop)
            {
                var objectTypeHelper = new HtmlHelper<object>(html.ViewContext, html.ViewDataContainer, html.RouteCollection);
                var runMethod = accessMethod(objectTypeHelper);
                var constructedMehtod = runMethod.Method;
                var genericHelperDefinition = constructedMehtod.GetGenericMethodDefinition();
                return genericHelperDefinition.MakeGenericMethod(prop.ContainerType, prop.ModelType);
            }
    
            private static object MakeStronglyTypedHtmlHelper(HtmlHelper html, Type type)
            {
                var genericTypeDefinition = typeof(HtmlHelper<>);
                var constructedType = genericTypeDefinition.MakeGenericType(type);
                var constructor = constructedType.GetConstructor(new[] { typeof(ViewContext), typeof(IViewDataContainer), typeof(RouteCollection) });
                return constructor.Invoke(new object[] { html.ViewContext, html.ViewDataContainer, html.RouteCollection });
            }
    
            private static LambdaExpression MakePropertyExpression(ModelMetadata prop)
            {
                var propertyInfo = prop.ContainerType.GetProperty(prop.PropertyName);
                var expressionParameter = Expression.Parameter(prop.ContainerType);
                var propertyExpression = Expression.MakeMemberAccess(expressionParameter, propertyInfo);
                return Expression.Lambda(propertyExpression, expressionParameter);
            }
    
            private delegate MvcHtmlString GenericHelper<TModel>(Expression<Func<TModel, object>> expression);
        }
    }
    
    0 讨论(0)
  • 2021-01-05 02:33

    I would recommend using EditorFor instead of Editor.

    Html.EditorFor(x => x.Title)
    

    instead of:

    Html.Editor("Title")
    

    This way not only that the view takes advantage of your view model but it behaves as expected in this case.

    Example with ASP.NET MVC 3.0 RTM (Razor):

    Model:

    public class MyViewModel
    {
        public string Title { get; set; }
    }
    

    Controller:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Title = "ViewBag title";
            ViewData["Title"] = "ViewData title";
            var model = new MyViewModel
            {
                Title = "Model title"
            };
            return View(model);
        }
    }
    

    View:

    @model AppName.Models.MyViewModel
    @{
        ViewBag.Title = "Home Page";
    }
    
    @Html.EditorFor(x => x.Title)
    
    @{
        ViewBag.Title = "Some other title";
    }
    

    So no matter how much we try to abuse here the editor template uses the correct model title (which is not the case if we used Html.Editor("Title")).

    0 讨论(0)
  • 2021-01-05 02:38

    I solve same problem. Use this syntax instead Html.Editor

    @(Html.EditorFor(p => property.Model))
    
    0 讨论(0)
提交回复
热议问题