You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET MVC Create视图中Fluent Validation实体级验证消息放置位置

ASP.NET MVC中Fluent Validation实体级验证消息不显示的解决办法

问题说明

在ASP.NET MVC项目里用Fluent Validation写了DocTrailListValidator验证器,ProjectID、DocumentType、IsThisCurrent这些字段级的验证规则能在Create视图正常显示错误消息,但针对整个实体的重复项验证规则(RuleFor(x => x).Must(...))的“Duplicate entry found.”错误始终显示不出来,试过关联到ProjectID的验证消息也没用。


原代码参考

验证器代码

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
using DocumentFormat.OpenXml.Linq;
using FluentValidation;
using FluentValidation.AspNetCore;
using FluentValidation.Results;
using Microsoft.Ajax.Utilities;
using OpenXmlPowerTools;
using ValidatorAttribute = ServiceStack.FluentValidation.Attributes.ValidatorAttribute;

namespace Research.Models
{
    public class DocTrailListValidator : AbstractValidator<DocTrailList>
    {
        public DocTrailListValidator()
        {
            RuleFor(x => x.ProjectID).NotNull().WithMessage("A required field");
            RuleFor(x => x.DocumentType).NotNull().WithMessage("A required field");
            RuleFor(x => x.IsThisCurrent).NotNull().WithMessage("A required field");
            RuleFor(x => x).Must(x => !IsDuplicate(x)).WithMessage("Duplicate entry found.");
        }

        private bool IsDuplicate(DocTrailList c)
        {
            // Example list of existing values for validation
            var existingValues = new List<DocTrailList>();
            

                return existingValues.Any(x => x.ProjectID == c.ProjectID &&
                                           x.DocumentType == c.DocumentType &&
                                           x.IsThisCurrent == c.IsThisCurrent);
        }
    }
}

Create视图代码片段

<div class="col">
    <div class="form-group"> 
        <label asp-for="IsThisCurrent" class="control-label"></label>
        @Html.DropDownList("IsThisCurrent", new SelectList(ViewBag.current,"YesNo","YesNo"),string.Empty)
        <div class="col-md-10">
            @Html.ValidationMessageFor(model => model.IsThisCurrent, null, new { @class = "text-danger" })
        </div>
    </div>
</div>
<div class="col">
    <div class="form-group">
        List<DocTrailList>
        @Html.ValidationMessageFor(model => model.ProjectID, null, new { @class = "text-danger" })
</div>

Create控制器代码

// GET: DocTrailLists/Create
public IActionResult Create()
{
    ViewData["ProjectID"] = new SelectList(_context.PIF, "ProjectID", "ProjectID");

    var dtname = _context.DocType.ToList();
    ViewBag.dtname = dtname;

    var current = _context.YesNoList.ToList();
    ViewBag.current = current;

    return View();
}

// POST: DocTrailLists/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("DetailID,ProjectID,DocumentType,Version,Dated,DateReceived,IsThisCurrent")] DocTrailList docTrailList)
{
    DocTrailListValidator doctraillistsvalidator = new DocTrailListValidator();
    ValidationResult result = doctraillistsvalidator.Validate(docTrailList);

    if (result.IsValid)
    {
        if (ModelState.IsValid)
        {
            _context.Add(docTrailList);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
    }
    else
    {
        foreach (var failure in result.Errors)
        {
            ModelState.AddModelError("", failure.ErrorMessage);
        }
    }
    ViewData["ProjectID"] = new SelectList(_context.PIF, "ProjectID", "ProjectID", docTrailList.ProjectID);
    return View(docTrailList);
}

解决办法

核心问题

你在控制器里把实体级验证错误加到了空键的ModelState里(ModelState.AddModelError("", failure.ErrorMessage);),但视图里没有对应显示空键错误的控件;另外Fluent Validation的实体级规则默认不会绑定到单个字段,得手动指定或者在视图加全局错误显示。

方案一:把重复错误绑定到指定字段

修改验证器,把实体级的重复验证规则关联到某个字段(比如ProjectID),这样就能用该字段的ValidationMessageFor显示错误:

// 替换原验证器中的实体级规则
RuleFor(x => x.ProjectID)
    .Must((entity, _) => !IsDuplicate(entity)) // 第一个参数是完整实体,第二个是ProjectID的值
    .WithMessage("已存在重复记录");

这样验证错误会被标记到ProjectID字段,视图里@Html.ValidationMessageFor(model => model.ProjectID)就能同时显示必填和重复的错误消息。

方案二:在视图中添加全局错误显示

不想绑定到单个字段的话,直接在表单顶部加全局错误显示控件:

<!-- 放在表单最上方,显示所有错误(包括实体级) -->
<div class="text-danger">
    @Html.ValidationSummary(false, "", new { @class = "text-danger" })
</div>

或者专门显示空键的错误:

@foreach (var error in ViewData.ModelState[""].Errors)
{
    <div class="text-danger">@error.ErrorMessage</div>
}

这样控制器里加的空键错误就能正常显示。

额外优化

  1. 修复重复验证逻辑:原代码里existingValues是空列表,永远不会检测到重复,要改成从数据库查真实数据,记得注入DbContext:
private readonly YourDbContext _context;

// 构造函数注入DbContext
public DocTrailListValidator(YourDbContext context)
{
    _context = context;
    // ...其他验证规则
}

private bool IsDuplicate(DocTrailList c)
{
    // 排除当前编辑的记录(如果是编辑场景)
    return _context.DocTrailList.Any(x => 
        x.ProjectID == c.ProjectID &&
        x.DocumentType == c.DocumentType &&
        x.IsThisCurrent == c.IsThisCurrent &&
        x.DetailID != c.DetailID);
}
  1. 启用Fluent Validation自动集成:不用手动实例化验证器,在Startup.cs里配置:
services.AddControllersWithViews()
    .AddFluentValidation(fv => fv.RegisterValidatorsFromAssemblyContaining<DocTrailListValidator>());

这样控制器里只要检查ModelState.IsValid就行,不用手动调用验证器:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("DetailID,ProjectID,DocumentType,Version,Dated,DateReceived,IsThisCurrent")] DocTrailList docTrailList)
{
    if (ModelState.IsValid)
    {
        _context.Add(docTrailList);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }

    // 重新绑定下拉框数据
    ViewData["ProjectID"] = new SelectList(_context.PIF, "ProjectID", "ProjectID", docTrailList.ProjectID);
    ViewBag.dtname = _context.DocType.ToList();
    ViewBag.current = _context.YesNoList.ToList();
    return View(docTrailList);
}

内容的提问来源于stack exchange,提问作者Sally Parkes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 09:34:56