Здравствуйте, я борюсь с проверкой с аннотациями данных в WPF
У меня есть класс в отдельном проекте, потому что мы повторно используем этот класс в нескольких проектах. Несколько полей имеют аннотации данных для проверки.
класс проверки объединяет ошибки, возвращаемые из аннотаций данных, с некоторыми настраиваемыми бизнес-правилами, различающимися для каждого клиента.
internal class Validation<T> : ValidationRule where T : class, ICore
{
IUnitofWork unitofWork;
List<Func<T, bool>> CompiledRules = new List<Func<T, bool>>();
List<Rule<T>> Rules = new List<Rule<T>>();
Dictionary<string, List<string>> _errors = new Dictionary<string, List<string>>();
List<Func<Object, bool>> FieldRules = new List<Func<object, bool>>();
public Validation()
{
unitofWork = new UnitOfWork();
CompileRules();
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
return IsValid((T)value);
}
public ValidationResult Validate(object value,string PropertyName ,CultureInfo cultureInfo)
{
return IsValid((T)value,PropertyName);
}
void CompileRules()
{
Type t = typeof(T);
List<BusinessRule> rules = unitofWork.Repository<BusinessRule>().GetAll(b => b.Name == t.Name && b.Enabled == true);
foreach (BusinessRule b in rules)
{
Func<T, bool> CompiledRule = CompileRule(b);
CompiledRules.Add(CompiledRule);
Rule<T> rule = new Rule<T>();
rule.CompiledRule = CompiledRule;
rule.ErrorMessage = b.MessageTemplate;
rule.FieldName = b.Field;
Rules.Add(rule);
}
}
ValidationResult IsValid(T Value)
{
bool valid = true;
_errors.Clear();
if (CompiledRules.Count > 0 || Value.Errors.Count > 0)
{
//valid = CompiledRules.All(rule => rule(Value));
foreach (Rule<T> r in Rules)
{
bool isValid = r.CompiledRule(Value);
r.PassedRule = isValid;
string field = r.FieldName;
if (!isValid )
{
valid = false;
string ErrorMessage = string.Format("{0} {1}", r.FieldName, r.ErrorMessage);
if (_errors.ContainsKey(field))
_errors[field].Add(ErrorMessage);
else
{
List<string> el = new List<string>();
el.Add(ErrorMessage);
_errors.Add(field, el);
}
}
}
}
ValidateAnnotations(Value,ref valid);
return new ValidationResult(valid, _errors);
}
void ValidateAnnotations(object Value,ref bool Valid)
{
DataAnnotationValidator annotationValidator = new DataAnnotationValidator();
List< System.ComponentModel.DataAnnotations.ValidationResult> results = annotationValidator.ValidateObject(Value);
if (results.Count > 0)
{
Valid = false;
foreach (System.ComponentModel.DataAnnotations.ValidationResult r in results)
{
if (_errors.ContainsKey(r.MemberNames.First()))
{
_errors[r.MemberNames.First()].Add(r.ErrorMessage);
}
else
{
List<string> propErrors = new List<string>();
propErrors.Add(r.ErrorMessage);
_errors.Add(r.MemberNames.First(), propErrors);
}
}
}
}
void ValidateAnnotations(object Value,string PropertyName, ref bool Valid)
{
DataAnnotationValidator annotationValidator = new DataAnnotationValidator();
List<System.ComponentModel.DataAnnotations.ValidationResult> results = annotationValidator.ValidateObject(Value, PropertyName);
if (results.Count > 0)
{
Valid = false;
foreach (System.ComponentModel.DataAnnotations.ValidationResult r in results)
{
if (_errors.ContainsKey(r.MemberNames.First()))
{
_errors[r.MemberNames.First()].Add(r.ErrorMessage);
}
else
{
List<string> propErrors = new List<string>();
propErrors.Add(r.ErrorMessage);
_errors.Add(r.MemberNames.First(), propErrors);
}
}
}
}
ValidationResult IsValid(T Value, string PropertyName)
{
_errors.Remove(PropertyName);
bool valid = true;
if (CompiledRules.Count > 0)
{
//valid = CompiledRules.All(rule => rule(Value));
foreach (Rule<T> r in Rules.Where(b=>b.FieldName == PropertyName))
{
bool isValid = r.CompiledRule(Value);
r.PassedRule = isValid;
string field = r.FieldName;
// string field = "SelectedRow." + r.FieldName;
if (!isValid)
{
valid = false;
string ErrorMessage = string.Format("{0} {1}", r.FieldName, r.ErrorMessage);
if (_errors.ContainsKey(field))
_errors[field].Add(ErrorMessage);
else
{
List<string> el = new List<string>();
el.Add(ErrorMessage);
_errors.Add(field, el);
}
}
}
}
ValidateAnnotations(Value,PropertyName, ref valid);
return new ValidationResult(valid, _errors);
}
public Func<T, bool> CompileRule(BusinessRule r)
{
var paramT = Expression.Parameter(typeof(T));
Expression expression = BuildExpr(r, paramT);
return Expression.Lambda<Func<T, bool>>(expression, paramT).Compile();
}
static Expression BuildExpr(BusinessRule r, ParameterExpression param)
{
var left = MemberExpression.Property(param, r.Field);
var tProp = typeof(T).GetProperty(r.Field).PropertyType;
ExpressionType tBinary;
// is the operator a known .NET operator?
if (ExpressionType.TryParse(r.Operator, out tBinary))
{
var right = Expression.Constant(Convert.ChangeType(r.CompareValue, tProp));
// use a binary operation, e.g. 'Equal' -> 'u.Age == 15'
return Expression.MakeBinary(tBinary, left, right);
}
else
{
var method = tProp.GetMethod(r.Operator);
var tParam = method.GetParameters()[0].ParameterType;
var right = Expression.Constant(Convert.ChangeType(r.CompareValue, tParam));
// use a method call, e.g. 'Contains' -> 'u.Tags.Contains(some_tag)'
return Expression.Call(left, method, right);
}
}
}
internal class Rule<T> where T : class, ICore
{
public string FieldName
{ get;set; }
public Func<T, bool> CompiledRule
{ get; set; }
public Func<object,bool> RevisedRule
{ get; set; }
public string ErrorMessage
{ get; set; }
public bool PassedRule
{ get; set; }
}
internal class Error
{
public string ErrorMessage { get; set; }
public string FieldName { get; set; }
}
Работает, поскольку список _errors содержит имена полей и любые ошибки, связанные с ними.
как только мы их получим, мы перебираем и поднимаем onPropertyErrorsChangedEvent
internal void SetErrorDetails(ValidationResult Result)
{
propErrors = (Dictionary<string, List<string>>)Result.ErrorContent;
foreach (string key in propErrors.Keys)
{
OnPropertyErrorsChanged(key);
}
}
, на мой взгляд, 2 поля:
<TextBox Canvas.Left="138"
Canvas.Top="75"
FontFamily="Verdana"
HorizontalAlignment="Left"
Height="20"
Text="{Binding OrganizationName,ValidatesOnDataErrors=True,NotifyOnValidationError=True,ValidatesOnNotifyDataErrors=True,ValidatesOnExceptions=True}" VerticalAlignment="Top" Width="137"
Validation.ErrorTemplate="{StaticResource ValidationTemplate }"
Style="{StaticResource TextErrorStyle}"/>
<TextBox Canvas.Left="138"
Canvas.Top="225"
FontFamily="Verdana"
HorizontalAlignment="Left"
Height="20"
Text="{Binding SelectedRow.Postcode,ValidatesOnDataErrors=True,ValidatesOnNotifyDataErrors=True,ValidatesOnExceptions=True}"
VerticalAlignment="Top"
Width="137"
Validation.ErrorTemplate="{StaticResource ValidationTemplate }"
Style="{StaticResource TextErrorStyle}" />
Я сталкиваюсь с двумя проблемами, с которыми сталкиваюсь
При привязке непосредственно к выбранной строке (почтовый индекс) мои аннотации данных появляются при загрузке формы, но при привязке через поле в моей модели представления (название организации) - нет.Нам нужно связать их с полями в модели представления, чтобы бизнес-правила запускались как часть проверки.
Вторая проблема, если я сохраняю форму с недопустимой записью для организации, сохранение останавливается, поскольку оно недопустимооднако я не получаю уведомление об ошибке, даже если в свойстве _errors есть ошибка.
Я не уверен, что я делаю неправильно, может кто-нибудь указать мне правильное направление, пожалуйста?
[Редактировать] Мы используем сторонний сервис документов для создания и отображения представления
void CreateDocument(object Arg)
{
string title = string.Empty;
if (Arg.ToString().ToLower() == "edit" && SelectedRow !=null)
{
if (SelectedRow.OrganizationName != null)
title = SelectedRow.OrganizationName;
}
else
{
SelectedRow = new Address();
title = "New Address";
}
AddressDetailVM detail = new AddressDetailVM(SelectedRow,this);
Document = iInternal.CreateDocument("AddressDetails",
detail,
title
);
detail.Document = Document;
// Document = iInternal.CreateDocument("AddressDetails", null, this, title);
Document.Show();
}