创建一个类型化的 ModelState.AddModelError()

Posted

技术标签:

【中文标题】创建一个类型化的 ModelState.AddModelError()【英文标题】:Creating a typed ModelState.AddModelError() 【发布时间】:2012-11-13 21:29:40 【问题描述】:

在控制器中,我可以执行数据库查找等并添加一些与模型属性相关的错误消息:

public ActionResult CreateJob(CreateJobModel viewModel)

    var call = FindCall(viewModel.CallNumber);

    if (call == null)
    
        ModelState.AddModelError("CallNumber", "Idiot User!");
    

我不喜欢 CallNumber 是一个字符串,理想情况下它应该直接引用 viewModel.CallNumber,如果我更改该属性的名称,它也应该更改。

我怎样才能做到这一点?

我想代码最终会是这样的,它需要一个属性访问表达式:

AddModelFieldError(() => viewModel.CallNumber, "Idiot User!");

但我不确定如何创建这样的方法,或者在它是需要错误消息的子/内部属性的情况下。

【问题讨论】:

【参考方案1】:

我会编写自己的通用扩展方法:

public static class ModelStateDictionaryHelper

    public static void AddModelError<TViewModel>(
        this ModelStateDictionary me,
        Expression<Func<TViewModel, object>> lambdaExpression, string error)
                
        me.AddModelError(GetPropertyName(lambdaExpression), error);
    

    private static string GetPropertyName(Expression lambdaExpression)
    
        IList<string> list = new List<string>();
        var e = lambdaExpression;

        while (true)
        
            switch (e.NodeType)
            
                case ExpressionType.Lambda:
                    e = ((LambdaExpression)e).Body;
                    break;
                case ExpressionType.MemberAccess:
                    var propertyInfo = ((MemberExpression)e).Member as PropertyInfo;
                    var prop = propertyInfo != null
                                      ? propertyInfo.Name
                                      : null;
                    list.Add(prop);

                    var memberExpression = (MemberExpression)e;
                    if (memberExpression.Expression.NodeType != ExpressionType.Parameter)
                    
                        var parameter = GetParameterExpression(memberExpression.Expression);
                        if (parameter != null)
                        
                            e = Expression.Lambda(memberExpression.Expression, parameter);
                            break;
                        
                    
                    return string.Join(".", list.Reverse());
                default:
                    return null;
            
        
    

    private static ParameterExpression GetParameterExpression(Expression expression)
    
        while (expression.NodeType == ExpressionType.MemberAccess)
        
            expression = ((MemberExpression)expression).Expression;
        
        return expression.NodeType == ExpressionType.Parameter ? (ParameterExpression)expression : null;
    

及用法:

ModelState.AddModelError<CreateJobModel>(x => x.CallNumber, 
                                              "some kind attention");

它看起来与您询问的版本有点不同,但我希望它是可以接受的替代方案。

【讨论】:

@Yorro:很高兴知道,当我提供这个答案时,我不知道 ExpressionHelper.GetExpressionText 存在于 MVC 框架中。 @Yorro 不错。我在想肯定有这样的课程:)【参考方案2】:

从 C# 6 开始,您可以使用 nameof 运算符。

public ActionResult CreateJob(CreateJobModel viewModel)

    var call = FindCall(viewModel.CallNumber);

    if (call == null)
    
        ModelState.AddModelError(nameof(CreateJobModel.CallNumber), "Idiot User!");
    

【讨论】:

它只适用于单一级别,即 nameof(CreateJobModel.CallNumber),但不适用于 nameof(CreateJobModel.CallNumber.Extension)。 如果可能(?),我建议将模型属性的所有错误添加到模型状态字典中的同一键 可以将所有错误添加到 string.Empty 键,但为什么要在所有情况下都这样做?每个属性的键对于正确的错误报告很有意义。如果你不是这种情况,你甚至不需要字典。只返回一个错误数组。 你能把多个错误加到一个真正的键上吗?如果是,那么我会将嵌套对象中的所有错误添加到该对象的键中。

以上是关于创建一个类型化的 ModelState.AddModelError()的主要内容,如果未能解决你的问题,请参考以下文章

c++ 创建类的实例作为引用类型

在 python 中使用类型类创建类型类

从它的类型创建一个 UIViewController 具体类

我为文件类型创建了一个文件类型和一个解释器。我希望在双击文件类型后启用解释器运行文件类型

使用自定义类型参数创建一个过程。 PL/SQL

创建一个MySQL数据库中的datetime类型