视图模型实现IDataErrorInfo接口来进行验证是个好主意吗?

7
我有一个应用程序使用MVVM模式,我想在用户填写信息时实现验证。我想使用IDataErrorInfo,但我不知道我的视图模型是否实现该接口是一个好主意,还是更好地创建一个新类。怎样才是在MVVM模式下使用IDataErrorInfo来实现验证的最佳方法?
编辑:我看到一些示例中的实现位于模型中(不同于视图模型),但在我的情况下,模型基本上是从我的数据库创建的POCO实体,当我使用Entity Framework创建我的edmx模型时,因此我想避免修改这些实体,因为如果我不更新我的模型,我将不得不再次进行这项工作。
谢谢。

3
我认为VM实现IDataErrorInfo是一个非常好的想法。这个接口可以很好地“协助”将后台错误(数据错误)信息传递给用户(视图)。这正是VM的作用,让它在那里是完全有效的。此答案中的评论和下面的评论主张在Model中使用它。虽然将其定义在另一个类中而不是Model和VM中可能也并没有“错”,但有可能并不值得这样做。 - Viv
1
如果您使用 IDataErrorInfo 进行检查的内容是非常复杂的逻辑,并且您想要重复使用它,那么我会将这些功能检查字面上转移到服务中,然后在 VM 中实现该接口并使用该服务访问复杂的验证逻辑。这样一来,您就可以实现代码重用和共享,同时每个 VM 只需自己实现 IDataErrorInfo,保持代码整洁简单。 - Viv
4个回答

2
如果您有一个实体或自定义类型(如Person、Student等)来保存数据,那么在实体或自定义类型中实现IDataErrorInfo是必须的。假设您有一个视图允许输入学生数据,您有它的ViewModel StudentViewModel,这个ViewModel有一个类型为Student的属性Student,其属性(如Name、Age等)绑定到View的控件。为了使验证生效并在UI上反映更改,您必须在Student类中实现IDataErrorInfo,而不是在ViewModel中,并且您还必须在该类中实现INotifyPropertyChanged接口。因此,我的理解是,如果您的ViewModel中有一些属性是string和ValueTypes类型,并且绑定到View上,您想对它们应用验证,则必须在ViewModel中实现IDataErrorInfo。如果您有自定义类型/实体,则必须在这些类中实现接口,而不是在ViewModel中实现。

如果您希望验证生效,则必须在绑定到View的控件的EndProperties(例如Student的Name、Age)所在的类中实现IDataErrorInfo和INotifyPropertyChanged。

    //Interface which has fields of Student class on which ValidationAttribute are to be applied
public interface IStudent
{
    [Required(ErrorMessage = "Name is required")]
    string Name { get; set; }

    [Range(10, 90, ErrorMessage = "Age should be between 10 and 90")]
    int Age { get; set; }
}
//Partial Class to implement IStudent
public partial class Student : IStudent
{

}

//POCO
public partial class Student : INotifyPropertyChanged
{
    private string name;
    private int age;

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;

            Notify("Name");
        }
    }

    public int Age
    {
        get
        {
            return age;
        }
        set
        {
            age = value;

            Notify("Age");
        }
    }
    private void Notify(string propName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

我理解这个想法。那么,我如何在我的POCO实体中实现验证呢?这些实体是由T4模板从我的edmx模型生成的,该模型是从我的数据库创建的。如果我更新了我的数据库并且需要重新创建实体,则会丢失所有更改(在这种情况下是验证)。有没有办法在从数据库生成的实体中实现验证? - Álvaro García
你是否正在使用基于属性的验证? - yo chauhan
目前我没有使用任何类型的验证,我正在尝试决定最佳选项。 - Álvaro García
1
应用验证 Validations 到使用 T4 模板生成的 POCO 并不是一件容易的工作。几个月前我也遇到了同样的问题,但是通过更新代码并创建 ValidationAttributes 的方式解决了它。 - yo chauhan
1
我创建了一个名为POCO的部分类,然后创建了一个Partial类来扩展该POCO,并创建了一个接口,其中包含要应用验证的POCO字段,并在这些接口成员上应用了属性。希望你明白我的想法。 - yo chauhan

2

我同意大多数关于这个主题的评论,但是我想提供我的“升级”界面。

我认为 IDataErrorInfo 接口的问题在于它只能一次处理一个错误。因此,在我的 BaseDataType 类中(所有数据类型的基类)添加了一个额外的字段:

protected ObservableCollection<string> errors = new ObservableCollection<string>();

我随后添加了以下属性:
// this just enables me to add into the error collection from outside this class
public ObservableCollection<string> ExternalErrors
{
    get { return externalErrors; }
}

public override ObservableCollection<string> Errors
{
    get
    {
        errors = new ObservableCollection<string>();
        // add properties to validate
        errors.AddUniqueIfNotEmpty(this["Property1ToValidate"]);
        errors.AddUniqueIfNotEmpty(this["Property2ToValidate"]);
        errors.AddUniqueIfNotEmpty(this["Property3ToValidate"]);
        // add external errors (from view models)
        errors.AddRange(ExternalErrors);
        return errors;
    }
}

public virtual bool HasError
{
    get { return Errors != null && Errors.Count > 0; }
}
< p > AddUniqueIfNotEmpty 方法是一个扩展方法,我相信你们都能猜到它的作用。

使用它,我可以直接绑定到视图中错误的集合,更好的是,使用 BoolToVisibilityConverter 绑定到 HasError 属性,以便在集合为空时隐藏显示错误的控件。


2

将验证逻辑与UI分离始终是一个好主意。这样,使用IDataErrorInfo是正确的选择。

在视图模型和模型之间,我更喜欢在视图模型上实现IDataErrorInfo,因为该接口由UI使用。您可以通过直接调用索引器在测试代码中模拟UI,但如果您真的需要在业务逻辑层中使用验证逻辑,这样的调用就没有太多意义。

在我们的项目中,验证是一个更独立的组件,可以通过配置被呈现层和业务逻辑层同时使用。从视图模型的角度来看,它非常轻量级,只包含一个调用并在索引器内构造验证结果。

另外,还要考虑到由.Net 4.5和Silverlight提供的INotifyDataErrorInfo。它可以从一个属性提供更多的验证结果,并提供耗时验证的异步验证,这正是我们计划升级到.Net 4.5后想要的。

希望这可以帮助您。


1
<TextBox Text="{Binding Path=MyCoolProperty, ValidationOnDataErrors=true}"

也许我漏掉了什么,但如果你有这样的绑定 - 你的" MyCoolProperty "类必须实现INotifyPropertyChanges和IDataErrorInfo - 否则它将无法工作。
所以我想说的问题不是:"应该实现IDataErrorInfo",而是如何实现IDataErrorInfo。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接