Как правильно обновить DataTable из БД (Oracle) - PullRequest
0 голосов
/ 14 января 2019

После долгих поисков я все еще не могу найти правильный способ выполнения следующей основной задачи в .NET (может быть, я просто не могу найти что-то очень очевидное, извините, если так).

В приложении WinForms существует некоторый SQL-запрос ("select * from tab1 where col1 > 5"); Результат этого запроса сохраняется в DataTable-Object.

Через некоторое время я хочу обновить данные из БД. Так как есть PrimaryKey, я использую DataAdapter.Fill с объектом таблицы, и он обновляет значения просто отлично. Что не делает, так это удаляет строк, которых больше нет в DB-таблице.

См. Код ниже.

    Private connectionString As String = "Data Source=DBName;User Id=my_user;Password=my_pwd;"
    Private Sub LoadData(dt As DataTable, sql As String)
        Using conn = New Oracle.DataAccess.Client.OracleConnection(connectionString)
            Dim cmd = New Oracle.DataAccess.Client.OracleCommand(sql, conn)
            cmd.CommandType = CommandType.Text
            Using da = New Oracle.DataAccess.Client.OracleDataAdapter(cmd)
                conn.Open()
                da.Fill(dt)
                dt.PrimaryKey = New DataColumn() {dt.Columns(0)}
            End Using
        End Using
    End Sub

    Private dt As New DataTable
    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

        LoadData(dt, "select * from tab1 where col1 > 5")

    End Sub

EDIT1 : создание нового экземпляра DataTable или использование его Clear - Метод имеет нежелательные побочные эффекты, если объект привязан к сетке (текущая запись потеряна; необходимо перерисовывать каждую строка).

Теперь есть решение , которое я считаю обходным путем .

Если мы сначала пометим все строки в объекте DataTable как «Удаленные», а затем получим все данные из БД, он сбросит состояние существующих строк на Unchanged, оставив те, которые больше не на БД, как Deleted; AcceptChanges удаляет их из таблицы данных.

    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
        For Each r As DataRow In dt.Rows
            r.Delete()
        Next
        LoadData(dt, "select * from tab1 where col1 > 5")
        dt.AcceptChanges()

    End Sub

Это работает, но мне неприятно.

Существует также функция, называемая «Уведомление об изменении базы данных» , которая действительно хороша, потому что она может дать знать rowid добавленных / измененных / удаленных строк, но нам нужно явно выберите rowid, чтобы удалить строки из таблицы данных - что некрасиво (и не всегда легко).

Как правильно это сделать?

1 Ответ

0 голосов
/ 15 января 2019

Это может быть более фундаментальное изменение, но я думаю, что лучше не использовать DataTables, а вместо этого использовать BindingList. Когда список обновляется, все изменения автоматически отображаются в пользовательском интерфейсе.

Это довольно длинный пример. Используя этот метод, список обновляется из базы данных, но привязка сохраняется, поэтому вы не меняете источник данных в элементе управления.

Базовый класс для данных для реализации INotifyPropertyChanged:

Public Class INotifyBase
    Implements INotifyPropertyChanged

    Public Sub NotifyPropertyChanged(<CallerMemberName()> Optional ByVal propertyName As String = Nothing)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub

    Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged

End Class

В этом примере используется класс с именем User

Public Class User
    Public Sub New(userID As Integer, userName As String)
        Me.UserName = userName
        Me.UserID = userID
    End Sub

    Public Property UserID As Integer

    Public Property UserName As String

End Class

Вот список привязок класса

Public Class MyUsers
    Inherits BindingList(Of User)

    Public Sub New()
        'TODO Add database code to build list

        'Example list
        Me.Items.Add(New User(1, "Dave"))
        Me.Items.Add(New User(2, "John"))
        Me.Items.Add(New User(3, "Andrew"))

    End Sub

End Class

Создайте класс для привязки к пользовательскому интерфейсу.

Public Class UIBinders
    Inherits INotifyBase

    Private _users As MyUsers

    Property Users As MyUsers
        Get
            Return _users
        End Get
        Set(ByVal Value As MyUsers)
            If (_users Is Value) Then Return
            _users = Value
            NotifyPropertyChanged()
        End Set
    End Property

End Class

Наконец, в форме объявите класс связывателя и привяжите его к сетке. Событие нажатия кнопки загружает данные.

Public Class Form1

    Dim Binder As New UIBinders

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        DataGridView1.DataBindings.Add(New Binding("Datasource", Binder, "Users"))
    End Sub

    Private Sub bnLoadTable_Click(sender As Object, e As EventArgs) Handles bnLoadTable.Click
        Binder.Users = New MyUsers
    End Sub

End Class

Я понимаю, что это кажется многословным примером, и, возможно, он будет за бортом для вашего первоначального вопроса, но я считаю его полезным, потому что, как только строительные леса на месте, все остальное берет на себя. Также он отделяет пользовательский интерфейс от бизнес-логики.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...