我正在尝试在WCF服务中进行一些验证,为此我使用WCFDataAnnotations找到this post
问题是它不会递归验证,因此对于嵌套对象,它不起作用。让我们说这个
[DataContract]
public class Model
{
[DataMember]
[Required(ErrorMessage = "RequiredOne is required")]
public string RequiredOne { get; set; }
[DataMember]
[StringLength(10, ErrorMessage = "Not Required should be at most 10 characters long")]
public string NotRequired { get; set; }
[DataMember]
[Required(ErrorMessage = "ChildModel is required")]
public ChildModel ChildModel { get; set; }
}
[DataContract]
public class ChildModel
{
[DataMember]
[Required(ErrorMessage = "RequiredValue is required")]
public string RequiredValue { get; set; }
[DataMember]
public string NotRequiredValue { get; set; }
}
它不会将childModel RequiredValue精确地视为必需。
所以我正在查看该DLL的源代码并尝试使其工作。实际代码是
public class DataAnnotationsObjectValidator : IObjectValidator
{
public IEnumerable<ValidationResult> Validate(object input)
{
if (input == null) return Enumerable.Empty<ValidationResult>();
return from property in TypeDescriptor.GetProperties(input).Cast<PropertyDescriptor>()
from attribute in property.Attributes.OfType<ValidationAttribute>()
where !attribute.IsValid(property.GetValue(input))
select new ValidationResult
(
attribute.FormatErrorMessage(string.Empty),
new[] { property.Name }
);
}
}
所以我的想法正在改变这样的事情
public IEnumerable<ValidationResult> Validate(object input)
{
if (input == null) return Enumerable.Empty<ValidationResult>();
var validationResults = new List<ValidationResult>();
foreach (var prop in TypeDescriptor.GetProperties(input).Cast<PropertyDescriptor>())
{
foreach (var att in prop.Attributes.OfType<ValidationAttribute>())
{
//This doesn't work, it's one of the several
//attempts I've made
if (prop.ComponentType.IsClass)
Validate(prop.ComponentType);
if (!att.IsValid(prop.GetValue(input)))
{
validationResults.Add(new ValidationResult(
att.FormatErrorMessage(string.Empty),
new[] { prop.Name }
));
}
}
}
return validationResults;
}
目的是检查任何属性是否是复杂属性,如果是这种情况递归验证自己,但我不知道如何检查“如果将”props“转换为TypeDescriptors。
由于
答案 0 :(得分:1)
在我看来,以下代码可以解决这个问题:
public IEnumerable<ValidationResult> Validate(object input)
{
return ValidateWithState(input, new HashSet<object>());
}
private IEnumerable<ValidationResult> ValidateWithState(object input, HashSet<object> traversedInputs)
{
if (input == null || traversedInputs.Contains(input))
{
return Enumerable.Empty<ValidationResult>();
}
var validationResults = new List<ValidationResult>();
foreach (var prop in TypeDescriptor.GetProperties(input).Cast<PropertyDescriptor>())
{
foreach (var att in prop.Attributes.OfType<ValidationAttribute>())
{
if (!att.IsValid(prop.GetValue(input)))
{
validationResults.Add(new ValidationResult(
att.FormatErrorMessage(string.Empty),
new[] { prop.Name }
));
}
traversedInputs.Add(input);
if (prop.PropertyType.IsClass || prop.PropertyType.IsInterface))
{
validationResults.AddRange(ValidateWithState(prop.GetValue(input), traversedInputs));
}
}
return validationResults;
}
可能不是最优雅的解决方案,但我认为它会起作用。
答案 1 :(得分:0)
现在我能够验证公共List ChildModel。 参考DevTrends.WCFDataAnnotations,ValidatingParameterInspector.cs类, (http://wcfdataannotations.codeplex.com/SourceControl/latest#DevTrends.WCFDataAnnotations/ValidatingParameterInspector.cs)。
我确信可以进一步修改ValidateCollection来检查ChildModel中的集合。目前它只检查一个级别。
我的例子,
[DataContract]
public class Model
{
[DataMember]
[Required(ErrorMessage = "RequiredOne is required")]
public string RequiredOne { get; set; }
[DataMember]
[StringLength(10, ErrorMessage = "Not Required should be at most 10 characters long")]
public string NotRequired { get; set; }
[DataMember]
[Required(ErrorMessage = "ChildModel is required")]
public List<ChildModel> ChildModel { get; set; }
}
原始代码不验证List,所以我创建了另一个函数ValidateCollection 操作object []输入以提取每个ChildModel类并将其放回object []输入,如模型类驻留在object []输入中。
public object BeforeCall(string operationName, object[] inputs)
{
var validationResults = new List<ValidationResult>(); ErrorMessageGenerator.isValidationFail = false;
ErrorMessageGenerator.ErrorMessage = string.Empty;
***inputs=ValidateCollection( operationName, inputs);***
foreach (var input in inputs)
{
foreach (var validator in _validators)
{
var results = validator.Validate(input);
validationResults.AddRange(results);
}
}
if (validationResults.Count > 0)
{
return _errorMessageGenerator.GenerateErrorMessage(operationName, validationResults);
}
return null;
}
private object[] ValidateCollection(string operationName, object[] inputs)
{
object[] inputs1 = inputs;
try
{
foreach (var input in inputs)
{
foreach (var property in input.GetType().GetProperties())
{
IEnumerable enumerable = null;
if (property.PropertyType.Name.Contains("List"))
{
enumerable = property.GetValue(input, null) as IEnumerable;
int j = 0;
object[] o1 = new object[inputs.Count() + enumerable.OfType<object>().Count()];
for (int k = 0; k < inputs.Count(); k++)
{
o1[k] = inputs[k];
}
foreach (var item in enumerable)
{
o1[inputs.Count() + j] = item;
j = j + 1;
if (j == (o1.Length - inputs.Count()))
inputs = o1;
}
}
}
}
return inputs;
}
catch
{
return inputs1;
}
}