演练:在 .NET Framework 应用程序的事务中保存数据

注意

数据集和相关类是 2000 年代初的旧 .NET Framework 技术,使应用程序能够在应用程序与数据库断开连接时处理内存中的数据。 它们对于使用户能够修改数据并持续更改回数据库的应用程序特别有用。 虽然数据集已被证明是一项非常成功的技术,但我们建议新的 .NET 应用程序使用 Entity Framework Core。 实体框架提供了一种更自然的方式来将表格数据作为对象模型,并且具有更简单的编程接口。

本演练演示如何使用 System.Transactions 命名空间在事务中保存数据。 本演练将创建一个 Windows 窗体应用程序。 你将使用“数据源配置”向导为 Northwind 示例数据库中的两个表创建一个数据集。 将数据绑定控件添加到 Windows 窗体,并修改 BindingNavigator 的保存按钮的代码以更新 TransactionScope 内的数据库。

先决条件

要完成本教程,需要在 Visual Studio 中安装“.NET 桌面开发”以及“数据存储和处理”工作负载。 若要安装这些程序,请打开 Visual Studio 安装程序,然后在要修改的 Visual Studio 版本旁选择“修改”(或“更多”>“修改”)。 请参阅修改 Visual Studio

本演练使用 SQL Server Express LocalDB 和 Northwind 示例数据库。

  1. 如果尚未安装 SQL Server Express LocalDB,可以从 SQL Server Express 下载页或通过 Visual Studio 安装程序安装。 在 Visual Studio 安装程序中,可以将 SQL Server Express LocalDB 作为 .NET 桌面开发工作负载的一部分或作为单个组件进行安装。

  2. 按照以下步骤安装 Northwind 示例数据库:

    1. 在 Visual Studio 中,打开“SQL Server 对象资源管理器”窗口。 (在 Visual Studio 安装程序中 SQL Server 对象资源管理器作为数据存储和处理工作负载的一部分安装。)展开 SQL Server 节点 。 右键单击 LocalDB 实例并选择“新建查询”。

      此时将打开查询编辑器窗口。

    2. Northwind Transact-SQL 脚本复制到剪贴板。 此 T-SQL 脚本从头开始创建 Northwind 数据库并用数据填充它。

    3. 将 T-SQL 脚本粘贴到查询编辑器中,然后选择“执行”按钮。

      不久后,查询完成运行并且 Northwind 数据库创建完成。

创建 Windows 窗体应用程序

第一步是创建“Windows 窗体应用(.NET Framework)”

  1. 在 Visual Studio 的“文件”菜单中,依次选择“新建”>“项目” 。

  2. 在左侧窗格中展开“Visual C#”或“Visual Basic”,然后选择“Windows 桌面” 。

  3. 在中间窗格中,选择“Windows 窗体应用”项目类型。

  4. 将项目命名为 SavingDataInATransactionWalkthrough,然后选择“确定” 。

    创建“SavingDataInATransactionWalkthrough”项目并将其添加到“解决方案资源管理器”中

创建数据库数据源

此步骤根据 Northwind 示例数据库中的 CustomersOrders 表,使用“数据源配置”向导创建数据源。

  1. 若要打开“数据源”窗口,在“数据”菜单上选择“显示数据源” 。

  2. 在“数据源”窗口,选择“添加新数据源”以启动“数据源配置”向导

  3. 在“选择数据源类型”屏幕中,选择“数据库”,然后选择“下一步” 。

  4. 在“选择数据连接”屏幕中,执行以下操作之一:

    • 如果下拉列表中包含到 Northwind 示例数据库的数据连接,请选择该连接。

      -或-

    • 选择“新建连接”以启动“添加/修改连接”对话框,并创建到 Northwind 数据库的连接

  5. 如果数据库需要密码,请选择包含敏感数据的选项,再选择“下一步”。

  6. 在“将连接字符串保存到应用程序配置文件”屏幕中,选择“下一步” 。

  7. 在“选择数据库对象”屏幕中,展开“表”节点 。

  8. 选择 CustomersOrders 表,然后选择“完成”。

    将“NorthwindDataSet”添加到项目后,“数据源”窗口即会显示 CustomersOrders

向窗体添加控件

通过将某些项从“数据源”窗口拖到窗体,可创建数据绑定控件。

  1. 在“数据源”窗口中展开“Customers”节点 。

  2. 将主“Customers”节点从“数据源”窗口拖到“Form1”上

    用于导航记录的 DataGridView 控件和工具栏(BindingNavigator)将显示在窗体上。 此时组件栏中显示 NorthwindDataSetCustomersTableAdapterBindingSourceBindingNavigator

  3. 将相关的“Orders”节点(不是主“Orders”节点,而是“Fax”列下面的相关子表节点)拖到“CustomersDataGridView”下面的窗体上 。

    窗体上显示一个 DataGridView。 组件栏中显示 OrdersTableAdapterBindingSource

添加对 System.Transactions 程序集的引用

事务使用 System.Transactions 命名空间。 默认情况下,没有添加对 system.transactions 程序集的项目引用,因此你需要手动将其添加。

添加对 System.Transactions DLL 文件的引用

  1. 在“项目”菜单中,选择“添加引用”。

  2. 选择“System.Transactions”(在“.NET”选项卡上),然后选择“确定”

    将“System.Transactions”的引用添加到项目

修改 BindingNavigator 的 SaveItem 按钮中的代码

由于第一个表已放置在你的窗体上,因此代码会默认添加到 BindingNavigator 上保存按钮的 click 事件中。 你需要手动添加代码以更新所有附加的表。 对于本演练,我们将重构源自保存按钮的单击事件处理程序的现有保存代码。 还将创建更多的方法以根据是需要添加行还是需要删除行提供特定的更新功能。

修改自动生成的保存代码

  1. 在“CustomersBindingNavigator”上选择“保存”按钮(带有软盘图标的按钮) 。

  2. CustomersBindingNavigatorSaveItem_Click 方法替换为以下代码:

    private void customersBindingNavigatorSaveItem_Click(object sender, EventArgs e)
    {
        UpdateData();
    }
    
    private void UpdateData()
    {
        this.Validate();
        this.customersBindingSource.EndEdit();
        this.ordersBindingSource.EndEdit();
    
        using (System.Transactions.TransactionScope updateTransaction = 
            new System.Transactions.TransactionScope())
        {
            DeleteOrders();
            DeleteCustomers();
            AddNewCustomers();
            AddNewOrders();
    
            updateTransaction.Complete();
            northwindDataSet.AcceptChanges();
        }
    }
    

对相关数据的协调更改的顺序如下:

  • 删除子记录。 (在此情况下,从 Orders 表中删除记录。)

  • 删除父记录。 (在此情况下,从 Customers 表中删除记录。)

  • 插入父记录。 (在此情况下,在 Customers 表中插入记录。)

  • 插入子记录。 (在此情况下,在 Orders 表中插入记录。)

删除现有顺序

  • 将以下 DeleteOrders 方法添加到“Form1”

    private void DeleteOrders()
    {
        NorthwindDataSet.OrdersDataTable deletedOrders;
        deletedOrders = (NorthwindDataSet.OrdersDataTable)
            northwindDataSet.Orders.GetChanges(DataRowState.Deleted);
    
        if (deletedOrders != null)
        {
            try
            {
                ordersTableAdapter.Update(deletedOrders);
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("DeleteOrders Failed");
            }
        }
    }
    

删除现有客户

  • 将以下 DeleteCustomers 方法添加到“Form1”

    private void DeleteCustomers()
    {
        NorthwindDataSet.CustomersDataTable deletedCustomers;
        deletedCustomers = (NorthwindDataSet.CustomersDataTable)
            northwindDataSet.Customers.GetChanges(DataRowState.Deleted);
    
        if (deletedCustomers != null)
        {
            try
            {
                customersTableAdapter.Update(deletedCustomers);
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("DeleteCustomers Failed");
            }
        }
    }
    

添加新客户

  • 将以下 AddNewCustomers 方法添加到“Form1”

    private void AddNewCustomers()
    {
        NorthwindDataSet.CustomersDataTable newCustomers;
        newCustomers = (NorthwindDataSet.CustomersDataTable)
            northwindDataSet.Customers.GetChanges(DataRowState.Added);
    
        if (newCustomers != null)
        {
            try
            {
                customersTableAdapter.Update(newCustomers);
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("AddNewCustomers Failed");
            }
        }
    }
    

添加新顺序

  • 将以下 AddNewOrders 方法添加到“Form1”

    private void AddNewOrders()
    {
        NorthwindDataSet.OrdersDataTable newOrders;
        newOrders = (NorthwindDataSet.OrdersDataTable)
            northwindDataSet.Orders.GetChanges(DataRowState.Added);
    
        if (newOrders != null)
        {
            try
            {
                ordersTableAdapter.Update(newOrders);
            }
            catch (System.Exception ex)
            {
                MessageBox.Show("AddNewOrders Failed");
            }
        }
    }
    

运行应用程序

F5 运行该应用程序。