65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.31/5 (4投票s)

2015年1月4日

CPOL

7分钟阅读

viewsIcon

15004

downloadIcon

204

如何通过 Windows 窗体控件和 LINQ 指令,使用 DataSet 和 TableAdapter 来访问、显示、修改 SQL Server 数据(表等)

引言

在本文中,我们将了解如何使 SQL Server 中的表可用于我们的应用程序。除了使用 Visual Studio 向导连接到实例外,我们还将使用 DataSetTableAdapter 类。我们将通过一些示例,了解如何通过控件公开 SQL Server 中的数据,以及如何在代码端操作它们,以实现对底层数据库的更新。最后,我们将看到一些 LINQ 的参考,以应用于当前场景。

创建 DataSet 的几步

这个副标题可能有点过于简化,但事实也差不多:在这里,我们将通过“数据源配置向导”来连接到我们的数据库,该向导将创建我们在示例中使用的对象。这将是一个非常简单的操作。假设我们有一个 SQL Server 实例,上面驻留着 TECHNET 数据库。它包含一个名为 People 的表,由一个自动递增的 Id 和两个 Varchar 字段 NameCity 组成。

在 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。为此,我们将使用 TableAdapterFill 方法,将 People DataTable 作为参数传递给它。然后,DataSource 将被设置为 DataGridViewDataSource。这将为网格生成引用表字段的列,并显示它们的内容。现在我们可以修改显示的数据。

但是,要保存数据,这还不够:我们必须告诉 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

我们执行了与上一个示例相同的 DataSetTableAdapter 初始化,并继续填充我们的 DataSetComboBox 控件的 DataSource 属性的设置方式与我们为 DataGridView 设置的方式相同,然后我们使用定义列名的 stringColumnName)来设置 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 循环,我们更改了字段值,并通过调用 TableAdapterUpdate 来巩固了我们的数据。运行示例,并检查 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 的首次发布
© . All rights reserved.