对于我的ASP.NET MVC应用程序,我创建了自定义验证属性,并指出可以为单个字段或属性指定多个实例:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]
public sealed class SomeAttribute: ValidationAttribute
我为这样的属性创建了验证器:
public class SomeValidator : DataAnnotationsModelValidator<SomeAttribute>
并将其连接到Global.asax的Application_Start
DataAnnotationsModelValidatorProvider.RegisterAdapter(
typeof (SomeAttribute), typeof (SomeValidator));
最后,如果我以所需的方式使用我的属性:
[SomeAttribute(...)] //first
[SomeAttribute(...)] //second
public string SomeField { get; set; }
当框架执行验证时,只调用第一个属性实例。第二个似乎死了。我注意到在每个请求期间只创建了一个验证器实例(对于第一个注释)。
如何解决此问题并触发所有属性?
答案 0 :(得分:7)
让我自己回答。来自MSDN(http://msdn.microsoft.com/en-us/library/system.attribute.typeid.aspx,http://msdn.microsoft.com/en-us/library/6w3a7b50.aspx):
使用时定义自定义属性 AttributeUsageAttribute.AllowMultiple设置为true,您必须覆盖 Attribute.TypeId属性使其唯一。如果所有的实例 您的属性是唯一的,重写Attribute.TypeId以返回 属性的对象标识。如果只是你的一些实例 属性是唯一的,从Attribute.TypeId返回一个值 在这些情况下返回平等。例如,某些属性有一个 构造函数参数,充当唯一键。对于这些属性, 从中返回构造函数参数的值 Attribute.TypeId属性。
如实现的那样,该标识符仅仅是属性的类型。 但是,意图使用唯一标识符来识别 两个相同类型的属性。
总结:
TypeId被记录为&#34;唯一标识符,用于标识相同类型的两个属性&#34;。默认情况下,TypeId只是属性的类型,因此当遇到相同类型的两个属性时,它们会被视为&#34;相同的&#34;通过MVC验证框架。
实施:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)]
public sealed class SomeAttribute: ValidationAttribute
{
private string Parameter { get; set; }
public override object TypeId
{
get { return string.Format("{0}[{1}]", GetType().FullName, Parameter); }
}
public SomeAttribute(string parameter)
{
Parameter = parameter;
}
选择TypeId创建方式而不是以下方法:
完成后,服务器端验证案例工作。当这个想法需要与不引人注目的客户端验证相结合时,问题就开始了。假设您已按以下方式定义了自定义验证器:
public class SomeAttributeValidator : DataAnnotationsModelValidator<SomeAttribute>
{
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var rule = new ModelClientValidationRule {ValidationType = "someattribute"};
rule.ValidationParameters.Add(...)
yield return rule;
}
有这个:
public class Model
{
[SomeAttribute("first")]
[SomeAttribute("second")]
public string SomeField { get; set; }
导致以下错误:
不显眼的客户端验证规则中的验证类型名称必须是 独特。以下验证类型不止一次出现: someattribute
如上所述,解决方案是拥有独特的验证类型。我们必须区分每个已注册的属性实例,它已用于注释字段,并为其提供相应的验证类型:
public class SomeAttributeValidator : DataAnnotationsModelValidator<SomeAttribute>
{
private string AnnotatedField { get; set; }
public SomeAttributeValidator(
ModelMetadata metadata, ControllerContext context, SomeAttribute attribute)
: base(metadata, context, attribute)
{
AnnotatedField = string.Format("{0}.{1}",
metadata.ContainerType.FullName, metadata.PropertyName);
}
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()
{
var count = Storage.Get<int>(AnnotatedField) + 1;
Storage.Set(AnnotatedField, count);
var suffix = char.ConvertFromUtf32(96 + count); // gets a lowercase letter
var rule = new ModelClientValidationRule
{
ValidationType = string.Format("someattribute{0}", suffix)
};
(验证类型必须只包含小写字母 - 使用上面的解决方案,如果你有超过26个属性 - 整个字母表用尽,预计会有异常)
其中Storage
是一个简单的实用程序,用于存储当前http请求的数据:
internal class Storage
{
private static IDictionary Items
{
get
{
if (HttpContext.Current == null)
throw new ApplicationException("HttpContext not available.");
return HttpContext.Current.Items;
}
}
public static T Get<T>(string key)
{
if (Items[key] == null)
return default(T);
return (T)Items[key];
}
public static void Set<T>(string key, T value)
{
Items[key] = value;
}
}
最后,JavaScript部分:
$.each('abcdefghijklmnopqrstuvwxyz'.split(''), function(idx, val) {
var adapter = 'someattribute' + val;
$.validator.unobtrusive.adapters.add(adapter, [...], function(options) {
options.rules[adapter] = {
...
};
if (options.message) {
options.messages[adapter] = options.message;
}
});
});
$.each('abcdefghijklmnopqrstuvwxyz'.split(''), function(idx, val) {
var method = 'someattribute' + val;
$.validator.addMethod(method, function(value, element, params) {
...
}, '');
});
要获得完整的解决方案,go through this sources。