Проверка WPF с аннотациями данных - PullRequest
0 голосов
/ 23 октября 2018

Здравствуйте, я борюсь с проверкой с аннотациями данных в 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();
    }
...