首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >mvc3 ValidationSummary排除属性错误IValidatableObject

mvc3 ValidationSummary排除属性错误IValidatableObject
EN

Stack Overflow用户
提问于 2011-06-22 07:03:35
回答 6查看 8.5K关注 0票数 5

我的模型(类A)有一个实现了IValidatableObject的类型B的属性(称为b)。

视图已获取@Html.ValidationSummary(true)

在验证摘要中,我希望排除与属性相关的错误。在B类中,IValidatableObject实现返回的是不带memberNames的ValidationResult

但不会显示来自IValidatableObject的B类验证错误,因为B类是A类的属性

如何显示B类非属性验证错误?

EN

回答 6

Stack Overflow用户

发布于 2012-05-07 02:57:42

我认为这是非常直接的,让我用一个例子来解释。首先让我创建您面临的问题,然后我将解释如何解决。

1)声明我的模型。

代码语言:javascript
复制
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)声明简单的操作。

代码语言:javascript
复制
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创建一个简单的视图和编辑器模板。

测试视图:

代码语言:javascript
复制
@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

代码语言:javascript
复制
<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",,但错误不会显示 :) :)

此的原因

因此,如果我们看到视图的代码,我们可以找到这一行

代码语言:javascript
复制
 @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)显示。

代码

代码语言:javascript
复制
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (!string.IsNullOrWhiteSpace(MyProperty) && MyProperty.Length > 10)
            yield return new ValidationResult("MaxLength reached", new List<string> { "MyProperty" });
    }

类似于错误

我认为现在很清楚为什么没有显示错误消息,以及有哪些可用的选项。您可以自行决定最适合您的方法。

干杯

票数 3
EN

Stack Overflow用户

发布于 2012-05-05 00:20:22

虽然验证摘要的这种行为在您的情况下可能不合适,但通常它必须被认为是“正确的”。在模型中包含的子对象中创建错误时,将向错误添加右前缀。也就是说,如果子对象包含在属性MyProp中,则前缀MyProp会自动添加到所有错误中。这对于为创建的所有错误提供正确的名称是必要的,否则ValidationsummaryValidationMessageFor都不会正常工作,因为它们引用了完整的名称(包括整个前缀的名称)。这是避免歧义的唯一方法,因为您可能在两个不同的子对象中有两个Name属性。

然而,通常,当子对象中生成的错误不是简单的属性级错误而是“整个对象”级错误时,这种“正确”行为是不适当的。在这种情况下,您可能希望它们出现在常规验证摘要中。

你可以通过两种方式来面对这个问题:

使用另一个特定于sub-object

  • Error冒泡的验证摘要-我经常使用错误冒泡来表示模型的某个子部分--没有在屏幕上显示--包含错误,因此用户可以打开一个详细窗口(
  1. 或类似对话框)来查看它们。基本上,错误冒泡包括使用foreach处理ModelState中的所有错误,然后将其中一些错误提升。提升意味着删除错误前缀的最后一部分。在升级错误时,您可以保留或不保留原始错误-保留原始错误也更容易,并且在大多数情况下这是正确的做法。请注意,您不能在循环所有条目时删除条目-您必须将其放入列表中,然后在循环结束后将其删除。

推广标准可能取决于您的需求。我给你举一些例子:

提升属性级别错误的

  • 将其转换为对象级别错误。子对象级别错误将其转换为外部对象的对象级别错误。这就是您应该感兴趣的情况-只需将与根ViewModel的属性关联的对象级错误提升到包含整个对象而不是简单值!

错误处理可以在您可以定义的自定义ActionFilter中执行,并在几个操作方法中重用。

下面是一个简单的PromoteAttribute ActionFilter的代码。它的用法是:

代码语言:javascript
复制
[Promote("prop1.SubProp1 Prop2")]
public ActionResult MyMethod( ...

也就是说,你向它传递一个你想要提升到根模型的表达式错误列表,如果它在ModelState中找到与它们匹配的错误,它就会提升它们--显然这只是一个简单的例子-你可以只提升一个级别,而不是根模型,并且你可以使用一个复杂的标准来定位要提升的错误,而不是列出它们:

代码语言:javascript
复制
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);

            }
        }
    }
}
票数 2
EN

Stack Overflow用户

发布于 2012-05-03 22:29:19

挖掘出MVC3的源代码,并对其进行编辑以允许包含属性。

代码语言:javascript
复制
@Html.ValidationSummary(new [] { "PropertyName" })

将包括名为PropertyName的属性

代码语言:javascript
复制
@Html.ValidationSummary(new [] { "ArrayName[]" })

将包括属性ArrayName、ArrayName1等。

代码语言:javascript
复制
@Html.ValidationSummary(new [] { "ArrayName[]", "PropertyName" })

都会包括在内。

代码语言:javascript
复制
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;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/6433023

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档