【问题标题】:MVC 3 custom DataAnnotation: associate error message with particular propertyMVC 3 自定义 DataAnnotation:将错误消息与特定属性相关联
【发布时间】:2011-09-28 22:48:35
【问题描述】:

我已经定义了一个类似于this one 的自定义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")

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

【问题讨论】:

    标签: c# asp.net-mvc-3 data-annotations


    【解决方案1】:

    使用ryudice's answerthis question 作为起点,我能够使用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 中,因为它首先在模型上定义。我还没有找到解决这个问题的方法,因为在我的情况下这并不重要。

    【讨论】:

      【解决方案2】:

      您可以在模型上实现 IValidatableObject 并在那里执行自定义逻辑,它可以让您使用所需的任何键添加消息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-09-21
        • 1970-01-01
        • 1970-01-01
        • 2011-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-25
        相关资源
        最近更新 更多