我的模型(类A)有一个实现了IValidatableObject的类型B的属性(称为b)。
视图已获取@Html.ValidationSummary(true)
在验证摘要中,我希望排除与属性相关的错误。在B类中,IValidatableObject实现返回的是不带memberNames的ValidationResult
但不会显示来自IValidatableObject的B类验证错误,因为B类是A类的属性
如何显示B类非属性验证错误?
发布于 2012-05-07 02:57:42
我认为这是非常直接的,让我用一个例子来解释。首先让我创建您面临的问题,然后我将解释如何解决。
1)声明我的模型。
public class ClassA
{
[Required]
public string Name { get; set; }
public ClassB CBProp { get; set; }
}
public class ClassB:IValidatableObject
{
[Required]
public string MyProperty { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(MyProperty) && MyProperty.Length > 10)
yield return new ValidationResult("MaxLength reached");
}
}2)声明简单的操作。
public class HomeController : Controller
{
[HttpGet]
public ActionResult Test()
{
ClassA ca = new ClassA();
return View(ca);
}
[HttpPost]
public ActionResult Test(ClassA ca)
{
return View(ca);
}
}3)让我为ClassB创建一个简单的视图和编辑器模板。
测试视图:
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>ClassA</legend>
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
@Html.EditorFor(m => m.CBProp, "TestB")
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}EditorTemplate
<div class="editor-label">
@Html.LabelFor(model => model.MyProperty)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.MyProperty)
@Html.ValidationMessageFor(model => model.MyProperty)
</div>4)现在视图将如下所示:

5)现在,如果我们将单击Submit。不需要输入任何数据。它将如预期那样显示属性验证错误。

6)现在,如果我们在MyProperty中输入一个长度大于10的字符串,它应该会显示错误消息"MaxLength reached",,但错误不会显示 :) :)

此的原因
因此,如果我们看到视图的代码,我们可以找到这一行
@Html.ValidationSummary(true) /// true, will excludePropertyErrors由于CBProp是ClassA中的一个属性,因此ValidationSummary(true)将排除CBProp中的任何错误。因此,您不会发现正在显示的错误消息。然而,对于这一点,有几个选项可用。
选项
1)设置@Html.ValidationSummary()
这将显示错误消息,但也会显示任何其他错误消息(这是多余的),例如

2)在编辑器模板中设置@Html.ValidationSummary(true)。但它将显示错误消息,如

3)在ClassB的validation方法中,在ValidationResult.Now中指定属性名称和错误消息,它将被视为属性的验证错误,并在编辑器模板中由@Html.ValidationMessageFor(model => model.MyProperty)显示。
代码
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (!string.IsNullOrWhiteSpace(MyProperty) && MyProperty.Length > 10)
yield return new ValidationResult("MaxLength reached", new List<string> { "MyProperty" });
}类似于的错误

我认为现在很清楚为什么没有显示错误消息,以及有哪些可用的选项。您可以自行决定最适合您的方法。
干杯
发布于 2012-05-05 00:20:22
虽然验证摘要的这种行为在您的情况下可能不合适,但通常它必须被认为是“正确的”。在模型中包含的子对象中创建错误时,将向错误添加右前缀。也就是说,如果子对象包含在属性MyProp中,则前缀MyProp会自动添加到所有错误中。这对于为创建的所有错误提供正确的名称是必要的,否则Validationsummary和ValidationMessageFor都不会正常工作,因为它们引用了完整的名称(包括整个前缀的名称)。这是避免歧义的唯一方法,因为您可能在两个不同的子对象中有两个Name属性。
然而,通常,当子对象中生成的错误不是简单的属性级错误而是“整个对象”级错误时,这种“正确”行为是不适当的。在这种情况下,您可能希望它们出现在常规验证摘要中。
你可以通过两种方式来面对这个问题:
使用另一个特定于sub-object
foreach处理ModelState中的所有错误,然后将其中一些错误提升。提升意味着删除错误前缀的最后一部分。在升级错误时,您可以保留或不保留原始错误-保留原始错误也更容易,并且在大多数情况下这是正确的做法。请注意,您不能在循环所有条目时删除条目-您必须将其放入列表中,然后在循环结束后将其删除。推广标准可能取决于您的需求。我给你举一些例子:
提升属性级别错误的
ViewModel的属性关联的对象级错误提升到包含整个对象而不是简单值!错误处理可以在您可以定义的自定义ActionFilter中执行,并在几个操作方法中重用。
下面是一个简单的PromoteAttribute ActionFilter的代码。它的用法是:
[Promote("prop1.SubProp1 Prop2")]
public ActionResult MyMethod( ...也就是说,你向它传递一个你想要提升到根模型的表达式错误列表,如果它在ModelState中找到与它们匹配的错误,它就会提升它们--显然这只是一个简单的例子-你可以只提升一个级别,而不是根模型,并且你可以使用一个复杂的标准来定位要提升的错误,而不是列出它们:
public class PromoteAttribute : ActionFilterAttribute
{
string[] expressions;
public PromoteAttribute(string toPromote)
{
expressions = toPromote.Split(' ');
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ModelStateDictionary modelState=filterContext.Controller.ViewData.ModelState;
foreach(var x in expressions)
{
if (modelState.ContainsKey(x))
{
var entry = modelState[x];
if (entry.Errors.Count == 0) continue;
foreach (var error in entry.Errors) modelState.AddModelError("", error.ErrorMessage);
}
}
}
}发布于 2012-05-03 22:29:19
挖掘出MVC3的源代码,并对其进行编辑以允许包含属性。
@Html.ValidationSummary(new [] { "PropertyName" })将包括名为PropertyName的属性
@Html.ValidationSummary(new [] { "ArrayName[]" })将包括属性ArrayName、ArrayName1等。
@Html.ValidationSummary(new [] { "ArrayName[]", "PropertyName" })都会包括在内。
public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, string[] includePropertyErrors)
{
return ValidationSummary(htmlHelper, includePropertyErrors, null, null);
}
public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, string[] includePropertyErrors, string message)
{
return ValidationSummary(htmlHelper, includePropertyErrors, message, null);
}
public static MvcHtmlString ValidationSummary(this HtmlHelper htmlHelper, string[] includePropertyErrors, string message, IDictionary<string, object> htmlAttributes)
{
if (htmlHelper == null)
{
throw new ArgumentNullException("htmlHelper");
}
FormContext formContext = htmlHelper.ViewContext.ClientValidationEnabled ? htmlHelper.ViewContext.FormContext : null;
if (htmlHelper.ViewData.ModelState.IsValid)
{
if (formContext == null)
{ // No client side validation
return null;
}
// TODO: This isn't really about unobtrusive; can we fix up non-unobtrusive to get rid of this, too?
if (htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
{ // No client-side updates
return null;
}
}
string messageSpan;
if (!string.IsNullOrEmpty(message))
{
TagBuilder spanTag = new TagBuilder("span");
spanTag.SetInnerText(message);
messageSpan = spanTag.ToString(TagRenderMode.Normal) + Environment.NewLine;
}
else
{
messageSpan = null;
}
StringBuilder htmlSummary = new StringBuilder();
TagBuilder unorderedList = new TagBuilder("ul");
IEnumerable<ModelState> modelStates = from ms in htmlHelper.ViewData.ModelState
where ms.Key == htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix ||
includePropertyErrors.Any(property =>
{
string prefixedProperty = string.IsNullOrEmpty(htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix) ? property : htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix + "." + property;
if (property.EndsWith("[]"))
{
return prefixedProperty.Substring(0, property.Length - 2) == Regex.Replace(ms.Key, @"\[[^\]]+\]", string.Empty);
}
else
{
return property == ms.Key;
}
})
select ms.Value;
if (modelStates != null)
{
foreach (ModelState modelState in modelStates)
{
foreach (ModelError modelError in modelState.Errors)
{
string errorText = GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext, modelError);
if (!String.IsNullOrEmpty(errorText))
{
TagBuilder listItem = new TagBuilder("li");
listItem.SetInnerText(errorText);
htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
}
}
}
}
if (htmlSummary.Length == 0)
{
htmlSummary.AppendLine(@"<li style=""display:none""></li>");
}
unorderedList.InnerHtml = htmlSummary.ToString();
TagBuilder divBuilder = new TagBuilder("div");
divBuilder.MergeAttributes(htmlAttributes);
divBuilder.AddCssClass((htmlHelper.ViewData.ModelState.IsValid) ? HtmlHelper.ValidationSummaryValidCssClassName : HtmlHelper.ValidationSummaryCssClassName);
divBuilder.InnerHtml = messageSpan + unorderedList.ToString(TagRenderMode.Normal);
if (formContext != null)
{
if (!htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
{
// client val summaries need an ID
divBuilder.GenerateId("validationSummary");
formContext.ValidationSummaryId = divBuilder.Attributes["id"];
formContext.ReplaceValidationSummary = false;
}
}
return new MvcHtmlString(divBuilder.ToString(TagRenderMode.Normal));
}
private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error)
{
return string.IsNullOrEmpty(error.ErrorMessage) ? null : error.ErrorMessage;
}https://stackoverflow.com/questions/6433023
复制相似问题