C# mvc3 ValidationSummary排除属性错误IValidatableObject

C# mvc3 ValidationSummary排除属性错误IValidatableObject,c#,asp.net-mvc-3,validationsummary,C#,Asp.net Mvc 3,Validationsummary,我的模型(类A)有一个类型为b的属性(称为b),实现了IValidatableObject 视图已获得@Html.ValidationSummary(true) 在验证摘要中,我希望排除与属性相关的错误。 在类B中,IValidatableObject实现正在返回没有成员名的ValidationResult 但是由于类B是类a上的属性,因此不会显示来自IValidatableObject的类B有效性错误 如何显示B类非属性验证错误?挖掘MVC3的源代码并进行编辑以允许包含属性 @Html.Val

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

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

在验证摘要中,我希望排除与属性相关的错误。 在类B中,
IValidatableObject
实现正在返回没有成员名的
ValidationResult

但是由于类B是类a上的属性,因此不会显示来自
IValidatableObject
的类B有效性错误


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

挖掘MVC3的源代码并进行编辑以允许包含属性

@Html.ValidationSummary(new [] { "PropertyName" })
将包括名为PropertyName的属性

@Html.ValidationSummary(new [] { "ArrayName[]" })
将包括属性ArrayName[0]、ArrayName[1]等

@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;
}
public static MvcHtmlString ValidationSummary(此HtmlHelper HtmlHelper,字符串[]包含属性错误)
{
返回ValidationSummary(htmlHelper,includePropertyErrors,null,null);
}
公共静态MvcHtmlString验证摘要(此HtmlHelper HtmlHelper,字符串[]包括属性错误,字符串消息)
{
返回ValidationSummary(htmlHelper,includePropertyErrors,message,null);
}
公共静态MvcHtmlString验证摘要(此HtmlHelper HtmlHelper,字符串[]包括属性错误、字符串消息、IDictionary HtmlAttribute)
{
如果(htmlHelper==null)
{
抛出新ArgumentNullException(“htmlHelper”);
}
FormContext FormContext=htmlHelper.ViewContext.ClientValidationEnabled?htmlHelper.ViewContext.FormContext:null;
if(htmlHelper.ViewData.ModelState.IsValid)
{
if(formContext==null)
{//没有客户端验证
返回null;
}
//TODO:这并不是关于不引人注目的问题,我们能把不引人注目的问题也解决掉吗?
if(htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled)
{//没有客户端更新
返回null;
}
}
字符串消息跨度;
如果(!string.IsNullOrEmpty(消息))
{
标记生成器span标记=新标记生成器(“span”);
spanTag.SetInnerText(消息);
messageSpan=spanTag.ToString(TagRenderMode.Normal)+Environment.NewLine;
}
其他的
{
messageSpan=null;
}
StringBuilder htmlSummary=新建StringBuilder();
标记生成器无序列表=新标记生成器(“ul”);
IEnumerable ModelState=来自htmlHelper.ViewData.ModelState中的ms
其中ms.Key==htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix||
includePropertyErrors.Any(属性=>
{
string prefixedProperty=string.IsNullOrEmpty(htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix)?属性:htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix+“+”+属性;
if(property.EndsWith(“[]”)
{
返回prefixedProperty.Substring(0,property.Length-2)=Regex.Replace(ms.Key,@“\[^\]]+\]”,string.Empty);
}
其他的
{
返回属性==ms.Key;
}
})
选择ms.Value;
if(modelStates!=null)
{
foreach(ModelState中的ModelState)
{
foreach(modelState.Errors中的ModelError ModelError)
{
字符串errorText=GetUserErrorMessageOrDefault(htmlHelper.ViewContext.HttpContext,modelError);
如果(!String.IsNullOrEmpty(errorText))
{
标记生成器列表项=新标记生成器(“li”);
listItem.SetInnerText(errorText);
htmlSummary.AppendLine(listItem.ToString(TagRenderMode.Normal));
}
}
}
}
if(htmlSummary.Length==0)
{
htmlSummary.AppendLine(@“
  • ”; } unorderedList.InnerHtml=htmlSummary.ToString(); 标记生成器divBuilder=新标记生成器(“div”); divBuilder.MergeAttributes(HtmlatAttributes); divBuilder.AddCssClass((htmlHelper.ViewData.ModelState.IsValid)?htmlHelper.ValidationSummaryValidCssClassName:htmlHelper.ValidationSummaryCssClassName); divBuilder.InnerHtml=messageSpan+unorderedList.ToString(TagRenderMode.Normal); if(formContext!=null) { 如果(!htmlHelper.ViewContext.UnobtrusiveJavaScriptEnabled) { //客户val摘要需要一个ID divBuilder.GenerateId(“validationSummary”); formContext.ValidationSummaryId=divBuilder.Attributes[“id”]; formContext.ReplaceValidationSummary=false; } } 返回新的MvcHtmlString(divBuilder.ToString(TagRenderMode.Normal)); } 私有静态字符串GetUserErrorMessageOrDefault(HttpContextBase httpContext,ModelError错误) { 返回字符串.IsNullOrEmpty(error.ErrorMessage)?null:error.ErrorMessage; }
    让我们从我们知道的开始:

    如描述所示,如果我们有我们的模型:

    型号A:

    public class A
    {
        public B ModelB { get; set; }
    }
    
    public class B : IValidatableObject
    {
        public string Name { get; set; }
    
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            List<ValidationResult> errors = new List<ValidationResult>();
    
            if (string.IsNullOrEmpty(Name)) {
                errors.Add(new ValidationResult("Please enter your name"));
            }
    
            return errors;
        }
    }
    
    @model A
    
    @Html.ValidationSummary(true)
    
    @using (Html.BeginForm())
    {
        @Html.EditorFor(model => model.ModelB.Name)
    
        <input type="submit" value="submit" />
    }
    
    型号B:

    public class A
    {
        public B ModelB { get; set; }
    }
    
    public class B : IValidatableObject
    {
        public string Name { get; set; }
    
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            List<ValidationResult> errors = new List<ValidationResult>();
    
            if (string.IsNullOrEmpty(Name)) {
                errors.Add(new ValidationResult("Please enter your name"));
            }
    
            return errors;
        }
    }
    
    @model A
    
    @Html.ValidationSummary(true)
    
    @using (Html.BeginForm())
    {
        @Html.EditorFor(model => model.ModelB.Name)
    
        <input type="submit" value="submit" />
    }
    
    然后,当绑定到
    A
    模型时,
    DefaultModelBinder
    将查找名为
    ModelB.Name
    的属性,它将成功地找到并绑定到该属性

    但是,由
    DefaultModelBinder
    A
    模型执行的模型验证将调用为模型
    
    
    @Html.EditorFor(model => model.ModelB, null, "")
    
    <input class="text-box single-line" id="Name" name="Name" type="text" value="" /> 
    
    [HttpPost]
    public ActionResult Index(A modelA, B modelB)
    {
        modelA.ModelB = modelB;
        if (ModelState.IsValid)
        {
            return RedirectToAction("NextAction");
        }
        return View();
    }
    
    @Html.EditorFor(model => model.Name, null, "modelA.Name")
    
    [Promote("prop1.SubProp1 Prop2")]
    public ActionResult MyMethod( ...
    
    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);
    
                }
            }
        }
    }
    
    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");
        }
    }
    
    public class HomeController : Controller
    {       
        [HttpGet]
        public ActionResult Test()
        {
            ClassA ca = new ClassA();
            return View(ca);
        }
    
        [HttpPost]
        public ActionResult Test(ClassA ca)
        {            
            return View(ca);
        }
    }
    
    @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>
    }
    
    <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>
    
     @Html.ValidationSummary(true)  /// true, will excludePropertyErrors
    
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            if (!string.IsNullOrWhiteSpace(MyProperty) && MyProperty.Length > 10)
                yield return new ValidationResult("MaxLength reached", new List<string> { "MyProperty" });
        }
    
    ModelState.AddModelError("", "This is a summary level error text for the model");
    
    ModelState.AddModelError("b", "This is a 'summary' error for the property named b");