2014-01-24 2 views
5

Я ошибаюсь, полагая, что если два идентичных DataTablesобъединены, состояние каждой строки будет сохранено?Объединить два идентичных результата DataTables в DataRowState.Modified

Взгляните на этот простой пример. Он создает две идентичные таблицы и объединяет таблицу 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

Edit - Обходной

Следующий код обходной путь для этого странного поведения (?).

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 
+1

Продолжение с этим и по какой-то причине, если вы установите PreserveChanges = ложь строки не приготовьтесь к модифицирован после слияния ... Однако, если вы указываете, что PreserveChanges = истина (что бы вы думали бы сохранить изменения), строки становятся модифицированными. Я не смог воспроизвести вашу проблему, потому что я устанавливал perserveChanges = false в своем первоначальном тестировании .... Я нахожу, что эта функция слияния отсутствует в документации, и она, похоже, не работает интуитивно (т.е.: save changes = true устанавливает все на «измененные» и сохраняет изменения = false оставляет их в покое) – Frinavale

+0

Если вы установите perserveChanges = False, любые изменения в исходной таблице будут перезаписаны объединенными данными, но строки не будут изменены. Если вы установите perserveChanges = True, исходные данные не будут перезаписаны входящими данными. И по каким-то причинам состояния строк настроены на изменение. – Frinavale

ответ

4

Через некоторое время работы с DataTable слиянием Я нашел лучшее решение для объединения данных, сохраняя изменения и не устанавливая RowState Модифицированного для всех существующих строк.

Что я обнаружил, так это то, что все строки в исходном DataTable должны быть установлены в RoverState, если вы используете Merge DataTable и передаете True в качестве свойства сохранения. Если вместо этого вы передадите 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, указав, что сохранить change = false. Поскольку передача false при изменении параметра сохранения приводит к отсутствию изменений в RowState для исходных данных, все работает отлично! Мои изменения сохранены, а строки RowStates не изменены!

Счастливое кодирование!

-Frinny

+0

Вы правы! И я не могу поверить, что не читал страницы перегрузки. Благодаря! –

Смежные вопросы