vb.net - 在 DataRowState.Modified 中合并两个相同的 DataTables 结果

标签 vb.net winforms datatable

假设如果两个相同的 DataTables 我错了吗?合并后每一行的状态会被保留吗?

看看这个简单的例子。它创建两个相同的表并合并 updated表带 original table 。但是返回的表在 original.GetChanges()不是 Nothing正如预期的那样。另外,original中每一行的状态表更改为 Modified .

那么我错过了什么?我真的必须创建自己的合并方法来实现这一目标吗?

Public Sub Test()

    Dim original As DataTable = Me.CreateTableWithData()
    Dim updated As DataTable = Me.CreateTableWithData()
    Dim preserveChanges As Boolean = True
    Dim msAction As MissingSchemaAction = MissingSchemaAction.Ignore

    original.Merge(updated, preserveChanges, msAction)

    Dim changes As DataTable = original.GetChanges()

    MessageBox.Show(String.Format("Count={0}", If((changes Is Nothing), 0, changes.Rows.Count)), Me.Text, MessageBoxButtons.OK, MessageBoxIcon.Information)

    If (Not changes Is Nothing) Then changes.Dispose() : changes = Nothing
    updated.Dispose() : updated = Nothing
    original.Dispose() : original = Nothing

End Sub

Private Function CreateTableWithData() As DataTable
    Dim table As New DataTable("TEST")
    table.Columns.Add("ID", GetType(Integer))
    table.Columns.Add("VALUE", GetType(String))
    table.PrimaryKey = New DataColumn() {table.Columns(0)}
    table.Rows.Add(1, "Value 1")
    table.Rows.Add(2, "Value 2")
    table.AcceptChanges()
    Return table
End Function

输出: Count=2

编辑 - 解决方法

以下代码是这种奇怪(?)行为的解决方法。
Private Shared Sub Merge(target As DataTable, source As DataTable, preserveChanges As Boolean, msa As MissingSchemaAction)

    target.Merge(source, preserveChanges, msa)

    Dim row As DataRow
    Dim column As DataColumn
    Dim acceptChanges As Boolean

    For Each row In target.Rows
        If ((row.RowState = DataRowState.Modified) AndAlso ((row.HasVersion(DataRowVersion.Original)) AndAlso (row.HasVersion(DataRowVersion.Default)))) Then
            acceptChanges = True
            For Each column In target.Columns
                If (Not Object.Equals(row.Item(column, DataRowVersion.Original), row.Item(column, DataRowVersion.Default))) Then
                    acceptChanges = False
                    Exit For
                End If
            Next
            If (acceptChanges) Then
                row.AcceptChanges()
            End If
        End If
    Next

    acceptChanges = Nothing
    column = Nothing
    row = Nothing

End Sub

最佳答案

在使用 DataTable 合并一段时间后,我找到了合并数据、保留更改并且不将所有现有行的 RowState 设置为 Modified 的最佳解决方案。

我发现如果您使用 DataTable Merge 并将 True 作为保留更改属性传递,则原始 DataTable 中的所有行都将其 RowState 设置为 Modified。如果您改为传递 false,则 RowStates 保持不变。

回到 DataTable.Merge(DataTable, Boolean, MissingSchemaAction) Method 的文档我找到了这个:

...In this scenario, the GetChanges method is first invoked. That method returns a second DataTable optimized for validating and merging. This second DataTable object contains only the DataTable and DataRow objects objects that were changed, resulting in a subset of the original DataTable...



从那里我开始意识到这个合并并不是真的打算直接与原始数据一起使用......相反,您应该对 GetChanges 方法返回的表进行合并(在保留更改时传递 true),然后合并更改表进入原始源,为保留更改参数传递 false。

为了证明这一点,我创建了以下类:
Class TableManger
Implements ComponentModel.INotifyPropertyChanged

Private _table1 As System.Data.DataTable
Private _table2 As System.Data.DataTable
Private _changesDetected As Integer = 0

Public ReadOnly Property Table1
    Get
        Return _table1
    End Get
End Property
Public ReadOnly Property ChangesDetected As Integer
    Get
        Return _changesDetected
    End Get
End Property

Public Sub New()
    _table1 = CreateTableWithData()
    _table1.AcceptChanges()

    AddHandler _table1.RowChanged, New System.Data.DataRowChangeEventHandler(AddressOf Row_Changed)
End Sub

Public Sub MergeTables()

    _table2 = _table1.Clone
    Dim tableRows As New List(Of System.Data.DataRow)
    For Each r In _table1.Rows
        Dim dr2 = _table2.NewRow
        For Each col As System.Data.DataColumn In _table1.Columns
            dr2(col.ColumnName) = r(col.ColumnName)
        Next
        _table2.Rows.Add(dr2)
        tableRows.Add(dr2)
    Next
    _table2.AcceptChanges()


    If _table2.Rows.Count > 0 Then
        _table2.Rows(0)(1) = "TB2 Changed"
    End If

    If _table1.Rows.Count > 0 Then
        '_table1.Rows(0)(1) = "TB1 Change"'
        _table1.Rows(1)(1) = "TB1 Change"
    End If

    _changesDetected = 0
    Dim perserveChanges As Boolean = True
    Dim msAction As System.Data.MissingSchemaAction = System.Data.MissingSchemaAction.Ignore

    Dim changes As System.Data.DataTable = _table1.GetChanges()
    If changes IsNot Nothing Then
        changes.Merge(_table2, perserveChanges, msAction)
        _table1.Merge(changes, False, msAction)
    Else
        _table1.Merge(_table2, False, msAction)
    End If


    MessageBox.Show(String.Format("Changes in Change Table: {0} {1}Changes Detected: {2}", If((changes Is Nothing), 0, changes.Rows.Count), System.Environment.NewLine, _changesDetected), "Testing")

    RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("Table1"))
    RaiseEvent PropertyChanged(Me, New ComponentModel.PropertyChangedEventArgs("ChangesDetected"))
End Sub

Private Sub Row_Changed(ByVal sender As Object, ByVal e As System.Data.DataRowChangeEventArgs)
    Select Case e.Action
        Case System.Data.DataRowAction.Change
            If e.Row.RowState <> System.Data.DataRowState.Unchanged Then
                _changesDetected += 1
            End If
    End Select
End Sub

Private Function CreateTableWithData() As System.Data.DataTable
    Dim newTable As New System.Data.DataTable
    Dim columnID As New System.Data.DataColumn("ID", GetType(Guid))
    Dim columnA As New System.Data.DataColumn("ColumnA", GetType(String))
    Dim columnB As New System.Data.DataColumn("ColumnB", GetType(String))
    newTable.Columns.AddRange({columnID, columnA, columnB})
    newTable.PrimaryKey = {newTable.Columns(0)}
    For i = 0 To 5
        Dim dr = newTable.NewRow
        dr("ID") = Guid.NewGuid
        dr("ColumnA") = String.Format("Column A Row {0}", i.ToString)
        dr("ColumnB") = String.Format("Column B Row {0}", i.ToString)
        newTable.Rows.Add(dr)
    Next
    Return newTable
End Function

Public Event PropertyChanged(sender As Object, e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
End Class

因此,在 MergeTables 方法中,我对 _table2 中的第一行进行了更改,并对 _table1 中的第二行进行了更改。

因为我更改了 _table1 中的第一行,所以 _table1.GetChanges 方法返回一个包含所有更改行的 DataTable(在本例中只是第一行)。

然后我将包含更改的表与 _table2 合并,并指示我要保留更改。

合并完成后,我知道结果将保留我在合并之前所做的更改,并且该表也将包含新数据(只要没有冲突)。将传入数据合并到更改表中的结果将解决数据中的任何冲突。

在我解决了这个表之后,我可以安全地合并到原始的 _table1 表中,表明保留更改 = false。因为将 false 作为保留更改参数传递不会导致原始数据的 RowState 更改,所以一切正常!我的更改被保留并且 RowStates 没有被修改!

快乐编码!

-弗林尼

关于vb.net - 在 DataRowState.Modified 中合并两个相同的 DataTables 结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21334757/

相关文章:

c# - 在鼠标单击时保持多选并执行更多功能

javascript - 如何解决过滤数据表时出现错误 "oTable.draw is not a function"?

c# - 数据表序列化没有按预期工作

.net - 如何在 VB.NET 中重命名文件以具有唯一后缀?

php - 保持 Sql 连接打开以迭代许多请求?还是关闭每一步?

asp.net - 扩展 ASP.NET Web 控件时,应在什么情况下注入(inject)额外的 Web 控件?

jsf - 是否可以使用 p :cellEditor inside a p:columns tag?

vb.net - 修改 Word 文档中嵌入的 Excel 对象

c# - 在不终止应用程序的情况下处理全局异常?

.net - 在 GDI 中绘制图层