使用MVVM-Light WPF和Linq to Entity Framework进行数据验证

时间:2013-01-19 07:20:15

标签: wpf mvvm linq-to-entities mvvm-light

我想当我搜索wpf mvvm-light数据验证时,我已阅读google返回的每篇文章,我不知道要走哪条路。我知道josh smith,Karl Shifflett和MVVM LIGHT自己的数据验证演示技术。我看到的是,大多数验证都要求我在视图模型中完全“重新抽象”我的模型。这意味着我必须在我的viewmodel中为我想验证的模型的每个属性创建一个属性(在某些情况下,将所有这些属性转换为字符串值以进行绑定/验证)。当我想要做的就是根据需要标记大多数字段时,这似乎很多或冗余。

我正在为来自SQL Server DB的模型类使用LINQ to实体框架(具有自我跟踪)。因此,我更愿意在我的视图模型中保留业务数据验证/规则。我编写了一个简单的服务接口来从模型中获取数据并将其传递给我的viewmodel。

我能找到的大部分例子都是早在2008年(即乔什史密斯)。这些技术是否仍然有效,或者是否有更新的使用.NET 4.5等进行mvvm数据验证的最新实践。

所以我在问:

1)您建议我使用哪种方法 2)哪些方法在具有MVVM-Light环境的LINQ到EF中最有效。 3)编辑:我想在用户输入数据时向用户提供反馈,而不仅仅是在他们提交表单时

感谢

2 个答案:

答案 0 :(得分:0)

我这样做(不一定正确)的方法是在ViewModel(通常进行CRUD操作)中进行验证,然后如果存在验证错误,则中止保存/添加任何数据并使用{{1} }将自定义消息类型发送到我的视图。然后我通过DialogBox或其他方式提醒用户。

我过去曾尝试使用Binding ValidationRules,但迄今为止发现最简单Messenger.Default.Send语句的最可靠和一致的方法。

答案 1 :(得分:0)

我最终使用了以下内容。我改变了我的模型以使用LINQ来自我跟踪实体(有关STE http://msdn.microsoft.com/en-us/library/vstudio/ff407090%28v=vs.100%29.aspx的信息,请参阅此文章)。

LINQ to STE创建一个实现iNotifyPropertyChanged接口的OnPropertyChanged事件。

我刚为匹配的模型对象(linq实体生成的代码)创建了一个公共的部分类,我想要为OnPropertyChanged事件添加一个事件处理程序。然后我使用IDataErrorInfo接口来验证并根据需要抛出错误。这允许我验证字段,因为它们会反映给用户。这也允许您执行更高级的验证逻辑,可能需要重新查询数据库(即查找是否已使用用户名等)或抛出对话框

此外,如果我执行绕过UI的直接“批量”操作,那么在模型中进行数据验证后,我仍然可以进行验证。

然后我使用HasErrorsHasChanges属性并使用它们创建一个附加到中继命令的布尔值,如果存在错误则禁用crud命令按钮。

我将发布一些简单的代码来概述我刚才描述的内容,如果你想了解更多细节,请发表评论。

以下是模型类的实体框架扩展:

 Imports System.ComponentModel


Partial Public Class client

    Implements IDataErrorInfo

#Region "Properties / Declarations"

    'Collection / error description
    Private m_validationErrors As New Dictionary(Of String, String)
    Private _HasChanges As Boolean = False

    ''Marks object as dirty, requires saving
    Public Property HasChanges() As Boolean
        Get
            Return _HasChanges
        End Get
        Set(value As Boolean)
            If Not Equals(_HasChanges, value) Then
                _HasChanges = value
                OnPropertyChanged("HasChanges")
            End If
        End Set
    End Property

    'Extends the class with a property that determines
    'if the instance has validation errors
    Public ReadOnly Property HasErrors() As Boolean
        Get
            Return m_validationErrors.Count > 0
        End Get
    End Property

#End Region

#Region "Base Error Objects"
    'Returns an error message
    'In this case it is a general message, which is
    'returned if the list contains elements of errors
    Public ReadOnly Property [Error] As String Implements System.ComponentModel.IDataErrorInfo.Error
        Get
            If m_validationErrors.Count > 0 Then
                Return "Client data is invalid"
            Else
                Return Nothing
            End If
        End Get
    End Property

    Default Public ReadOnly Property Item(ByVal columnName As String) As String Implements System.ComponentModel.IDataErrorInfo.Item
        Get
            If m_validationErrors.ContainsKey(columnName) Then
                Return m_validationErrors(columnName).ToString
            Else
                Return Nothing
            End If
        End Get
    End Property

#End Region

#Region "Base Error Methods"

    'Adds an error to the collection, if not already present
    'with the same key
    Private Sub AddError(ByVal columnName As String, ByVal msg As String)
        If Not m_validationErrors.ContainsKey(columnName) Then
            m_validationErrors.Add(columnName, msg)
        End If
    End Sub

    'Removes an error from the collection, if present
    Private Sub RemoveError(ByVal columnName As String)
        If m_validationErrors.ContainsKey(columnName) Then
            m_validationErrors.Remove(columnName)
        End If
    End Sub

#End Region

    Public Sub New()

        Me.HasChanges = False
    End Sub

#Region "Data Validation Methods"

    ''handles event and calls function that does the actual validation so that it can be called explicitly for batch processes
    Private Sub ValidateProperty(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Handles Me.PropertyChanged
        If e.PropertyName = "HasChanges" Then
            Exit Sub
        End If
        IsPropertyValid(e.PropertyName)
        HasChanges = True
    End Sub

    Public Function IsPropertyValid(sProperty As String) As Boolean
        Select Case sProperty
            ''add validation by column name here
            Case "chrLast"
                If Me.chrLast.Length < 4 Then
                    Me.AddError("chrLast", "The last name is too short")
                    Return True
                Else
                    Me.RemoveError("chrLast")
                    Return False
                End If
            Case Else
                Return False

        End Select

    End Function

#End Region

End Class

然后在视图模型中我包含了以下代码来绑定命令并评估它是否可以执行。

 Public ReadOnly Property SaveCommand() As RelayCommand
        Get
            If _SaveCommand Is Nothing Then
                _SaveCommand = New RelayCommand(AddressOf SaveExecute, AddressOf CanSaveExecute)
            End If
            Return _SaveCommand
        End Get
    End Property

    Private Function CanSaveExecute() As Boolean
        Try
            If Selection.HasErrors = False And Selection.HasChanges = True Then
                Return True
            Else
                Return False
            End If
        Catch ex As Exception
            Return False
        End Try

    End Function

    Private Sub SaveExecute()
        ''this is my LINQ to Self Tracking Entities DataContext
        FTC_Context.SaveChanges()
    End Sub

以下是我如何绑定我的按钮(在WPF中具有自定义样式)

 <Button Content="" Height="40" Style="{DynamicResource ButtonAdd}" Command="{Binding SaveCommand}" Width="40" Cursor="Hand" ToolTip="Save Changes" Margin="0,0,10,10"/>

因此,当没有验证错误且当前客户端记录“isDirty”时,保存按钮会自动启用,如果这两个条件中的任何一个失败,则禁用。这样我现在有一种简单的方法可以验证我想要的任何类型的列/数据,并且我可以在表单中输入数据时提供用户反馈,并且只有在我的所有“条件”出现后才启用CRUD命令按钮满足。

这是一场很难找到的战斗。