通过控件和 LINQ 从 SQL Server 源进行数据操作






4.31/5 (4投票s)
如何通过 Windows 窗体控件和 LINQ 指令,使用 DataSet 和 TableAdapter 来访问、显示、修改 SQL Server 数据(表等)
引言
在本文中,我们将了解如何使 SQL Server 中的表可用于我们的应用程序。除了使用 Visual Studio 向导连接到实例外,我们还将使用 DataSet
和 TableAdapter
类。我们将通过一些示例,了解如何通过控件公开 SQL Server 中的数据,以及如何在代码端操作它们,以实现对底层数据库的更新。最后,我们将看到一些 LINQ 的参考,以应用于当前场景。
创建 DataSet 的几步
这个副标题可能有点过于简化,但事实也差不多:在这里,我们将通过“数据源配置向导”来连接到我们的数据库,该向导将创建我们在示例中使用的对象。这将是一个非常简单的操作。假设我们有一个 SQL Server 实例,上面驻留着 TECHNET
数据库。它包含一个名为 People
的表,由一个自动递增的 Id 和两个 Varchar
字段 Name
和 City
组成。
在 Visual Studio 菜单中,点击“项目”,然后选择“添加新数据源”。
在将出现的窗口中,我们必须选择数据将从中读取的对象类型。在我们的例子中,是 Database
。
现在我们将选择一个模型类型,以便我们的应用程序可以通过它读取数据。我们将使用 DataSet
。
现在我们将被要求输入连接参数。点击“新建连接”,然后向向导提供我们的 SQL Server 实例参数。连接字符串将保存在 App.config 文件中,以便以后在迁移到不同的操作环境或更改实例等情况下进行修改。
点击“下一步”。向导将显示我们数据库(在本例中为 TECHNET)中包含的对象。选择 People
表,然后点击“下一步”。
向导将要求输入 DataSet
的名称。在过程结束时,我们将在解决方案所属的文件中看到我们的 DataSet
。
双击我们的 DataSet
将打开设计器,通过它我们可以看到向导如何创建了一个 DataTable
类型的对象,名为 People
,具有从源表中读取的相同字段,以及一个 TableAdapter
类型的对象,该对象公开了一些方法,例如填充、更新和删除功能,这些功能将通过 T-SQL 执行,而 T-SQL 是由表架构自动生成的。在这里,我们可以重命名向导创建的对象、列、更改列属性以及修改查询。
绑定到 DataGridView:数据呈现和修改
假设我们在 Windows Forms 环境中:我们有一个窗体,在其顶部我们将创建一个 DataGridView
。在该控件中,我们希望显示我们表的内容,并允许用户进行修改。
以下代码将实现这些功能。我们将首先声明两个新的引用,第一个是上面看到的 DataSet
,第二个是它的 TableAdapter
,与 DataSet
架构中的一样。
Public Class Form1
Dim myDS As New TECHNETDataSet
Dim myTB As New TECHNETDataSetTableAdapters.PeopleTableAdapter
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
myTB.Fill(myDS.People)
DataGridView1.DataSource = myDS.People
End Sub
Private Sub DataGridView1_CellValidated(sender As Object, _
e As DataGridViewCellEventArgs) Handles DataGridView1.CellValidated
myTB.Update(myDS.People)
End Sub
End Class
在窗体的 Load
事件中,我们希望填充我们的 DataSet
。为此,我们将使用 TableAdapter
的 Fill
方法,将 People DataTable
作为参数传递给它。然后,DataSource
将被设置为 DataGridView
的 DataSource
。这将为网格生成引用表字段的列,并显示它们的内容。现在我们可以修改显示的数据。
但是,要保存数据,这还不够:我们必须告诉 TableAdapter
对底层数据执行更新。我们将为此使用 Update
方法。在上面的代码片段中,更新过程将在单元格内容被验证后发生,即当单元格中的数据被标记为正确时。如果我们希望在整个行被验证时执行此操作,可以使用 RowValidated
事件。
运行我们的示例,并进行一些插入数据、修改数据和删除行的测试,我们将看到每一个修改都会自动反映在原始表中。
绑定到 ComboBox 的单列
有些控件虽然拥有允许数据绑定的相同属性,但并不适用于显示全部结果。例如,ComboBox
无法显示表中的所有列,只能显示其中一列。在 ComboBox
上,我们可以像为 DataGridView
一样设置 DataSource
,但要指定 DisplayMember
属性中将被公开的数据成员。
一个例子可能如下所示
Public Class Form1
Dim myDS As New TECHNETDataSet
Dim myTB As New TECHNETDataSetTableAdapters.PeopleTableAdapter
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
myTB.Fill(myDS.People)
ComboBox1.DataSource = myDS.People
ComboBox1.DisplayMember = myDS.People.NameColumn.ColumnName
End Sub
End Class
我们执行了与上一个示例相同的 DataSet
和 TableAdapter
初始化,并继续填充我们的 DataSet
。ComboBox
控件的 DataSource
属性的设置方式与我们为 DataGridView
设置的方式相同,然后我们使用定义列名的 string
(ColumnName
)来设置 DisplayMember
属性,该列名属于 DataSet
中驻留的 People
表的 Name
字段(NameColumn
)。这样,运行我们的程序,我们将看到 ComboBox
的元素将由 Name
列表示。
LINQ 参考
Language-Integrated Queries 的主要特点是其指令集的一致性,独立于引用的数据源,以及强大的过滤功能,可以非常简洁高效地选择数据。在初始化对我们 DataSet
的引用后,LINQ 语法可以在我们已经看到的情况下有效地使用。
回到 DataGridView
示例,假设我们只想提取并显示 Name
字段以“John
”开头的记录,我们可以写如下代码片段
Public Class Form1
Dim myDS As New TECHNETDataSet
Dim myTB As New TECHNETDataSetTableAdapters.PeopleTableAdapter
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
myTB.Fill(myDS.People)
DataGridView1.DataSource = (From pr As TECHNETDataSet.PeopleRow
In myDS.People
Where pr.Name Like "John*").ToList
End Sub
Private Sub DataGridView1_CellValidated(sender As Object, _
e As DataGridViewCellEventArgs) Handles DataGridView1.CellValidated
myTB.Update(myDS.People)
End Sub
End Class
请注意,DataGridView DataSource
并不直接设置为 DataTable
,而是设置为对 PeopleRow
元素的提取,这些元素是根据 Where
子句读取的,该子句在 Name
字段中搜索字符串“John*
”(其中星号是通配符)。在这种情况下,处理过滤后的列表不允许我们添加新项。但我们可以修改显示的项,并且通过调用 Update
方法,我们的更改将被保存。
同样,如果我们想重建上述条件来处理我们的 ComboBox
,一个例子可能是
Public Class Form1
Dim myDS As New TECHNETDataSet
Dim myTB As New TECHNETDataSetTableAdapters.PeopleTableAdapter
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
myTB.Fill(myDS.People)
ComboBox1.DataSource = (From pr As TECHNETDataSet.PeopleRow
In myDS.People
Select pr.Name).ToList
End Sub
End Class
在这种情况下,知道我们可以绑定一个简单的 string
列表,我们可以通过在查询中添加进一步的选择来绕过 DisplayMember
所固有的问题。在最后一个示例中,我们从 People
表中提取所有行,然后从中提取 Name
元素。显然,要继续使用 DisplayMember
属性,我们可以简单地写
ComboBox1.DataSource = (From pr As TECHNETDataSet.PeopleRow
In myDS.People).ToList
ComboBox1.DisplayMember = "Name"
或者,避免从第一个选择中提取子集,让 DisplayMember
属性负责显示特定列。
通过 LINQ 进行数据修改
假设我们希望执行一次无需用户交互的更新。考虑一个字段,当满足预定条件时,必须自动修改。在我们的示例中,我们想将表中的所有记录的城市都更改为“Turin
”。通过 LINQ 和我们迄今为止看到的那些方法,我们可以做到这一点,而无需担心连接层
Public Class Form1
Dim myDS As New TECHNETDataSet
Dim myTB As New TECHNETDataSetTableAdapters.PeopleTableAdapter
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
myTB.Fill(myDS.People)
Dim results As IEnumerable(Of TECHNETDataSet.PeopleRow) = _
From p As TECHNETDataSet.PeopleRow In myDS.People
For Each result In results
result.City = "Turin"
Next
myTB.Update(myDS.People)
End Sub
End Class
我们已经填充了我们的 DataSet
,从表中提取了 PeopleRows
的枚举。然后,通过一个 For
/Each
循环,我们更改了字段值,并通过调用 TableAdapter
的 Update
来巩固了我们的数据。运行示例,并检查 SQL Server 端相应的属性,我们可以注意到数据已成功修改。
更符合 LINQ 风格的方法来编写上述示例可能是以下方法,在其中我们将城市修改为“Milan
”
Public Class Form1
Dim myDS As New TECHNETDataSet
Dim myTB As New TECHNETDataSetTableAdapters.PeopleTableAdapter
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
myTB.Fill(myDS.People)
Dim results As IEnumerable(Of TECHNETDataSet.PeopleRow) = _
(From p As TECHNETDataSet.PeopleRow In myDS.People)
results.ToList.ForEach(Sub(x As TECHNETDataSet.PeopleRow) x.City = "Milan")
myTB.Update(myDS.People)
End Sub
End Class
最后,假设我们希望修改特定记录的 City
字段。例如,我们想提取 Name
包含字符串“John
”的单个记录,并将其 City
修改为“New York
”。
Public Class Form1
Dim myDS As New TECHNETDataSet
Dim myTB As New TECHNETDataSetTableAdapters.PeopleTableAdapter
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
myTB.Fill(myDS.People)
Dim p As TECHNETDataSet.PeopleRow = myDS.People.Single(Function_
(x As TECHNETDataSet.PeopleRow) x.Name.Contains("John") <> 0)
p.City = "New York"
myTB.Update(myDS.People)
End Sub
End Class
在这种情况下,使用 Single
函数,我们提取了一个引用了内部函数中设定的条件的记录,即在 Name
列中存在字符串“John
”。之后,引用结果变量,就足以修改所需的属性,然后调用 Table Adapter 的 Update
函数。
参考文献
历史
- 2015-01-04:向文章添加了源代码(包含示例数据库备份)
- 2015-01-04:CodeProject 的首次发布