I have this validator class:
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);
}
}
}
The first three rules validation errors are assigned to their respective fields in the Create view and I can see them firing. However, for the existingValues variable, how/where do I place the Validation Message in order for it to fire on the Create page? The following snippet of the Create view code shows the IsThisCurrent field (for which the Validation Message works) and bmy 'effort' at getting the variable in the list to fire. I tried it with the variable name also but that failed. Any help gratefully received.
<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" }) *@
@Html.ValidationMessageFor(model => model.ProjectID, null, new { @class = "text-danger" })
</div>
UPDATE: This is the controller for the Create view.
// 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.AddModelError("", failure.ErrorMessage);, I doubt this will reset all the validation property name. You should provide the property name:ModelState.AddModelError(failure.PropertyName, failure.ErrorMessage);. Other than that, my concern will be does your 4th validation failed and it appears in theresult.Errors.result.Errors. If yes, means that could be model state or view issue that not able to bind that error message. If no, means that your 4th validation is passed, then you should check on its implementation why the validation passes.