【问题标题】:Merge two identical DataTables results in DataRowState.Modified在 DataRowState.Modified 中合并两个相同的 DataTables 结果
【发布时间】:2014-02-15 13:41:09
【问题描述】:

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

看看这个简单的例子。它创建两个相同的表并将updated 表与original 表合并。但是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

【问题讨论】:

  • 继续此操作,并且由于某种原因,如果您设置 preserveChanges=false 行不会在合并后设置为修改...但是,如果您指出 preserveChanges=true (您认为会保留更改)行设置为已修改。我无法重现您的问题,因为我在原始测试中设置 perserveChanges = false....将所有内容设置为“已修改”并保留更改=false 不理会它们)
  • 如果您设置 perserveChanges=False 原始表中的任何更改都将被合并的数据覆盖,但不会将行状态设置为已修改。如果您设置 perserveChanges=True 则原始数据不会被传入数据覆盖并且行状态都设置为修改由于某种原因。

标签: vb.net winforms datatable


【解决方案1】:

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

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

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

...在这种情况下,首先调用 GetChanges 方法。该方法返回为验证和合并优化的第二个 DataTable。这第二个 DataTable 对象仅包含已更改的 DataTable 和 DataRow 对象对象,从而产生原始 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 没有被修改!

编码愉快!

-弗林尼

【讨论】:

  • 你是对的!而且我不敢相信我没有阅读超载页面。谢谢!
猜你喜欢
  • 2013-10-19
  • 1970-01-01
  • 2021-10-28
  • 2017-03-07
  • 2021-06-08
  • 2014-05-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多