C# MVC 3:防止属性属性中出现魔术字符串
我在网上找到了一个RequiredIfAttribute,我将其修改为RequiredNotIf。该属性可以这样使用。
[RequiredNotIf("LastName", null, ErrorMessage = "You must fill this.")]
public string FirstName { get; set; }
[RequiredNotIf("FirstName", null, ErrorMessage = "You must fill this")]
public string LastName { get; set; }
以及属性的源代码...
[AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true)]
public class RequiredNotIfAttribute : RequiredAttribute, IClientValidatable
{
private string OtherProperty { get; set; }
private object Condition { get; set; }
public RequiredNotIfAttribute(string otherProperty, object condition)
{
OtherProperty = otherProperty;
Condition = condition;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(OtherProperty);
if (property == null)
{
return new ValidationResult(String.Format("Property {0} not found.", OtherProperty));
}
var propertyValue = property.GetValue(validationContext.ObjectInstance, null);
var conditionIsMet = !Equals(propertyValue, Condition);
return conditionIsMet ? base.IsValid(value, validationContext) : null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
var depProp = BuildDependentPropertyId(metadata, context as ViewContext);
var targetValue = (Condition ?? "").ToString();
if (Condition != null && Condition is bool)
{
targetValue = targetValue.ToLower();
}
rule.ValidationParameters.Add("otherproperty", depProp);
rule.ValidationParameters.Add("condition", targetValue);
yield return rule;
}
private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
{
var depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(OtherProperty);
var thisField = metadata.PropertyName + "_";
if (depProp.StartsWith(thisField))
{
depProp = depProp.Substring(thisField.Length);
}
return depProp;
}
}
这个的缺点 - 正如我所看到的 - 是属性“header”中的魔术字符串。我怎样才能摆脱它?
I found a RequiredIfAttribute on the internet which I modified to RequiredNotIf. The attribute can be used like this.
[RequiredNotIf("LastName", null, ErrorMessage = "You must fill this.")]
public string FirstName { get; set; }
[RequiredNotIf("FirstName", null, ErrorMessage = "You must fill this")]
public string LastName { get; set; }
And the source code to the attribute...
[AttributeUsageAttribute(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true)]
public class RequiredNotIfAttribute : RequiredAttribute, IClientValidatable
{
private string OtherProperty { get; set; }
private object Condition { get; set; }
public RequiredNotIfAttribute(string otherProperty, object condition)
{
OtherProperty = otherProperty;
Condition = condition;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(OtherProperty);
if (property == null)
{
return new ValidationResult(String.Format("Property {0} not found.", OtherProperty));
}
var propertyValue = property.GetValue(validationContext.ObjectInstance, null);
var conditionIsMet = !Equals(propertyValue, Condition);
return conditionIsMet ? base.IsValid(value, validationContext) : null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
var depProp = BuildDependentPropertyId(metadata, context as ViewContext);
var targetValue = (Condition ?? "").ToString();
if (Condition != null && Condition is bool)
{
targetValue = targetValue.ToLower();
}
rule.ValidationParameters.Add("otherproperty", depProp);
rule.ValidationParameters.Add("condition", targetValue);
yield return rule;
}
private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext)
{
var depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(OtherProperty);
var thisField = metadata.PropertyName + "_";
if (depProp.StartsWith(thisField))
{
depProp = depProp.Substring(thisField.Length);
}
return depProp;
}
}
The drawback with this - as I see it - is the magic string in the attribute "header". How can I get rid of it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您无法摆脱它,因为属性是元数据,并且值必须在编译时已知。如果您想在不使用魔术字符串的情况下进行更高级的验证,我强烈推荐您FluentValidation.NET。恕我直言,以声明方式对属性执行验证非常有限。只要看看您必须为像RequiredIf 或RequiredNotIf 这样标准且简单的东西编写的源代码数量即可。我不知道框架的设计者在选择数据注释进行验证时是怎么想的。这太荒谬了。也许将来他们会丰富它并允许更复杂的场景,但在那之前我坚持使用 FV。
You can't get rid of it because attributes are metadata and values must be known at compile time. If you want to do more advanced validation without magic strings I would very strongly recommend you FluentValidation.NET. Performing validation with attributes in a declarative manner is very limiting IMHO. Just look at the quantity of source code you have to write for something as standard and easy as RequiredIf or RequiredNotIf. I don't know what the designers of the framework were thinking when they choose Data Annotations for validation. It's just ridiculous. Maybe in the future they will enrich it and allow for more complex scenarios but until then I stick with FV.
假设您指的是其他属性名称;你不能。属性只能使用有限数量的参数类型 -
Expression
不是其中之一,因此您不能使用 lambda 技巧(无论如何这并不是一个好主意)。而且 C# 没有infoof
/memberof
运算符。所以你所拥有的只是诸如字符串之类的东西。好吧,我猜你可以添加一个非魔法密钥(即不直接与成员名称绑定的密钥),但这似乎是巨大的杀伤力,例如:
这已经删除了成员-名称,但仍然依赖于解析为另一个属性成员的键 (1),并且更加复杂。它也不太清楚,即在一个不平凡的类上,您可能必须上下扫描才能查看哪个其他成员具有匹配的标签。我不是粉丝;p
Assuming you mean the other property name; you can't. Attributes can only use a limited number of parameter types -
Expression
is not one of them, so you can't use the lambda trick (not that it would be a good idea anyway). And C# does not have ainfoof
/memberof
operator. So all you have is things like strings.Well, I guess you could add a non-magic key (i.e. one that doesn't tie directly to a member-name), but it seems massive overkill, for example:
This has removed the member-name, but still has a dependency on the key (1) resolving to another attributed member, and is more complex. It is also less clear, i.e. on a non-trivial class you might have to scan up and down to see which other member has the matching tag. I'm not a fan ;p