MVC 3自定义DataAnnotation:将错误消息与特定属性关联

本文关键字:属性 关联 消息 错误 自定义 DataAnnotation MVC | 更新日期: 2023-09-27 18:10:11

我已经定义了一个自定义的DataAnnotation属性,它与这个属性类似,但确保至少填充了一个属性。它可以正常工作,并向模型的ValidationSummary添加错误消息。但是,我希望能够将错误消息与特定的属性(或者任何字符串)关联起来,以便我可以在视图的特定位置显示它。

因此,如果我的自定义属性像这样使用:
[RequireAtLeastOne(GroupId = 0, ErrorMessage = "You must specify at least one owner phone number.")]
public class UserViewModel: User {
    ...
}

那么我希望能够这样说:

[RequireAtLeastOne(GroupId = 0, ErrorMessage = "You must specify at least one owner phone number.", ValidationErrorKey = "my_key")]
public class UserViewModel: User {
    ...
}

…然后在视图中使用它,像这样:

@Html.ValidationMessage("my_key")

如果我必须将错误消息与模型上的特定属性相关联,而不是与任意字符串相关联,也会很好。我怎样才能做到这一点呢?

MVC 3自定义DataAnnotation:将错误消息与特定属性关联

使用ryudice的答案和这个问题作为起点,我能够使用IValidatableObject解决这个问题。对于任何感兴趣的人,这里是我最后的完整代码:

1。定义一个自定义验证属性,RequireAtLeastOneAttribute

这个属性在类上指示验证应该检查属性组,并确保从每个组中至少填充一个属性。该属性还定义了错误消息和ErrorMessageKey,后者将用于跟踪错误消息并在视图中显示它们,而不是使用通用的ValidationSummary集合。

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class RequireAtLeastOneAttribute: ValidationAttribute {
    /// <summary>
    /// This identifier is used to group properties together.
    /// Pick a number and assign it to each of the properties
    /// among which you wish to require one.
    /// </summary>
    public int GroupId { get; set; }
    /// <summary>
    /// This defines the message key any errors will be associated
    /// with, so that they can be accessed via the front end using
    /// @Html.ValidationMessage(errorMessageKey).
    /// </summary>
    public string ErrorMessageKey { get; set; }
    public override bool IsValid(object value) {
        // Find all properties on the class having a "PropertyGroupAttribute"
        // with GroupId matching the one on this attribute
        var typeInfo = value.GetType();
        var propInfo = typeInfo.GetProperties();
        foreach (var prop in propInfo) {
            foreach (PropertyGroupAttribute attr in prop.GetCustomAttributes(typeof(PropertyGroupAttribute), false)) {
                if (attr.GroupId == this.GroupId
                    && !string.IsNullOrWhiteSpace(prop.GetValue(value, null).GetString())) {
                    return true;
                }
            }
        }
        return false;
    }
}

2。定义自定义属性PropertyGroupAttribute

这将用于定义哪些属性组需要至少填充一个值。

[AttributeUsage(AttributeTargets.Property)]
public class PropertyGroupAttribute : Attribute {
    public PropertyGroupAttribute(int groupId) {
        this.GroupId = groupId;
    }
    public int GroupId { get; set; }
}

3。将属性附加到模型和属性

使用"GroupId"整数将属性组合在一起(该整数可以是任何值,只要它与所有必须填写至少一个的属性相同)。

[RequireAtLeastOne(GroupId = 0, ErrorMessage = "You must specify at least one owner phone number.", ErrorMessageKey = "OwnerPhone")]
[RequireAtLeastOne(GroupId = 1, ErrorMessage = "You must specify at least one authorized producer phone number.", ErrorMessageKey = "AgentPhone")]
public class User: IValidatableObject {
    #region Owner phone numbers
    // At least one is required
    [PropertyGroup(0)]
    public string OwnerBusinessPhone { get; set; }
    [PropertyGroup(0)]
    public string OwnerHomePhone { get; set; }
    [PropertyGroup(0)]
    public string OwnerMobilePhone { get; set; }
    #endregion
    #region Agent phone numbers
    // At least one is required
    [PropertyGroup(1)]
    public string AgentBusinessPhone { get; set; }
    [PropertyGroup(1)]
    public string AgentHomePhone { get; set; }
    [PropertyGroup(1)]
    public string AgentMobilePhone { get; set; }
    #endregion
}

4。在模型上实现IValidatableObject

public class User: IValidatableObject {
    ...
    #region IValidatableObject Members
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
        var results = new List<ValidationResult>();
        // This keeps track of whether each "RequireAtLeastOne" group has been satisfied
        var groupStatus = new Dictionary<int, bool>();
        // This stores the error messages for each group as defined
        // by the RequireAtLeastOneAttributes on the model
        var errorMessages = new Dictionary<int, ValidationResult>();
        // Find all "RequireAtLeastOne" property validators 
        foreach (RequireAtLeastOneAttribute attr in Attribute.GetCustomAttributes(this.GetType(), typeof(RequireAtLeastOneAttribute), true)) {
            groupStatus.Add(attr.GroupId, false);
            errorMessages[attr.GroupId] = new ValidationResult(attr.ErrorMessage, new string[] { attr.ErrorMessageKey });
        }
        // For each property on this class, check to see whether
        // it's got a PropertyGroup attribute, and if so, see if
        // it's been populated, and if so, mark that group as "satisfied".
        var propInfo = this.GetType().GetProperties();
        bool status;
        foreach (var prop in propInfo) {
            foreach (PropertyGroupAttribute attr in prop.GetCustomAttributes(typeof(PropertyGroupAttribute), false)) {
                if (groupStatus.TryGetValue(attr.GroupId, out status) && !status
                    && !string.IsNullOrWhiteSpace(prop.GetValue(this, null).GetString())) {
                    groupStatus[attr.GroupId] = true;
                }
            }
        }
        // If any groups did not have at least one property 
        // populated, add their error messages to the
        // validation result.
        foreach (var kv in groupStatus) {
            if (!kv.Value) {
                results.Add(errorMessages[kv.Key]);
            }
        }
        return results;
    }
    #endregion
}

5。在视图

中使用验证消息

验证消息将保存为您在RequireAtLeastOne属性定义中指定的ErrorMessageKey—在本例中为OwnerPhoneAgentPhone

@Html.ValidationMessage("OwnerPhone")

警告

内置验证还将错误消息添加到ValidationSummary集合,但仅针对模型上定义的第一个属性。因此,在本例中,只有OwnerPhone的消息会显示在ValidationSummary中,因为它首先在模型上定义。我还没有找到解决这个问题的方法,因为在我的情况下,这无关紧要。

你可以在你的模型上实现IValidatableObject并在那里做自定义逻辑,它将允许你使用任何你想要的键来添加消息