我正在尝试对没有绑定到UI的对象执行某些验证。例如,我有这三个类:
public class XDeftable {
[ObjectCollectionValidator(typeof(XSchedGroup))]
public List<XSchedGroup> SCHED_GROUP { get; set; }
}
[IdentifyingProperty("TABLE_NAME")]
public class XSchedGroup {
[ObjectCollectionValidator(typeof(XJob))]
public List<XJob> JOB { get; set; }
[Required]
public string TABLE_NAME { get; set; }
}
[IdentifyingProperty("JOBNAME")]
public class XJob : ICalendar {
[Required]
public string JOBNAME { get; set; }
[Range(-62, 62)]
public string SHIFTNUM { get; set; }
[ObjectCollectionValidator(typeof(XTagNames))]
public List<XTagNames> TAG_NAMES { get; set; }
}
XDeftable - &gt; XSchedGroup - &gt; XJob - &gt; XTagNames
当一个对象验证失败时,事情就像人们期望的那样工作,但如果我只是检查ValidationResult的 Key 和 Message ,我最终得到的结果如下:“JOBNAME |字段是必需的。”
问题在于,考虑到我可能在一个调度组中有数百个作业,验证是无用的,因为我不知道哪个特定的作业失败了。我搜索了有关验证和C#的所有文档,但没有找到任何获取更多数据的方法。我创建了属性IdentifyingProperty,以允许我标记类的哪个属性标识类的特定实例。我有一个以前的自定义验证解决方案,我根据这个Git Repo嘲笑:https://github.com/reustmd/DataAnnotationsValidatorRecursive/tree/master/DataAnnotationsValidator/DataAnnotationsValidator。它工作正常,但我想交换更强大的东西。
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class IdentifyingProperty : Attribute {
public string Name { get; set; }
public IdentifyingProperty(string name) {
this.Name = name;
}
}
到目前为止,我已经能够提出以下建议:
public ValidationResults Validate(XDeftable deftable) {
var results = new ObjectValidator(typeof(XDeftable)).Validate(deftable);
var detailedResults = new ValidationResults();
foreach (var item in results) {
var targetType = item.Target.GetType();
var identProp = targetType.GetCustomAttribute<IdentifyingProperty>();
if (identProp != null) {
var pi = targetType.GetProperty(identProp.Name);
var newKey = String.Format("{0}[{1}].{2}", targetType.Name, pi.GetValue(item.Target).ToString(), item.Key);
detailedResults.AddResult(new ValidationResult(item.Message, item.Target, newKey, item.Tag, item.Validator));
}
else {
detailedResults.AddResult(item);
}
}
return detailedResults;
}
这至少会让我“XJob [JOBNAME] .SHIFTNUM |字段SHIFTNUM必须在-62到62之间。”如果我有办法,我仍然会喜欢它获得跟随容器链的结果,例如: XSchedGroup [TABLE_NAME] .XJob [JOBNAME] .SHIFTNUM 。
答案 0 :(得分:1)
我不会操纵Key,而是捎带Tag属性,因为这就是它的用途(“标记的含义由消耗ValidationResults的客户端代码决定”)。
坚持你的方法,如:
public ValidationResults Validate(XDeftable deftable)
{
var results = new ObjectValidator(typeof(XDeftable)).Validate(deftable);
var detailedResults = new ValidationResults();
Microsoft.Practices.EnterpriseLibrary.Validation.ValidationResult result = null;
foreach (var item in results)
{
result = item;
var targetType = item.Target.GetType();
var attribute = (IdentifyingPropertyAttribute)
targetType.GetCustomAttributes(
typeof(IdentifyingPropertyAttribute),
false)
.SingleOrDefault();
if (attribute != null)
{
var propertyInfo = targetType.GetProperty(attribute.Name);
if (propertyInfo != null)
{
object propertyValue = propertyInfo.GetValue(item.Target) ?? "";
result = new Microsoft.Practices.EnterpriseLibrary.Validation.ValidationResult(
item.Message,
item.Target,
item.Key,
propertyValue.ToString(),
item.Validator);
}
}
detailedResults.AddResult(result);
}
return detailedResults;
}
答案 1 :(得分:0)
我曾经使用类似的东西
public class RequiredPropertyAttribute : Attribute
{
public bool Required { get { return true; } }
}
public class RequiredListAttribute : Attribute
{
public bool Required { get { return true; } }
}
以及以下用于验证。它检查我指定的属性,并返回未填充的那些属性。
public List<string> IterateProperties(object _o)
{
List<string> problems = new List<string>();
foreach (PropertyInfo info in _o.GetType().GetProperties())
{
bool isGenericType = info.PropertyType.IsGenericType;
Type infoType = info.PropertyType;
if (infoType.IsGenericType && infoType.GetGenericTypeDefinition() == typeof(List<>))
{
infoType = infoType.GetGenericArguments()[0];
if (infoType.IsNested)
{
System.Collections.IList subObjects = (System.Collections.IList)info.GetValue(_o, null);
object[] requiredListAttributes = info.GetCustomAttributes(typeof(RequiredListAttribute), true);
if (requiredListAttributes.Length > 0 && subObjects.Count == 0)
{
problems.Add(String.Format("List {0} in class {1} must have at least 1 row", info.Name, info.PropertyType.ToString()));
}
else
{
foreach (object sub in subObjects)
{
problems.AddRange(this.IterateProperties(sub));
}
}
}
}
else
{
if (infoType.IsNested)
{
object sub = info.GetValue(_o, null);
if (sub != null)
{
problems.AddRange(this.IterateProperties(sub));
}
}
}
object[] attributes = info.GetCustomAttributes(typeof(RequiredPropertyAttribute), true);
foreach (object o in attributes)
{
if (info.GetValue(_o, null) == null)
{
problems.Add(String.Format("Attribute {0} in class {1} cannot be null", info.Name, info.PropertyType.ToString()));
}
}
}
return problems;
}
}