经过大量挖掘,我仍然无法找到在.NET中完成以下基本任务的正确方法(也许我只是找不到非常明显的东西,如果是的话,很抱歉)。
在WinForms应用程序中,存在一些SQL查询("select * from tab1 where col1 > 5"
);此查询的结果存储在DataTable对象中。
经过一段时间后,我想刷新数据库中的数据。由于有一个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对象中的所有行标记为"Deleted",然后从DB中获取所有数据,则会将现有行的状态重置为Unchanged
,而将不在DB中的行保留为Deleted
;则随后的CCD_ 6将它们从数据表中移除。
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
来从DataTable中删除行——这很难看(而且并不总是容易)。
做这件事的正确方法是什么?
这可能是一个更根本的更改,但我认为最好不要使用DataTables,而是使用BindingList。更新列表后,所有更改都会自动反映在UI中。
这是一个很长的例子。使用此方法,列表将从数据库中刷新,但绑定将保留,因此您不会更改控件上的数据源。
用于实现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与业务逻辑分离。