扩展 IHtmlHelper - 检查 MyEditorFor 中属性的 DataTypeAttribute

问题描述 投票:0回答:1

我正在制作自己的 Html.EditorFor 方法,我想在其中检查给定属性的 DataTypeAttribute。获取 ViewData.Model 的类型很容易。但是,如果该属性来自不同的类,就像在示例用法行中一样,除了将其容器类的类型作为参数传递之外,我找不到其他方法。

如何在省略containerType参数的情况下获取属性的DataTypeAttribute?

这是我当前的代码:

public static IHtmlContent MyEditorFor<TModel, TResult>(this IHtmlHelper<TModel> html,
    Expression<Func<TModel, TResult>> expression,
    object moreData,
    Type? containerType = null)
{
    var ep = html.ViewContext.HttpContext.RequestServices.GetService(typeof(ModelExpressionProvider)) as ModelExpressionProvider;
    var bindName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(ep.GetExpressionText(expression));

    if (containerType == null) containerType = html.ViewData.Model.GetType();
    var dataType = GetDataType(containerType, bindName);

    //etc...
}

private static DataType GetDataType(Type containerType, string bindName)
{
    var result = DataType.Text;
    var propName = bindName.Split('.')[^1];
    var prop = containerType.GetProperty(propName);
    var att = prop.GetCustomAttributes(typeof(DataTypeAttribute), true);
    var attProperty = typeof(DataTypeAttribute).GetProperty(nameof(DataTypeAttribute.DataType));
    result = (DataType)(attProperty.GetValue(att[0]) ?? DataType.Text);
    return result;
}

这是一个示例用法:

@Html.EditorForEx(x => x.LinkedTable[i].MyDate, new { placeholder = "Test Date" }, Model.LinkedTable[i].GetType())
  • 为了简单起见,省略了空检查
c# asp.net-core razor attributes
1个回答
0
投票

您应该像下面这样更改您的

HtmlHelperExtensions

using System;
using System.Linq.Expressions;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace _79271231.Helpers
{
    public static class HtmlHelperExtensions
    {
        public static IHtmlContent MyEditorFor<TModel, TResult>(
            this IHtmlHelper<TModel> html,
            Expression<Func<TModel, TResult>> expression,
            object moreData)
        {
            var expressionText = GetExpressionText(expression);
            var fullName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(expressionText);

            var (containerType, propertyInfo) = GetContainerTypeAndProperty(expression);

            var dataType = GetDataType(containerType, propertyInfo);

            switch (dataType)
            {
                case DataType.Date:
                    return html.TextBoxFor(expression, new { type = "date", @class = "form-control", placeholder = GetPlaceholder(moreData) });
                case DataType.MultilineText:
                    return html.TextAreaFor(expression, new { @class = "form-control", placeholder = GetPlaceholder(moreData) });
                default:
                    return html.TextBoxFor(expression, new { @class = "form-control", placeholder = GetPlaceholder(moreData) });
            }
        }

        private static string GetExpressionText<TModel, TResult>(Expression<Func<TModel, TResult>> expression)
        {
            var memberNames = new System.Collections.Generic.List<string>();
            Expression expr = expression.Body;

            while (expr is MemberExpression memberExpr)
            {
                memberNames.Insert(0, memberExpr.Member.Name);
                expr = memberExpr.Expression;
            }

            if (expr is MethodCallExpression methodCallExpr && methodCallExpr.Arguments.Count > 0)
            {
                var methodName = methodCallExpr.Method.Name;
                if (methodName == "get_Item" && methodCallExpr.Arguments[0] is ConstantExpression constExpr)
                {
                    memberNames.Insert(1, $"[{constExpr.Value}]");
                }
                else
                {
                    memberNames.Insert(1, "[*]");
                }
            }

            return string.Join(".", memberNames);
        }

        private static (Type containerType, PropertyInfo propertyInfo) GetContainerTypeAndProperty<TModel, TResult>(
            Expression<Func<TModel, TResult>> expression)
        {
            MemberExpression memberExpr = null;

            if (expression.Body is MemberExpression)
            {
                memberExpr = (MemberExpression)expression.Body;
            }
            else if (expression.Body is UnaryExpression unaryExpr && unaryExpr.Operand is MemberExpression)
            {
                memberExpr = (MemberExpression)unaryExpr.Operand;
            }

            if (memberExpr == null)
                throw new ArgumentException("Expression is not a member access", nameof(expression));

            var propertyInfos = new System.Collections.Generic.List<PropertyInfo>();
            var type = typeof(TModel);
            Expression expr = memberExpr;

            while (expr is MemberExpression m)
            {
                if (m.Member is PropertyInfo pi)
                {
                    propertyInfos.Insert(0, pi);
                    type = pi.PropertyType;
                    expr = m.Expression;
                }
                else
                {
                    throw new ArgumentException("Member is not a property", nameof(expression));
                }
            }

            if (propertyInfos.Count == 0)
                throw new ArgumentException("No properties found in expression", nameof(expression));

            PropertyInfo propertyInfoFinal = propertyInfos[^1];
            Type containerTypeFinal = typeof(TModel);

            if (propertyInfos.Count > 1)
            {
                PropertyInfo containerProperty = propertyInfos[^2];
                containerTypeFinal = containerProperty.PropertyType;
            }

            return (containerTypeFinal, propertyInfoFinal);
        }

        private static DataType GetDataType(Type containerType, PropertyInfo propertyInfo)
        {
            var att = propertyInfo.GetCustomAttribute<DataTypeAttribute>();
            return att?.DataType ?? DataType.Text;
        }

        private static string GetPlaceholder(object moreData)
        {
            if (moreData == null) return string.Empty;
            var placeholderProperty = moreData.GetType().GetProperty("placeholder");
            return placeholderProperty?.GetValue(moreData)?.ToString() ?? string.Empty;
        }
    }
}

像下面一样使用它,它对我有用。

@model _79271231.Models.SampleModel
@using _79271231.Helpers

@{
    ViewData["Title"] = "Custom EditorFor Sample";
}

<h1>@ViewData["Title"]</h1>

<form asp-action="Index" method="post">
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Description</th>
            </tr>
        </thead>
        <tbody>
            @for (int i = 0; i < Model.LinkedTable.Length; i++)
            {
                <tr>
                    <td>
                        @Html.MyEditorFor(x => x.LinkedTable[i].MyDate, new { placeholder = "Test Date" })
                    </td>
                    <td>
                        @Html.MyEditorFor(x => x.LinkedTable[i].Description, new { placeholder = "Test Description" })
                    </td>
                </tr>
            }
        </tbody>
    </table>
    <button type="submit" class="btn btn-primary">Submit</button>
</form>
© www.soinside.com 2019 - 2024. All rights reserved.