如何从数据库(Oracle)正确刷新DataTable

时间:2019-01-14 16:03:42

标签: .net winforms odp.net data-access

经过广泛的挖掘,我仍然找不到在.NET中执行基本任务的正确方法(也许我只是找不到非常明显的东西,如果可以,抱歉)。

在WinForms应用程序中,存在一些SQL查询("select * from tab1 where col1 > 5")。该查询的结果存储在DataTable对象中。

一段时间后,我想刷新数据库中的数据。因为有一个PrimaryKey,所以我将DataAdapter.Fill与表对象一起使用,它确实更新了值。 不执行的操作是删除数据库表中不再存在的行。

请参见下面的代码。

    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

这行得通,但对我来说却很丑。

还有一个名为"Database Change Notification"的功能,它非常不错,因为它可以让已添加/已修改/已删除的行的rowid知道,但是我们需要明确选择{{ 1}}从DataTable中删除行,这很丑陋(而且并不总是那么容易)。

执行此操作的正确方法是什么?

1 个答案:

答案 0 :(得分:0)

这可能是更根本的变化,但是我认为最好不要使用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

创建一个类以绑定到UI。

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

我意识到这似乎是一个漫长的例子,对于您最初的问题可能有些过头,但是我发现它很有用,因为一旦安装了脚手架,其他所有事情都可以解决。还将UI与业务逻辑分开。