Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何为多个BO属性定义IDataErrorInfo错误属性_C#_Wpf_Mvvm_Idataerrorinfo - Fatal编程技术网

C# 如何为多个BO属性定义IDataErrorInfo错误属性

C# 如何为多个BO属性定义IDataErrorInfo错误属性,c#,wpf,mvvm,idataerrorinfo,C#,Wpf,Mvvm,Idataerrorinfo,我开始通过IDataErrorInfo接口在我的WPF项目中实现验证。 我的业务对象包含多个具有验证信息的属性。如何获取与对象关联的所有错误消息的列表。我的想法是,这就是Error属性的用途,但是我无法找到任何使用它来报告多个属性的人 谢谢 public string this[string property] { get { string msg = null; switch (property)

我开始通过IDataErrorInfo接口在我的WPF项目中实现验证。 我的业务对象包含多个具有验证信息的属性。如何获取与对象关联的所有错误消息的列表。我的想法是,这就是Error属性的用途,但是我无法找到任何使用它来报告多个属性的人

谢谢

public string this[string property]
    {
        get {

            string msg = null;
            switch (property)
            {
                case "LastName":
                    if (string.IsNullOrEmpty(LastName))
                        msg = "Need a last name";
                    break;
                case "FirstName":
                    if (string.IsNullOrEmpty(LastName))
                        msg = "Need a first name";
                    break;

                default:
                    throw new ArgumentException(
                        "Unrecognized property: " + property);
            }
            return msg;

        }
    }

    public string Error
    {
        get
        {
            return null ;
        }
    }

我的理解是,要使用这个接口,需要枚举对象上的属性,并为每个属性调用索引器一次。呼叫方负责汇总任何错误消息。

是的,我知道您可以在哪里使用索引器。我想这是个不错的选择。不过,我真正关注的是“错误”属性。我喜欢将错误包含在业务对象中的概念。我认为我想做的事情并不存在,所以我只是在对象上创建了一个错误字典(在属性更改时更新),让错误返回一个以回车分隔的错误列表,如下所示:

    public string this[string property]
    {
        get {

            string msg = null;
            switch (property)
            {
                case "LastName":
                    if (string.IsNullOrEmpty(LastName))
                       msg = "Need a last name";
                    break;
                case "FirstName":
                    if (string.IsNullOrEmpty(FirstName))
                        msg = "Need a first name";
                    break;
                default:
                    throw new ArgumentException(
                        "Unrecognized property: " + property);
            }

            if (msg != null && !errorCollection.ContainsKey(property))
                errorCollection.Add(property, msg);
            if (msg == null && errorCollection.ContainsKey(property))
                errorCollection.Remove(property);

            return msg;
        }
    }

    public string Error
    {
        get
        {
            if(errorCollection.Count == 0)
                return null;

            StringBuilder errorList = new StringBuilder();
            var errorMessages = errorCollection.Values.GetEnumerator();
            while (errorMessages.MoveNext())
                errorList.AppendLine(errorMessages.Current);

            return errorList.ToString();
        }
    }

我认为使用验证属性要容易得多

class MyBusinessObject {
    [Required(ErrorMessage="Must enter customer")]
    public string Customer { get; set; }

    [Range(10,99, ErrorMessage="Price must be between 10 and 99")]
    public decimal Price { get; set; }

    // I have also created some custom attributes, e.g. validate paths
    [File(FileValidation.IsDirectory, ErrorMessage = "Must enter an importfolder")]
    public string ImportFolder { get; set; }

    public string this[string columnName] {
        return InputValidation<MyBusinessObject>.Validate(this, columnName);
    }

    public ICollection<string> AllErrors() {
        return InputValidation<MyBusinessObject>.Validate(this);
    }
}
类MyBusinessObject{
[必需(ErrorMessage=“必须输入客户”)]
公共字符串Customer{get;set;}
[范围(10,99,ErrorMessage=“价格必须介于10和99之间”)]
公共十进制价格{get;set;}
//我还创建了一些自定义属性,例如验证路径
[文件(FileValidation.IsDirectory,ErrorMessage=“必须输入导入文件夹”)]
公共字符串导入文件夹{get;set;}
公共字符串此[string columnName]{
返回InputValidation.Validate(这个,columnName);
}
公共i收集所有错误(){
返回InputValidation.Validate(此);
}
}
助手类InputValidation如下所示

internal static class InputValidation<T>
    where T : IDataErrorInfo
{
    /// <summary>
    /// Validate a single column in the source
    /// </summary>
    /// <remarks>
    /// Usually called from IErrorDataInfo.this[]</remarks>
    /// <param name="source">Instance to validate</param>
    /// <param name="columnName">Name of column to validate</param>
    /// <returns>Error messages separated by newline or string.Empty if no errors</returns>
    public static string Validate(T source, string columnName) {
       KeyValuePair<Func<T, object>, ValidationAttribute[]> validators;
       if (mAllValidators.TryGetValue(columnName, out validators)) {
           var value = validators.Key(source);
           var errors = validators.Value.Where(v => !v.IsValid(value)).Select(v => v.ErrorMessage ?? "").ToArray();
           return string.Join(Environment.NewLine, errors);
       }
       return string.Empty;
    }

    /// <summary>
    /// Validate all columns in the source
    /// </summary>
    /// <param name="source">Instance to validate</param>
    /// <returns>List of all error messages. Empty list if no errors</returns>
    public static ICollection<string> Validate(T source) {
        List<string> messages = new List<string>();
        foreach (var validators in mAllValidators.Values) {
            var value = validators.Key(source);
            messages.AddRange(validators.Value.Where(v => !v.IsValid(value)).Select(v => v.ErrorMessage ?? ""));
        }
        return messages;
    }

    /// <summary>
    /// Get all validation attributes on a property
    /// </summary>
    /// <param name="property"></param>
    /// <returns></returns>
    private static ValidationAttribute[] GetValidations(PropertyInfo property) {
        return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true);
    }

    /// <summary>
    /// Create a lambda to receive a property value
    /// </summary>
    /// <param name="property"></param>
    /// <returns></returns>
    private static Func<T, object> CreateValueGetter(PropertyInfo property) {
        var instance = Expression.Parameter(typeof(T), "i");
        var cast = Expression.TypeAs(Expression.Property(instance, property), typeof(object));
        return (Func<T, object>)Expression.Lambda(cast, instance).Compile();
    }

    private static readonly Dictionary<string, KeyValuePair<Func<T, object>, ValidationAttribute[]>>  mAllValidators;

    static InputValidation() {
        mAllValidators = new Dictionary<string, KeyValuePair<Func<T, object>, ValidationAttribute[]>>();
        foreach (var property in typeof(T).GetProperties()) {
            var validations = GetValidations(property);
            if (validations.Length > 0)
                mAllValidators.Add(property.Name,
                       new KeyValuePair<Func<T, object>, ValidationAttribute[]>(
                         CreateValueGetter(property), validations));
        }       
    }
}
内部静态类InputValidation
其中T:IDataErrorInfo
{
/// 
///验证源中的单个列
/// 
/// 
///通常从IErrorDataInfo调用。此[]
///要验证的实例
///要验证的列的名称
///由换行符或字符串分隔的错误消息。如果没有错误,则为空
公共静态字符串验证(T源,字符串列名称){
键值对验证器;
if(mAllValidators.TryGetValue(columnName,out validators)){
var值=validators.Key(源);
var errors=validators.Value.Where(v=>!v.IsValid(Value))。选择(v=>v.ErrorMessage???).ToArray();
返回string.Join(Environment.NewLine,errors);
}
返回字符串。空;
}
/// 
///验证源中的所有列
/// 
///要验证的实例
///所有错误消息的列表。如果没有错误,则为空列表
公共静态ICollection验证(T源){
列表消息=新列表();
foreach(mAllValidators.Values中的var验证器){
var值=validators.Key(源);
messages.AddRange(validators.Value.Where(v=>!v.IsValid(Value))。选择(v=>v.ErrorMessage??);
}
返回消息;
}
/// 
///获取属性上的所有验证属性
/// 
/// 
/// 
私有静态验证属性[]GetValidations(PropertyInfo属性){
return(ValidationAttribute[])属性。GetCustomAttributes(typeof(ValidationAttribute),true);
}
/// 
///创建lambda以接收属性值
/// 
/// 
/// 
私有静态Func CreateValueGetter(PropertyInfo属性){
var instance=Expression.Parameter(typeof(T),“i”);
var cast=Expression.TypeAs(Expression.Property(实例,属性),typeof(对象));
return(Func)Expression.Lambda(cast,instance.Compile();
}
私有静态只读字典验证器;
静态输入验证(){
mAllValidators=新字典();
foreach(typeof(T).GetProperties()中的var属性){
var validations=GetValidations(属性);
如果(validations.Length>0)
mallvalidates.Add(property.Name,
新的KeyValuePair(
CreateValueGetter(属性),验证);
}       
}
}

谢谢。你说得对。我一直在寻找一种更能封装在业务对象中的解决方案,见上文。也许不是分离关注点的最完美解决方案。这是另一个好的解决方案,谢谢。它在一个VM属性上对我有效,而标准的IDataErrorInfo没有。如果有人使用它,需要添加对System.ComponentModel.DataAnnotations.this的引用。这非常好-来自MVC世界,能够使用相同的约定非常好。这很好。最好使errorCollection与最新的错误消息保持最新(如果密钥已存在,且消息不为null)。
error
属性可以优化为:
get{return string.Join(Environment.NewLine,errorCollection.Values);}
,如果为空,则只返回“”,即(如接口文档中所述)不视为错误。