C#5 数据库入门指南(三)

原文:Beginning C# 5.0 Databases

协议:CC BY-NC-SA 4.0

十四、使用数据读取器

在第十三章中,您使用数据读取器从多行结果集中检索数据。在本章中,我们将更详细地了解数据读取器。您将看到它们是如何使用的,以及它们在 ADO.NET 编程中的重要性。

在本章中,我们将介绍以下内容:

  • Understand the general situation of data readers.
  • Get data about data
  • Get data about the table
  • Using a data reader using multiple result sets

从总体上了解数据阅读器

在连接和命令之后,数据提供者的第三个组件是*数据读取器。*一旦连接到数据库并进行查询,就需要某种方法来访问结果集。这就是数据读取器的用武之地。

数据读取器是实现System.Data.IDataReader接口的对象。数据读取器是一个快速、无缓冲、只进、只读的连接的流,它以每行为基础检索数据。它在结果集中循环时一次读取一行。

不能直接实例化数据读取器;相反,您可以使用命令的ExecuteReader方法创建一个。例如,假设cmd是一个查询的SqlClient命令对象,下面介绍如何创建一个SqlClient数据读取器:

SqlDataReader rdr = cmd.ExecuteReader();

现在,您可以使用这个数据读取器来访问查询的结果集。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 提示我们将在下一章进一步讨论的一点是选择数据读取器还是数据集。一般规则是总是使用数据读取器来简单地检索数据。如果你需要做的只是显示数据,那么在大多数情况下你需要使用的就是数据阅读器。

我们将通过几个例子演示基本的数据读取器用法。第一个例子是最基本的;它只是使用一个数据读取器来遍历结果集。

假设您已经成功地与数据库建立了连接,执行了一个查询,一切似乎都很顺利——现在该怎么办?下一个明智的做法是检索行并处理它们。

试试看:遍历一个结果集

下面的 Windows 应用展示了如何使用SqlDataReader遍历结果集并检索行:

  1. 创建一个名为 Chapter14 的新 Windows 窗体应用项目。当解决方案资源管理器打开时,保存解决方案。

  2. 将 Chapter14 项目重命名为 DataReader。将 Form1.cs 文件重命名为 DataLooper.cs。

  3. 通过单击窗体的标题栏选择 DataLooper 窗体,并将 Size 属性的宽度设置为 346,高度设置为 476。

  4. 将 ListBox 控件拖到窗体上,并将其放在窗体的中央。选择此列表框,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 lbxProduct。
    • 将位置属性的 X 设置为 21,Y 设置为 22。
    • 将 Size 属性的宽度设置为 281,高度设置为 394。
  5. Now your DataLooper form in the Design view should like Figure 14-1. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 14-1。*数据活套表单的设计视图

  6. Double-click the empty surface of the DataLooper.cs form, and it will open the code editor window, showing the DataLooper_Load event. Modify the DataLooper_Load event to look like Listing 14-1.

    ***清单 14-1。*T4DataLooper.cs

    `Using System.Data.SqlClient;

    private void DataLooper_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @“server=.\sql2012;database=AdventureWorks;
                                       Integrated Security=SSPI”;

    // Query
                string sql = @“select Name from Production.Product”;             // Create connection
                SqlConnection conn = new SqlConnection(connString);

    try
                {
                    // Open connection
                    conn.Open();

    // Create command
                    SqlCommand cmd = new SqlCommand(sql, conn);

    // Create data reader
                    SqlDataReader rdr = cmd.ExecuteReader();

    // Loop through result set
                    while (rdr.Read())
                    {
                        // Add to listbox - one row at a time
                        lbxProduct.Items.Add(rdr[0]);
                    }

    // Close data reader
                    rdr.Close();
                }

    catch (SqlException ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace);
                }

    finally
                {
                    conn.Close();
                }
         }`

  7. Build the project, and run the DataLooper form by pressing Ctrl+F5. Your results should look like Figure 14-2. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 14-2。*使用DataReader 循环结果集

它是如何工作的

SqlDataReader是抽象类,不能显式实例化。出于这个原因,您通过执行SqlCommandExecuteReader方法来获得一个SqlDataReader的实例。

// Create data reader SqlDataReader rdr = cmd.ExecuteReader();

ExecuteReader()不仅仅是创建一个数据阅读器;它将 SQL 发送到连接以供执行,因此当它返回时,您可以遍历结果集的每一行并逐列检索值。为此,您调用SqlDataReaderRead方法,如果有一行可用,该方法将返回true,并使光标前进(指向结果集中下一行的内部指针),如果另一行不可用,该方法将返回false。由于Read()将光标移动到下一个可用行,您必须为结果集中的所有行调用它,所以您在while循环中调用它作为条件。

// Loop through result set while (rdr.Read()) {        // Add to listbox - one row at a time        lbxProduct.Items.Add(rdr[0]); }

一旦调用了Read方法,下一行将作为集合返回并存储在SqlDataReader对象中。要访问特定列中的数据,可以使用许多方法(我们将在下一节中介绍这些方法),但是对于这个应用,您使用序号索引器查找方法,将列号提供给读取器以检索值(就像您为数组指定索引一样)。因为在这种情况下,您在查询数据库时从 Customers 表中选择了一个单独的列,所以只有“zeroth”索引器是可访问的,所以您将该索引硬编码为rdr[0]

为了将连接用于其他目的或者在数据库上运行另一个查询,调用SqlDataReaderClose方法来显式关闭阅读器是很重要的。一旦读取器被连接到活动连接,该连接就保持忙于为读取器获取数据,并且保持不可用于其他用途,直到读取器被从其分离。这就是为什么您在try块中关闭阅读器,而不是在finally块中(即使这个简单的程序不需要将连接用于其他目的)。

// Close data reader rdr.Close();

使用序数索引器

使用序号索引器从结果集中检索列数据。让我们了解更多关于序数索引。以下代码:

rdr[0]

是对数据读取器的Item属性的引用,并返回为当前行指定的列中的值。该值作为对象返回。

试试看:使用序数索引器

在此示例中,您将向 DataReader 项目添加一个 Windows 窗体,然后您将使用一个序号索引器。

  1. 选择 DataReader 项目,右键单击,然后选择“添加外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Windows 窗体”。在打开的对话框中,确保选择了 Windows 窗体,并将 Form1.cs 重命名为 OrdinalIndexer.cs 然后单击“确定”将该窗体添加到 DataReader 项目中。

  2. 通过单击窗体的标题栏选择 OrdinalIndexer 窗体,并将 Size 属性的宽度设置为 289,高度设置为 351。

  3. 将 TextBox 控件拖到窗体上,并将其放在窗体的中央。选择此 TextBox 控件,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 txtValues。
    • 将位置属性的 X 设置为 12,Y 设置为 12。
    • 将 Multiline 属性设为 True。
    • 将 ScrollBars 属性设置为垂直。
    • 将 Size 属性的宽度设置为 249,高度设置为 287。
    • 将文本属性留空。
  4. Now your OrdinalIndexer form in the Design view should like Figure 14-3. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    **图 14-3。**普通型设计视图

  5. Double-click the empty surface of the DataLooper.cs form, and it will open the code editor window, showing the DataLooper_Load event. Modify the DataLooper_Load event to look like Listing 14-2.

    ***清单 14-2。*T40rdinallndexer.cs

    `Using System.Data.SqlClient;

    private void OrdinalIndexer_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @“server=.\sql2012;database=AdventureWorks;                               Integrated Security=SSPI”; // Query
                string sql = @" select FirstName,LastName
                                from  Person.Contact
                                where FirstName like ‘M%’";

    // Create connection
                SqlConnection conn = new SqlConnection(connString);

    try
                {
                    // Open connection
                    conn.Open();

    // Create command
                    SqlCommand cmd = new SqlCommand(sql, conn);

    // Create data reader
                    SqlDataReader rdr = cmd.ExecuteReader();

    // Print headings
                    StringBuilder sb=new StringBuilder();
                    txtValues.AppendText(“First Name”.PadRight(25));
                    txtValues.AppendText(“Last Name”.PadLeft(20));
                    txtValues.AppendText(Environment.NewLine);
                    txtValues.AppendText(“-----------------------------------------
                                    --------------------------”);
                    txtValues.AppendText(Environment.NewLine);

    // Loop through result set
                    while (rdr.Read())
                    {
                        txtValues.AppendText(rdr[0].ToString());
                        txtValues.AppendText(“\t\t\t”);
                        txtValues.AppendText(rdr[1].ToString());
                        txtValues.AppendText(Environment.NewLine);
                    }

    // Close reader
                    rdr.Close();
                }

    catch (SqlException ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace,“Exception Details”);
                }

    finally
                {
                    // Close connection
                    conn.Close();
                }         }`

  6. To set the OrdinalIndexer form as the start-up form, modify the Program.cs statement: Application.Run(new DataLooper());

    表现为:

    Application.Run(new OrdinalIndexer());

  7. Build the project, and run it by pressing Ctrl+F5. You should see the results in Figure 14-4. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 14-4。*使用 OrdinalIndexer 显示多列

它是如何工作的

你询问这个人。FirstName 和 LastName 列的联系人表,其中联系人姓名以字母 M 开头。

            // Query             string sql = @" select FirstName,LastName                          from  Person.Contact                          where FirstName like 'M%'";

由于查询选择了两列,返回的数据也只包含这两列中的行集合,因此只允许访问两个可能的顺序索引器,即 0 和 1。

出于格式化的目的,我们希望将列标题 FirstName 和 LastName 显示为第一个标题行,然后我们希望将所有其他值行添加到它的下面。因此,您可以使用PadeRightPadLeft方法来格式化输出,通过在指定的总长度的左侧/右侧填充空格,使所有字符左右对齐。

                // Print headings                 StringBuilder sb=new StringBuilder();                 txtValues.AppendText("First Name".PadRight(25));                 txtValues.AppendText("Last Name".PadLeft(20));                 txtValues.AppendText(Environment.NewLine);                 txtValues.AppendText("---------------------------------------------                                  --------------------");             txtValues.AppendText(Environment.NewLine);

现在你在一个while循环中读取每一行,用索引器获取两列的值并将这两列追加到文本框中,这样所有的名字都显示为一个列表,如图 14-2 所示。

                // Loop through result set                 while (rdr.Read())                 {                     txtValues.AppendText(rdr[0].ToString());                     txtValues.AppendText("\t\t\t");                     txtValues.AppendText(rdr[1].ToString());                     txtValues.AppendText(Environment.NewLine);                 }

处理完结果集中的所有行后,显式关闭读取器以释放连接。

                // Close reader                 rdr.Close();

使用列名索引器

大多数时候,我们并不真正跟踪列号,而更喜欢通过各自的列名来检索值,只是因为通过它们的名称来记住它们要容易得多,这也使得代码更加自文档化。

通过指定列名而不是序号索引号来使用列名索引。这有它的好处。例如,添加或删除一个或多个列可能会更改表,打乱列的顺序,并在使用序号索引器的旧代码中引发异常。使用列名索引器可以避免这个问题,但是顺序索引器更快,因为它们直接引用列,而不是通过名称来查找。

下面的代码片段使用列名索引器检索与上一示例相同的列(FirstName 和 LastName)。

                // Loop through result set                 while (rdr.Read())                 {                     txtValues.AppendText(rdr["FirstName"].ToString());                                       txtValues.AppendText(rdr["LastName"].ToString());                 }

用列名索引器替换OrdinalIndexer.cs中的序数索引器,重新运行项目;你会得到与图 14-2 中相同的结果。下一节讨论大多数情况下的更好的方法。

使用类型化访问器方法

当数据读取器从数据源返回值时,将检索结果值并以. NET 类型而不是原始数据源类型存储在本地。这种就地类型转换功能是一致性和速度之间的一种折衷,因此为了对正在检索的数据进行一些控制,数据读取器公开了类型化访问器方法,如果您知道所返回值的特定类型,就可以使用这些方法。

类型化访问器方法都以Get开头,采用序号索引进行数据检索,并且是类型安全的;C# 不允许你逃脱不安全的强制转换。这些方法比序号和列名索引器方法都要快。比列名索引更快似乎是合乎逻辑的,因为类型化的访问器方法采用序数进行引用;然而,我们需要解释它为什么比顺序索引快。这是因为即使这两种技术都接受列号,传统的顺序索引方法也需要查找结果的数据源数据类型,然后进行类型转换。使用类型化访问器可以避免查找模式的开销。

。NET 类型和类型化访问器方法可用于 SQL Server 和 OLE DB 数据库支持的几乎所有数据类型。

表 14-1 应该给你一个什么时候使用类型化访问器和使用什么数据类型的简单概念。它列出了 SQL Server 数据类型及其对应的。网络类型,。NET 类型访问器,以及专门为返回类型为System.Data.SqlTypes的对象而设计的特定于 SQL Server 的特殊类型访问器。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

表 14-2 列出了一些可用的 OLE DB 数据类型,它们对应的。NET 类型以及它们的。NET 类型的访问器。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

要查看运行中的类型化访问器,您将构建一个使用它们的控制台应用。对于这个例子,您将使用来自Northwind数据库的 Products 表。

表 14-3 显示了表格的数据设计。请注意,表中给定的数据类型将在表 14-1 中查找其对应的类型化访问器方法,以便您可以在您的应用中正确使用它们。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

试试看:使用类型化访问器方法

在此示例中,您将向 DataReader 项目添加一个 Windows 窗体,然后使用类型化访问器方法。

  1. 选择 DataReader 项目,右键单击,然后选择“添加外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Windows 窗体”。在打开的对话框中,确保选择了 Windows 窗体,并将 Form1.cs 重命名为 TypedAccessor.cs 单击“确定”将该窗体添加到 DataReader 项目中。

  2. 选择 TypedAccessor 窗体,并将 Size 属性的宽度设置为 476,高度设置为 353。

  3. 将 TextBox 控件拖到窗体上,并将其放在窗体的中央。选择此 TextBox 控件,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 txtTypeAccess。
    • 设置位置属性的 X 为 12,Y 为 12。
    • 将 ScrollBars 属性设置为垂直。
    • 将 Size 属性的宽度设置为 437,高度设置为 290。
    • 将 Multiline 属性设置为 True。
  4. Now your TypedAccessor form in the Design view should look like Figure 14-5. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 14-5。*typed accessor 表单的设计视图

  5. Now double-click the empty surface of the TypedAccessor.cs form, and it will open the code editor window, showing the TypedAccessor_Load event. Modify the TypedAccessor _Load event to look like Listing 14-3.

    ***清单 14-3。*T4TypedAccessors.cs

    `Using System.Data.SqlClient;
             private void TypedAccessors_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @“server=.\sql2012;database=AdventureWorks;
                                  Integrated Security=SSPI”;

    // Query
                string sql = @“select CardType, CardNumber,ExpMonth,ExpYear from
    Sales.CreditCard”;             // Create connection
                SqlConnection conn = new SqlConnection(connString);

    try
                {
                    // Open connection
                    conn.Open();

    // Create command
                    SqlCommand cmd = new SqlCommand(sql, conn);

    // Create data reader

    SqlDataReader rdr = cmd.ExecuteReader();

    // Fetch data
                    while (rdr.Read())
                    {

    // CardType
                        txtTypeAccess.AppendText(rdr.GetString(0).PadRight(30));
                        txtTypeAccess.AppendText(“\t”);
                        // CardNumber
                        txtTypeAccess.AppendText(rdr.GetString(1));
                        txtTypeAccess.AppendText(“\t\t”);
                        // ExpMonth
                        txtTypeAccess.AppendText(rdr.GetByte(2).ToString());
                        txtTypeAccess.AppendText(“\t\t”);
                        // ExpYear
                        txtTypeAccess.AppendText(rdr.GetInt16(3).ToString());
                        txtTypeAccess.AppendText(“\n”);
                    }
                    // Close data reader
                    rdr.Close();
                }
                catch (SqlException ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace,“Exception Details”);
                }

    finally
                {

    // Close connection
                    conn.Close();
                }
            }`

  6. To set the TypedAccessor form as the start-up form, modify the Program.cs statement. Application.Run(new OrdinalIndexer ());

    出现为:

    Application.Run(new TypedAccessor());

  7. Build the project, and run it by pressing Ctrl+F5. You should see the results in Figure 14-6. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 14-6。*使用类型化访问器

它是如何工作的

你查询销售情况。CardType、CardNumber、ExpMonth 和 ExpYear 的信用卡表。

            // Query             string sql = @"select CardType, CardNumber,ExpMonth,ExpYear from Sales.CreditCard";

我们让您选择这些列的原因是为了处理不同种类的数据类型,并展示如何使用相关的类型化访问器来获得正确的结果。

`// Fetch data
                while (rdr.Read())
                {

// CardType
                    txtTypeAccess.AppendText(rdr.GetString(0).PadRight(30));
                    txtTypeAccess.AppendText(“\t”);
                    // CardNumber
                    txtTypeAccess.AppendText(rdr.GetString(1));
                    txtTypeAccess.AppendText(“\t\t”);                     // ExpMonth
                    txtTypeAccess.AppendText(rdr.GetByte(2).ToString());
                    txtTypeAccess.AppendText(“\t\t”);
                    // ExpYear
                    txtTypeAccess.AppendText(rdr.GetInt16(3).ToString());
                    txtTypeAccess.AppendText(“\n”);
                }`

查看表 14-1 ,您可以看到您可以分别使用GetStringGetByteGetlntl6访问器方法访问 SQL Server 中的 nvarchar、tinyint 和 smallint 数据类型。

这项技术速度很快,而且完全是类型安全的。我们的意思是,如果从本机数据类型到。NET 类型失败,则会因无效的强制转换而引发异常。例如,如果您尝试对一个bit数据类型使用GetString方法,而不是使用GetBoolean方法,将会抛出一个“指定的强制转换无效”异常。

获取关于数据的数据

到目前为止,您所做的只是从数据源中检索数据。一旦你有了一个填充的数据阅读器,你可以做更多的事情。有许多有用的方法可以检索模式信息或与结果集直接相关的信息。表 14-4 描述了数据读取器的一些元数据方法和属性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

试试看:用数据阅读器获取结果集的信息

在本练习中,您将使用其中的一些方法和属性。

  1. 选择 DataReader 项目,右键单击,然后选择“添加外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Windows 窗体”。在打开的对话框中,确保选择了 Windows Form,并将 Form1.cs 重命名为 ResultSetInfo.cs 然后单击“确定”将该窗体添加到 DataReader 项目中。

  2. 选择 ResultSetInfo 表单,并将 Size 属性的宽度设置为 462,高度设置为 460。

    • 将一个 Label 控件拖到窗体上,选择该控件,导航到“属性”窗口,并设置以下属性:
    • 将 Name 属性设置为 lblDataType。
    • 将 AutoSize 属性设置为 false。
    • 将位置属性的 X 设置为 3,Y 设置为 31。
    • 将 Size 属性的 X 设置为 57,Y 设置为 13。
    • 将 Text 属性设置为数据类型。
  3. 将 Label 控件拖到窗体上。选择该控件,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 lblType1。
    • 将 AutoSize 属性设置为 false。
    • 将位置属性的 X 设置为 80,Y 设置为 21。
    • 将 Size 属性的 X 设置为 101,Y 设置为 34。
    • 将文本属性留空。
    • 将 Label 控件拖到窗体上。选择该控件,导航到“属性”窗口,并设置以下属性:
    • 将 Name 属性设置为 lblType2。
    • 将 AutoSize 属性设置为 false。
    • 将位置属性的 X 设置为 222,Y 设置为 21。
    • 将 Size 属性的 X 设置为 101,Y 设置为 34。
    • 将文本属性留空。
    • 将 TextBox 控件拖动到 Label 控件下方,并导航到“属性”窗口以配置以下属性:
    • 将 Name 属性设置为 txtResultSet。
    • 将 Multiline 属性设置为 True。
    • 将位置属性的 X 设置为 32,Y 设置为 58。
    • 将 ScrollBars 属性设置为垂直。
    • 将 Size 属性的宽度设置为 341,高度设置为 234。
    • 将 Label 控件拖动到 TextBox 下方,导航到“属性”窗口,并设置以下属性:
    • 将 Name 属性设置为 lblType3。
    • 将 AutoSize 属性设置为 false。
    • 将位置属性的 X 设置为 38,Y 设置为 317。
    • 将 Size 属性的宽度设置为 335,高度设置为 13。
    • 将文本属性留空。
    • 将另一个标签控件拖到刚刚添加的标签下方。选择该控件,导航到“属性”窗口,并设置以下属性:
    • 将 Name 属性设置为 lblType4。
    • 将 AutoSize 属性设置为 false。
    • 将位置属性的 X 设置为 38,Y 设置为 352。
    • 将 Size 属性的宽度设置为 335,高度设置为 13。
    • 将文本属性留空。
    • 将另一个标签控件拖到刚刚添加的标签下方。选择该控件,导航到“属性”窗口,并设置以下属性:
    • 将 Name 属性设置为 lblType5。
    • 将 AutoSize 属性设置为 false。
    • 将位置属性的 X 设置为 38,Y 设置为 381。
    • 将 Size 属性的宽度设置为 335,高度设置为 13。
    • 将文本属性留空。
  4. Now your ResultSetInfo form in the Design view should like Figure 14-7. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 14-7。*ResultSetInfo 表单的设计视图

  5. Double-click the empty surface of the TypedAccessor.cs form, and it will open the code editor window, showing the TypedAccessor_Load event. Modify the TypedAccessor_Load event to look like Listing 14-4.

    ***清单 14-4。*T4ResultSetInfo.cs

    `Using System.Data.SqlClient;

    private void ResultSetInfo_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @“server=.\sql2012;database=AdventureWorks;
                                Integrated Security=SSPI”;

    // Query
                string sql = @" select FirstName,LastName from Person.Contact
                                 order by LastName"; // Create connection
                SqlConnection conn = new SqlConnection(connString);

    try
                {
                    conn.Open();

    SqlCommand cmd = new SqlCommand(sql, conn);

    SqlDataReader rdr = cmd.ExecuteReader();

    // Get column names
                    lbltype1.Text = rdr.GetName(0);
                    lblType2.Text = rdr.GetName(1);

    //Get column data types
                    lbltype1.Text += “\n”+  rdr.GetDataTypeName(0).ToString();
                    lblType2.Text += “\n”+ rdr.GetDataTypeName(1).ToString();

    // Get number of columns
                    lblType3.Text = “Number of columns in a row::” + rdr.FieldCount.ToString();

    // Get info about each column
                    lblType4.Text = rdr.GetName(0).ToString() + " is at index::" +
                                    rdr.GetOrdinal(“FirstName”).ToString()  +
                                    " and its type is::" + rdr.GetFieldType(0).ToString();

    lblType5.Text = rdr.GetName(1).ToString() + " is at index:: “+
                                   rdr.GetOrdinal(“LastName”).ToString()  +
                                   " and its type is::” + rdr.GetFieldType(1).ToString();

    while (rdr.Read())
                    {
                      // Get column values for all rows
                        txtResultSet.AppendText(“\t”);
                        txtResultSet.AppendText(rdr[0].ToString());
                        txtResultSet.AppendText(“\t\t\t”);  
                        txtResultSet.AppendText(rdr[1].ToString() );
                        txtResultSet.AppendText(“\n”);  
                    }

    //Close reader
                    rdr.Close();

    }
                catch (SqlException ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace,“Exception Details”);
                }

    finally
                {                 //Clsoe connection
                    conn.Close();
                }
            }`

  6. To set the ResultSetInfo form as the start-up form, modify the Program.cs statement. Application.Run(new TypedAccessor ());

    表现为:

    Application.Run(new ResultSetInfo());

  7. Build the project, and run it by pressing Ctrl+F5. Your results should look like Figure 14-8. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 14-8。*显示结果集元数据

它是如何工作的

GetName方法通过索引获取列名。这个方法返回关于结果集的信息*,所以它可以在第一次调用Read()之前被调用。*

                // Get column names                 lbltype1.Text = rdr.GetName(0);                 lblType2.Text = rdr.GetName(1);

GetDataTypeName方法返回列的数据库数据类型。它也可以在第一次调用Read()之前被调用。

                //Get column data types                 lbltype1.Text += "\n"+  rdr.GetDataTypeName(0).ToString();                 lblType2.Text += "\n"+ rdr.GetDataTypeName(1).ToString();

数据读取器的FieldCount属性包含结果集中的列数。这对于在不知道列名或其他属性的情况下遍历列很有用。

               // Get number of columns                 lblType3.Text = "Number of columns in a row::" + rdr.FieldCount.ToString();

最后,您将看到如何使用GetOrdinalGetFieldType方法。前者返回基于其名称的列索引;后者返回 C# 类型。这些分别是GetName()GetDataTypeName()的计数器类型。

`                // Get info about each column
                lblType4.Text = rdr.GetName(0).ToString() + " is at index::" +
                                rdr.GetOrdinal(“FirstName”).ToString()  +
                                " and its type is::" + rdr.GetFieldType(0).ToString();

lblType5.Text = rdr.GetName(1).ToString() + " is at index:: “+
                               rdr.GetOrdinal(“LastName”).ToString()  +
                               " and its type is::” + rdr.GetFieldType(1).ToString();`

关于获取结果集的信息就说到这里。现在您将学习如何获取关于模式的信息。

获取关于表格的数据

术语模式对于关系数据库有多种含义。这里,我们用它来表示数据结构的设计,尤其是数据库表。表格由行和列组成,每一列都可以有不同的数据类型。列及其属性(数据类型、长度等)构成了表的模式。

为了方便地检索模式信息,可以在数据读取器上调用GetSchemaTable方法。顾名思义,这个方法返回一个System.Data.DataTable对象,它是被查询的表的一个表示(模式),并且以DataRowDataColumn对象的形式包含行和列的集合。这些行和列由DataTable类的属性RowsColumns作为集合对象返回。

然而,这里通常会出现轻微的混淆。数据列对象不是列值;相反,它们是表示和控制单个列的行为的列定义。它们可以通过使用列名索引器来循环,并且可以告诉您许多关于数据集的信息。

尝试一下:获取模式信息

这里你会看到一个GetSchemaTable方法的实际演示。

  1. 选择 DataReader 项目,右键单击,然后选择“添加外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Windows 窗体”。在打开的对话框中,确保选择了 Windows 窗体,并将 Form1.cs 重命名为 SchemaTable.cs,然后单击 OK 将该窗体添加到 DataReader 项目中。

  2. 选择 SchemaTable 表单,并将 Size 属性的宽度设置为 378,高度设置为 459。

  3. 将 TextBox 控件拖到窗体上,并将其放在窗体的中间。选择此 TextBox 控件,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 txtSchema。
    • 将位置属性的 X 设置为 12,Y 设置为 12。
    • 将 Multiline 属性设置为 True。
    • 将 ScrollBars 属性设置为垂直。
    • 将 Size 属性的宽度设置为 392,高度设置为 333。
  4. Now your SchemaTable form in the Design view should look like Figure 14-9. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 14-9。*可图式的设计视图

  5. Double-click the empty surface of the SchemaTable.cs form, and it will open the code editor window, showing the SchemaTable_Load event. Modify the SchemaTable _Load event to look like Listing 14-5.

    ***清单 14-5。*T4SchemaTable.cs

    `Using System.Data.SqlClient
            private void SchemaTable_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @“server=.\sql2012;
                database=AdventureWorks;Integrated Security=SSPI”;

    // Query
                string sql = @“select * from  Person.Address”;

    // Create connection
                SqlConnection conn = new SqlConnection(connString);

    try
                {
                    conn.Open();

    SqlCommand cmd = new SqlCommand(sql, conn);
                    SqlDataReader rdr = cmd.ExecuteReader();

    // Store Employees schema in a data table
                    DataTable schema = rdr.GetSchemaTable();

    // Display info from each row in the data table.
                    // Each row describes a column in the database table.

    foreach (DataRow row in schema.Rows)
                    {
                        foreach (DataColumn col in schema.Columns)
                        {
                            txtSchema.AppendText(col.ColumnName + " = " + row[col]);
                            txtSchema.AppendText(“\n”);
                        }
                        txtSchema.AppendText(“-----------------”);
                    }

    //Close reader
                    rdr.Close();

    }
                catch (Exception err)
                {
                    MessageBox.Show(err.ToString() );
                }
                finally
                {                 //connection close
                    conn.Close();
                }
     }`

  6. To set the SchemaTable form as the start-up form, modify the Program.cs statement. Application.Run(new ResultSetInfo ());

    表现为

    Application.Run(new SchemaTable());

  7. Build the project, and run it by pressing Ctrl+F5. You should see the results in Figure 14-10. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 14-10。*显示模式元数据

它是如何工作的

这段代码与您之前编写的代码有些不同。当调用GetSchemaTable方法时,返回一个数据表的填充实例。

// Store Person’s schema in a data table DataTable schema = rdr.GetSchemaTable();

您可以使用数据表来表示数据库中的完整表,可以是表示其架构的表的形式,也可以是保存所有原始数据以供脱机使用的表的形式。

在这个例子中,一旦获得了一个模式表,就可以通过DataTableRows属性检索一个行集合,通过DataTableColumns属性检索一个列集合。(您可以使用Rows属性向表中添加一个新行或删除一行,并且您可以使用Columns属性添加或删除一个现有列——我们将在第十五章中对此进行介绍。)表返回的每一行都描述了原始表中的一列,因此对于这些行中的每一行,都要使用嵌套的foreach循环逐个遍历列的模式信息。

// Display info from each row in the data table. // Each row describes a column in the database table. foreach (DataRow row in schema.Rows) {      foreach (DataColumn col in schema.Columns)     {          txtSchema.AppendText(col.ColumnName + " = " + row[col]);          txtSchema.AppendText("\n");     }          txtSchema.AppendText("-----------------"); }

注意如何在循环中使用DataColumn对象的ColumnName属性来检索当前的模式列名,然后通过使用熟悉的索引器风格的方法来检索与该列的定义相关的值,该方法使用了一个DataRow对象。有许多重载的索引器,这只是其中的一种方法。

通过数据读取器使用多个结果集

有时,您可能真的希望快速完成一项工作,并且还希望同时使用两个或更多查询来查询数据库。此外,您不希望通过实例化多个命令或数据读取器,或者通过一次又一次地使用相同的对象,并在运行过程中添加代码,以任何方式影响应用的整体性能。

那么,有没有办法让一个数据读取器遍历多个结果集呢?是的,数据读取器有一个方法NextResult(),它将读取器推进到下一个结果集。

尝试一下:处理多个结果集

在这个例子中,您将使用NextResult()来处理多个结果集。

  1. 选择 DataReader 项目,右键单击,然后选择“添加外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Windows 窗体”。在打开的对话框中,确保选择了 Windows 窗体,并将 Form1.cs 重命名为 MultipleResults.cs,单击“确定”将该窗体添加到 DataReader 项目中。

  2. 选择 MultipleResults 表单,并将 Size 属性的宽度设置为 358,高度设置为 516。

  3. 将 TextBox 控件拖到窗体上,并将其放在窗体的中间。选择此文本框,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 txtResult。
    • 将位置属性的 X 设置为 12,Y 设置为 12。
    • 将 Multiline 属性设置为 True。
    • 将 ScrollBars 属性设置为垂直。
    • 将 Size 属性的宽度设置为 318,高度设置为 454。
  4. Now your MultipleResults form in the Design view should like Figure 14-11. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 14-11。*多结果表单的设计视图

  5. Now double-click the empty surface of the MultipleResults.cs form, and it will open the code editor window, showing the SchemaTable_Load event. Modify the MultipleResults_Load event to look like Listing 14-6.

    ***清单 14-6。*T4MultipleResults.cs

    `using System.Data.SqlClient;

    private void MultipleResults_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @“server=.\sql2012;database=AdventureWorks;
                                      Integrated Security=SSPI”;

    // Query1
                string sql1 = @"select CountryRegionCode,Name
                                from Person.CountryRegion
                                where Name like ‘A%’ ";

    //Query2
                string sql2 = @“select FirstName, LastName
                                from Person.Contact”;

    //Combining queries to produce multiple result set
                string sql = sql1 + sql2;

    // Create connection
                SqlConnection conn = new SqlConnection(connString);
                try
                {
                    // Open connection
                    conn.Open();

    // Create command
                    SqlCommand cmd = new SqlCommand(sql, conn);

    // Create data reader
                    SqlDataReader rdr = cmd.ExecuteReader();

    // Loop through result sets
                    do
                    {
                        txtResult.AppendText(rdr.GetName(0));
                        txtResult.AppendText(“\t\t”);
                        txtResult.AppendText(rdr.GetName(1));
                        txtResult.AppendText(“\n”);
                        txtResult.AppendText(“”.PadLeft(30, ‘=’));
                        txtResult.AppendText(“\n”);

    while (rdr.Read())
                        {
                            // Print one row at a time                         txtResult.AppendText(rdr[0].ToString());
                            txtResult.AppendText(“\t\t\t”);
                            txtResult.AppendText(rdr[1].ToString());
                            txtResult.AppendText(“\n”);
                        }
                    }
                    while (rdr.NextResult());

    // Close data reader
                    rdr.Close();
                }
                catch (SqlException ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace,“Exception Details”);
                }
                finally
                {
                    // Close connection
                    conn.Close();
                }`

  6. To set the MultipleResults form as the start-up form, modify the Program.cs statement. Application.Run(new SchemaTable ());

    表现为:

    Application.Run(new MultipleResults());

  7. Build the project, and run it by pressing Ctrl+F5. You should see the results in Figure 14-12. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 14-12。*处理多个结果集

它是如何工作的

这个程序本质上与第一个相同,DataLooper.cs ( 清单 14-1 )。这里,您定义了两个独立的查询,然后将它们组合起来。

`// Query1
            string sql1 = @"select CountryRegionCode,Name
                            from Person.CountryRegion
                            where Name like ‘A%’ ";

//Query2
            string sql2 = @“select FirstName, LastName
                            from Person.Contact”;             //Combining queries to produce multiple result set
            string sql = sql1 + sql2;`

唯一的另一个变化是循环遍历结果集。将检索行的循环嵌套在遍历结果集的循环中。

`                // Loop through result sets
                do
                {
                    txtResult.AppendText(rdr.GetName(0));
                    txtResult.AppendText(“\t\t”);
                    txtResult.AppendText(rdr.GetName(1));
                    txtResult.AppendText(“\n”);
                    txtResult.AppendText(“”.PadLeft(30, ‘=’));
                    txtResult.AppendText(“\n”);

while (rdr.Read())
                    {
                        // Print one row at a time
                        txtResult.AppendText(rdr[0].ToString());
                        txtResult.AppendText(“\t\t\t”);
                        txtResult.AppendText(rdr[1].ToString());
                        txtResult.AppendText(“\n”);
                    }
                }
                while (rdr.NextResult());`

为了简化,我们让您在每个查询中只选择两个字符串列。扩展它来处理具有不同列数和列数据类型的结果表非常简单。

总结

在本章中,您使用了数据读取器来执行各种常见任务,从简单地遍历单个结果集到处理多个结果集。您了解了如何通过列名和索引来检索列的值,还了解了可用于处理不同数据类型的值的方法。您还了解了如何获取关于结果集的信息和模式信息。

在下一章,我们将讨论 ADO 真正有趣的方面。NET:使用数据集和数据适配器在与数据库断开连接时处理数据库数据。

十五、使用数据集和数据适配器

在第十四章中,您看到了如何使用数据读取器以连接的、只进、只读的方式访问数据库数据。通常,这就是您想要做的,而数据阅读器完全符合您的目的。

在这一章中,你将看到一个新的数据访问对象,即*数据集。*与数据读取器不同,数据读取器是实现System.Data.IDataReader接口的特定于数据提供者的类的对象,数据集是类System.Data.DataSet的对象,这是一个由所有数据提供者使用的独特的 ADO.NET 组件。数据集完全独立于数据源,既可以连接到数据源,也可以从数据源断开。它们的基本目的是提供存储在内存缓存中的数据的关系视图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意在另一个有点令人困惑的术语中,这个类被命名为DataSet,但是通用术语被拼写为数据集

那么,如果数据集不必连接到数据库,如何用数据填充它并将它的数据保存到数据库呢?这就是数据适配器的用武之地。将数据适配器视为数据集和数据源之间的桥梁。没有数据适配器,数据集就不能访问任何类型的数据源。数据适配器负责数据集的所有连接细节,用数据填充数据集,并更新数据源。

在本章中,我们将介绍以下内容:

  • Understand the object model
  • Working with datasets and data adapters
  • Propagate changes to data sources
  • be complicated by
  • Working with datasets and XML
  • Understanding typed and untyped datasets

了解对象模型

本章开始时,我们将快速介绍使用数据集和数据适配器需要了解的所有新对象。您将从查看数据集和数据读取器之间的差异开始,然后更详细地查看数据在数据集中的结构以及数据集如何与数据适配器协作。

数据集与数据读取器

如果你只是想读取和显示数据,那么你只需要使用一个数据读取器,就像你在上一章看到的那样,特别是当你处理大量数据的时候。在需要遍历数千或数百万行的情况下,您需要一个快速的顺序读取器(一次从结果集中读取一行),数据读取器以一种高效的方式完成这项工作。

如果您需要以任何方式操作数据,然后更新数据库,您需要使用数据集。数据适配器使用数据读取器填充数据集;需要额外的资源来保存数据以供离线使用。你需要思考你是否真的需要一个数据集;否则,你只是在浪费资源。除非您需要更新数据源或使用其他数据集功能,如读写 XML 文件、导出数据库架构和创建数据库的 XML 视图,否则应该使用数据读取器。

数据集简介

ADO.NET 的数据集概念是多层数据库应用开发世界中的一大步。当检索或修改大量数据时,在等待用户发出请求的同时保持与数据源的开放连接是对宝贵资源的巨大浪费。

数据集在这里非常有用,因为它们使您能够在本地缓存中存储和修改大量数据,以表格的形式查看数据,并以离线模式(换句话说,与数据库断开连接)处理数据。

我们来看一个例子。假设您试图通过互联网连接到远程数据库服务器,以获取一些商业事务的详细信息。您在特定日期搜索所有可用的事务,并显示结果。在后台,您的应用创建一个与数据源的连接,连接两个表,并检索结果。假设您现在想要编辑这些信息,并添加或删除细节。不管是什么原因,您的应用都会一遍又一遍地经历相同的循环:创建新的连接、连接表和检索数据。不仅每次创建一个新的连接会有开销,而且您可能会做许多其他多余的工作,尤其是当您处理相同的数据时。如果您可以连接到数据源一次,将数据本地存储在类似于关系数据库的结构中,关闭连接,修改本地数据,然后在适当的时候将更改传播到数据源,这样不是更好吗?

这正是数据集的设计目的。数据集将关系数据存储为*数据表的集合。*在上一章中,当一个System.Data.DataTable对象用来保存模式信息时,你简要地见过数据表。然而,在这种情况下,数据表只包含模式信息,但是在数据集中,数据表包含描述数据结构和数据本身的元数据。

图 15-1 显示了数据集架构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

***图 15-1。*数据集架构

该体系结构反映了关系数据库的逻辑设计。在本章中,您将看到如何使用数据表、数据行和数据列。

数据适配器简介

当您第一次实例化数据集时,它不包含任何数据。您可以通过将填充的数据集传递给数据适配器来获得它,数据适配器负责连接细节,并且是数据提供程序的一个组件。数据集不是数据提供程序的一部分。就像一个水桶,准备装水,但是需要一个外接管道让水进来。换句话说,数据集需要一个数据适配器来填充数据并支持对数据源的访问。

每个数据提供程序都有自己的数据适配器,就像它有自己的连接、命令和数据读取器一样。图 15-2 描述了数据集、数据适配器和数据源之间的交互。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

***图 15-2。*数据集、数据适配器和数据源交互

数据适配器构造函数被重载。您可以使用以下任何一种方法来获取新的数据适配器。我们使用 SQL Server 数据提供程序,但是其他数据提供程序的构造函数是类似的。

SqlDataAdapter  da  =   new   SqlDataAdapter(); SqlDataAdapter  da  =   new   SqlDataAdapter(cmd); SqlDataAdapter  da   =  new   SqlDataAdapter(sql, conn); SqlDataAdapter  da   =  new   SqlDataAdapter(sql, connString);

因此,您可以用四种方式创建数据适配器:

  • You can use its parameterless constructor (assign SQL and join later).
  • You can pass a command to its constructor (here, cmd is a SqlCommand object).
  • You can pass an SQL string and a connection.
  • You can pass an SQL string and a connection string.

您将很快看到所有这些都在起作用。现在,我们将继续介绍如何使用数据表、数据列和数据行。您将在接下来的章节中使用这些内容。

数据表、数据列和数据行的简要介绍

数据表是类System.Data.DataTable的一个实例。它在概念上类似于关系表。如图图 15-1 所示,一个数据表有数据行和数据列的集合。您可以通过数据表的RowsColumns属性来访问这些嵌套集合。

一个数据表可以代表一个独立的表,或者在一个数据集内,正如你将在本章中看到的,或者作为一个由另一个方法创建的对象,正如你在上一章中看到的,当一个数据表通过调用数据读取器上的GetSchemaTable方法被返回时。

数据列表示数据表中列的模式,然后可用于设置或获取列属性。例如,您可以使用它通过为数据列的DefaultValue属性赋值来设置列的默认值。

您可以使用数据表的Columns属性获取数据列的集合,该属性的索引器接受列名或从零开始的索引,例如(其中dt是数据表):

DataColumn col = dt.Columns["ContactName"]; DataColumn col = dt.Columns[2];

数据行代表一行中的数据。您可以以编程方式添加、更新或删除数据表中的行。要访问数据表中的行,可以使用它的Rows属性,该属性的索引器接受从零开始的索引,例如(其中dt是数据表):

DataRow row = dt.Rows[2];

这是足够的理论了。是时候做一些编码了,看看这些对象在实践中是如何协同工作的!

使用数据集和数据适配器

数据集构造函数被重载。

DataSet ds = new DataSet(); DataSet ds = new DataSet("MyDataSet");

如果使用无参数构造函数,数据集名称默认为NewDataSet。如果需要多个数据集,最好使用另一个构造函数并显式命名它。但是,您总是可以通过设置其DataSetName属性来更改数据集名称。

您可以通过多种方式填充数据集,包括以下方式:

  • Use data adapter
  • Read from XML document

在本章中,我们将使用数据适配器。但是,在“使用数据集和 XML”一节中,您将快速浏览一下第二种方法的相反情况,您将从数据集编写 XML 文档。

试试看:用数据适配器填充数据集

在本例中,您将创建一个数据集,用数据适配器填充它,然后显示它的内容。

  1. 创建一个名为 Chapter15 的新 Windows 窗体应用项目。当解决方案资源管理器打开时,保存解决方案。

  2. 将 Chapter15 项目重命名为 DataSetandDataAdapter。

  3. 重命名Form1.cs文件以弹出DataSet.cs

  4. 通过单击窗体的标题栏选择 PopDataSet 窗体,并将 Size 属性的宽度设置为 301,高度设置为 342。

  5. 将 GridView 控件拖到窗体上,并将其放在窗体的左上角。选择此 GridView,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 gvProduct。
    • 对于 Location 属性,将 X 设置为 12,将 Y 设置为 12。
    • 将 ScrollBars 属性设置为 Both。
    • 对于 Size 属性,将 Width 设置为 263,Height 设置为 282。
  6. Now your PopDataSet form in the Design view should look like Figure 15-3. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 15-3。*pop dataset 表单的设计视图

  7. Double-click the empty surface of the PopDataSet.cs form, and it will open the code editor window, showing the PopDataSet_Load event. Modify the PopDataSet_Load event to look like Listing 15-1.

    清单 15-1。 PopDataSet.cs

    `Using System.Data.SqlClient;

    private void PopDataSet_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @" server=.\sql2012;database=AdventureWorks;
                                Integrated Security=true";

    // Query
                string sql = @“select Name,ProductNumber
                               from Production.Product
                               where SafetyStockLevel > 600”;

    // Create connection
                SqlConnection conn = new SqlConnection(connString);             try
                {
                    // Open connection
                    conn.Open();

    // Create Data Adapter
                    SqlDataAdapter da = new SqlDataAdapter(sql, conn);

    // Create Dataset
                    DataSet ds = new DataSet();

    // Fill Dataset
                    da.Fill(ds, “Production.Product”);

    // Display data
                    gvProduct.DataSource = ds.Tables[“Production.Product”];
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace);
                }
                finally
                {
                    //Connection close
                    conn.Close();
                }
            }`

  8. Build the project, and run the DataLooper form by pressing Ctrl+F5. Your results should look like 15-4. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 15-4。*填充数据集

它是如何工作的

定义查询并打开连接后,创建并初始化数据适配器。

// Create Data Adapter SqlDataAdapter da = new SqlDataAdapter(sql, conn);

然后你创建一个数据集。

// Create Dataset DataSet ds = new DataSet();

在这个阶段,你所拥有的只是一个空的数据集。关键是在数据适配器上使用Fill方法来执行查询、检索数据和填充数据集。

// Fill Dataset da.Fill(ds, "Production.Product");

Fill方法在内部使用数据读取器来访问表模式和数据,然后使用它们来填充数据集。

注意,这个方法不仅仅用于填充数据集。它有许多重载,如果需要,还可以用于填充没有数据集的单个数据表。

如果您没有向Fill方法提供表的名称,它将被自动命名为TableN,其中N以空字符串开始(第一个表名就是Table),并在每次向数据集中插入新表时递增。更好的做法是显式命名数据表,但这并不重要。

如果对已经包含数据的数据集多次运行相同的查询,Fill()会更新数据,跳过根据模式重新定义表的过程。

这里值得一提的是,下面的代码会产生相同的结果。除了将 SQL 和连接传递给数据适配器的构造函数之外,您还可以使用使用适当的 SQL 和连接创建的命令来设置它的SelectCommand属性。

// Create data adapter SqlDataAdapter da = new SqlDataAdapter(); da.SelectCommand = new SqlCommand(sql, conn);

有了填充的数据集,您现在可以访问各个数据表中的数据。(这个数据集只包含一个数据表。)

// get data table DataTable dt = ds.Tables["Production.Product"];

最后,使用嵌套的foreach循环来访问每行中的列,并将它们的数据值输出到屏幕上。

// display data gvProduct.DataSource = ds.Tables["Production.Product"];

数据集中的过滤和排序

在前面的例子中,您看到了如何从数据集中提取数据。然而,如果您正在处理数据集,很可能您想要对数据做更多的事情,而不仅仅是显示它。通常,您会希望对数据进行动态过滤或排序。在下面的示例中,您将看到如何使用数据行来实现这一点。

试试看:对数据集中的数据进行动态过滤和排序

我们将从 Customers 表中获取所有的行和列,只过滤德国客户的结果,并按公司排序。我们将使用一个单独的查询来查找产品,并在同一个数据集中填充两个数据表。

  1. 选择 DataSetandDataAdapter 项目,右键单击,然后选择添加外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Windows 窗体。从打开的对话框中,确保选择了 Windows 窗体,并将Form1.cs重命名为FilterSort.cs。单击“确定”将该表单添加到 DataSetandDataAdapter 项目中。

  2. 通过单击窗体的标题栏选择 FilterSort 窗体,并将 Size 属性的宽度设置为 350,高度设置为 489。

  3. 将文本框控件拖到窗体上,并将其放在窗体的中央。选择此 TextBox 控件,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 txtSort。
    • 对于 Location 属性,将 X 设置为 12,将 Y 设置为 8。
    • 将 Multiline 属性设置为 True。
    • 将 ScrollBars 属性设置为垂直。
    • 对于“大小”属性,将“宽度”设置为 312,将“高度”设置为 435。
    • 将文本属性留空。
  4. Now your FilterSort form in the Design view should look like Figure 15-5. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 15-5。FilterSort 表单的设计视图

  5. Double-click the empty surface of the FilterSort.cs form, and it will open the code editor window, showing the FilterSort_Load event. Modify the FilterSort_Load event to look like Listing 15-2.

    ***清单 15-2。*T4FilterSort.cs

    `Using System.Data.SqlClient;
            private void FilterSort_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @“server=.\sql2012; database=AdventureWorks;
                               Integrated Security=true”;

    // Query
                string sql1 = @" select *
                               from Production.Product
                               where Name Like ‘Mountain%’";

    string sql2 = @" select *
                               from Production.Location where CostRate > 10.0 ";

    // Combine queries
                string sql = sql1 + sql2;

    // Create connection
                SqlConnection conn = new SqlConnection(connString);

    try
                {
                    // Create Data Adapter
                    SqlDataAdapter da = new SqlDataAdapter();
                    da.SelectCommand = new SqlCommand(sql, conn);

    // Create and Fill Data Set
                    DataSet ds = new DataSet();
                    da.Fill(ds, “Production.Product”);

    // Get the data tables collection
                    DataTableCollection dtc = ds.Tables;

    // Display data from first data table
                    //
                    // Display output header
                    txtSort.AppendText(“Results from Product table:\n”);
                    txtSort.AppendText(“*******************************\n”);
                    txtSort.AppendText(“Name\t\t\t\tProductNumber\n”);

    txtSort.AppendText(“_________________________________________\n”);

    // set display filter
                    string fl = “Color = ‘Black’”;

    // Set sort
                    string srt = “ProductNumber asc”;

    // display filtered and sorted data
                    foreach (DataRow row in dtc[“Production.Product”].Select(fl, srt))
                    {
                        txtSort.AppendText(row[“Name”].ToString().PadRight(25));
                        txtSort.AppendText(“\t\t”);
                        txtSort.AppendText(row[“ProductNumber”].ToString());
                        txtSort.AppendText(Environment.NewLine);
                    }

    txtSort.AppendText(“============================================\n”);
                    // Display data from second data table

    // Display output header

    txtSort.AppendText(“Results from Location table:\n”);
                    txtSort.AppendText(“***********************************\n”);                 txtSort.AppendText(“Name\t\t\tCostRate\n”);
                    txtSort.AppendText(“__________________________________________\n”);

    // Display data
                    foreach (DataRow row in dtc[1].Rows)
                    {
                        txtSort.AppendText(row[“Name”].ToString().PadRight(25));
                        txtSort.AppendText(“\t”);
                        txtSort.AppendText(row[“CostRate”].ToString() );
                        txtSort.AppendText(Environment.NewLine);
                    }
                }

    catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace);
                }

    finally
                {
                    // Connection close
                    conn.Close();
                }
            }`

  6. To set the FilterSort form as the start-up form, modify the Program.cs statement. Application.Run(new PopDataSet());

    表现为:

    Application.Run(new FilterSort());

    构建项目,并通过按 Ctrl+F5 运行它。你的结果应该看起来像图 15-6 。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 15-6。*过滤整理数据表

它是如何工作的

您编码并组合两个查询,以便在同一个连接上执行。

`// Query1
            string sql1 = @" select *
                           from Production.Product
                           where Name Like ‘Mountain%’";

// Query2
            string sql2 = @" select *
                           from Production.Location
                           where CostRate > 10.0 ";

// Combine queries
string sql = sql1 + sql2; // Create connection
SqlConnection conn = new SqlConnection(connString);`

您创建一个数据适配器,为其属性SelectCommand分配一个封装查询和连接的命令(供数据适配器的Fill方法内部使用)。

// Create Data Adapter SqlDataAdapter da = new SqlDataAdapter(); da.SelectCommand = new SqlCommand(sql, conn);

然后创建并填充一个数据集。

// Create and Fill Data Set DataSet ds = new DataSet(); da.Fill(ds, "Production.Product");

每个查询返回一个单独的结果集,每个结果集存储在一个单独的数据表中(按照指定查询的顺序)。第一个表被明确命名为 Product 第二个被赋予默认名称 Location。

您从数据集的Tables属性中获取数据表集合,以便于以后引用。

// get the data tables collection DataTableCollection dtc = ds.Tables;

作为显示第一个数据表的一部分,您声明了两个字符串。

// Set display filter string fl = "Color = 'Black'"; // Set sort string srt = "ProductNumber asc";

第一个字符串是一个指定行选择标准的过滤器表达式。它在语法上与 SQL WHERE子句谓词相同。您只需要颜色列等于黑色的那些行。第二个字符串指定您的排序标准,在语法上与 SQL ORDER BY子句相同,给出数据列名和排序顺序。

您使用一个foreach循环来显示从数据表中选择的行,将过滤器和排序字符串传递给数据表的Select方法。这个特定的数据表是数据表集合中一个名为 Product 的数据表。

               // display filtered and sorted data                 foreach (DataRow row in dtc["Production.Product"].Select(fl, srt))                 {                     txtSort.AppendText(row["Name"].ToString().PadRight(25));                     txtSort.AppendText("\t\t");                     txtSort.AppendText(row["ProductNumber"].ToString());                     txtSort.AppendText(Environment.NewLine);                 }

使用创建数据集时指定的表名,从数据表集合(dtc对象)中获取对单个数据表的引用。重载的Select方法在数据表上进行内部搜索,过滤掉不满足选择标准的行,按照规定对结果进行排序,最后返回一个数据行数组。您可以使用索引器中的列名来访问行中的每一列。

值得注意的是,如果您只是对客户数据使用不同的查询,您可以获得相同的结果,而且效率会高得多。

Select * From Production.Productionwhere Color= ‘Black’ order by ProductNumber asc

从性能的角度来看,这是理想的,但是只有当您需要的数据仅限于这个特定序列中的这些特定行时,这才是可行的。然而,如果您正在构建一个更复杂的系统,那么最好是从数据库中一次提取所有数据(就像您在这里所做的那样),然后以不同的方式对其进行过滤和排序。阿多。NET 丰富的操作数据集及其组件的方法套件为您提供了以最佳方式满足特定需求的广泛技术。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 提示一般来说,尽量利用 SQL,而不是编写 C# 程序,从数据库中获取你需要的数据。数据库服务器被优化来执行选择和排序,以及其他事情。查询可以比你在这本书里玩的那些更复杂更强大。通过仔细地(创造性地)对查询进行编码,以准确地返回您需要的内容,您不仅最小化了资源需求(内存、网络带宽等),而且减少了您必须编写的用于操作和格式化结果集数据的代码。

*第二个数据表中的循环有趣之处主要在于它的第一行,其中使用了序号索引:

foreach (DataRow row in dtc[l].Rows)

不要重命名第二个数据表(可以用它的TableName属性来重命名),最好使用索引而不是名称位置,因为在Fill()调用中更改名称需要您在这里进行更改,如果出现这种情况,这是不太可能的。

比较 FilterSort 和 PopDataSet

在第一个例子中,PopDataSet ( 清单 15-1 ),您看到了将数据放入数据集是多么简单。第二个例子,FilterSort ( 清单 15-2 ),只是一个变体,演示了如何处理多个结果集以及如何过滤和排序数据表。然而,这两个程序有一个主要的区别。你注意到了吗?

FilterSort不显式打开连接!事实上,这是您编写的第一个(但不会是最后一个)不支持这一点的程序。为什么不呢?

答案很简单,但是非常重要。当调用Fill()时,如果连接没有打开,则Fill方法自动打开连接。然后,它在填充数据集后关闭连接。然而,如果一个连接在Fill()被调用时是打开的,它会使用那个连接,而不会在之后关闭它。

因此,尽管数据集完全独立于数据库(和连接),但仅仅因为您正在使用数据集并不意味着您正在脱离数据库运行。如果要在断开连接的情况下运行,请使用数据集,但在填充数据集之前不要打开连接(或者,如果连接是打开的,请先将其关闭)。换句话说,数据集本质上是与数据库断开的。但是,这并不意味着使用数据集的应用已断开连接。

您将标准conn.Close();留在finally块中。因为Close()可以在关闭的连接上被正确调用,如果被不必要的调用,它不会出现问题,但是它肯定保证连接将被关闭,不管在try块中可能发生什么。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意如果你想自己证明这一点,只需在调用Fill()之前打开FilterSort中的连接,然后显示连接的State属性的值。会是Open。注释掉这个Open()调用,并再次运行它。State即将关闭。

使用数据视图

在前面的例子中,您看到了如何使用Select方法对数据表中的数据进行动态过滤和排序。然而,ADO.NET 有另一种方法来做同样的事情,而且做得更多:数据视图。一个数据视图(类System.Data.DataView的一个实例)使您能够创建存储在底层数据表中的数据的动态视图,反映对其内容和顺序所做的所有更改。这与Select方法不同,后者返回一个数据行数组,其内容反映数据值的变化,但不反映数据顺序。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意数据视图是数据表内容的动态表示。像 SQL 视图一样,它实际上并不保存数据。

试试看:用数据视图提炼数据

我们不会在这里涵盖数据视图的所有方面,因为它们超出了本书的范围。然而,为了展示如何使用它们,我们将给出一个简短的例子,使用数据视图来动态排序和过滤底层数据表。

  1. 选择 DataSetandDataAdapter 项目,右键单击,然后选择添加外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Windows 窗体。从打开的对话框中,确保选择了 Windows 窗体,并将Form1.cs重命名为DataViews.cs。单击“确定”将该表单添加到 DataSetandDataAdapter 项目中。

  2. 通过单击窗体的标题栏选择 DataViews 窗体,并将 Size 属性的宽度设置为 304,高度设置为 359。

  3. 将 GridView 控件拖到窗体上,并将其放在窗体的中央。选择此 GridView 控件,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 gvContact。
    • 对于 Location 属性,将 X 设置为 12,将 Y 设置为 12。
    • 将 ScrollBars 属性设置为垂直。
    • 对于“大小”属性,将“宽度”设置为 262,将“高度”设置为 298。
  4. Now your FilterSort form in the Design view should look like Figure 15-7. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 15-7。数据视图表单的设计视图

  5. Double-click the empty surface of the DataViews.cs form, and it will open the code editor window, showing the DataViews_Load event. Modify the DataViews_Load event to look like Listing 15-3.

    ***清单 15-3。*T4DataViews.cs

    `Using System.Data.SqlClient;
            private void DataViews_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @“server=.\sql2012;database=AdventureWorks;Integrated
    Security=true”;

    // Query
                string sql = @“select FirstName, MiddleName
                            from Person.Contact”;

    // Create connection
                SqlConnection conn = new SqlConnection(connString);

    try
                {
                    // Create Data Adapter
                    SqlDataAdapter da = new SqlDataAdapter();                 da.SelectCommand = new SqlCommand(sql, conn);

    // Create and Fill Dataset
                    DataSet ds = new DataSet();

    da.Fill(ds, “Person.Contact”);

    // Get Data Table reference
                    DataTable dt = ds.Tables[“Person.Contact”];

    // Create Data View
                    DataView dv = new DataView(dt,
                       “MiddleName = ‘J.’”,
                       “MiddleName”,
                       DataViewRowState.CurrentRows);

    // Display data from data view
                    gvContact.DataSource = dv;
                }

    catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace);
                }

    finally
                {
                    //Connection close
                    conn.Close();
                }
            }`

  6. To set the FilterSort form as the start-up form, modify the Program.cs statement. Application.Run(new FilterSort());

    表现为:

    Application.Run(new DataView());

    构建项目,并通过按 Ctrl+F5 运行它。您应该在图 15-8 中看到结果。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 15-8。*使用数据视图

它是如何工作的

这个程序与其他示例基本相同,所以我们将重点关注它对数据视图的使用。创建一个新的数据视图,并通过向其构造函数传递四个参数来初始化它。

// Create Data View DataView dv = new DataView(dt,                    "MiddleName = 'J.'",                    "MiddleName",                    DataViewRowState.CurrentRows);

第一个参数是数据表,第二个参数是数据表内容的筛选器,第三个参数是排序列,第四个参数指定要包含在数据视图中的行的类型。

System.Data.DataViewRowState是数据视图的底层数据表中行可以具有的状态的枚举。表 15-1 总结了这些状态。

每次添加、修改或删除一行时,它的行状态都会改变到表 15-1 中相应的行状态。如果您对基于特定行的状态(例如,数据表中的所有新行或所有已修改的行)检索、排序或筛选特定行感兴趣,这将非常有用。

然后,将数据视图作为数据源绑定到网格视图。

// display data from data view gvContact.DataSource = dv;

就像数据行代表数据表中的一行一样,数据行视图(也许称之为数据视图行会更好)代表数据视图中的一行。您为每个数据行视图检索经过筛选和排序的列数据,并将其输出到控制台。

正如这个简单的例子所表明的,数据视图提供了一种强大而灵活的方法来动态地改变数据表中的数据。

修改数据集中的数据

在接下来的几节中,您将通过一个实际的例子来演示以编程方式更新数据表中的数据的多种方法。请注意,这里您将只修改数据集中的数据,而不更新数据库中的数据。在“将更改传播到数据源”一节中,您将看到如何持久化对数据集所做的原始数据源更改。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意你对数据集的更改不会自动传播到数据库。要保存数据库中的更改,您需要再次连接到数据库并显式执行必要的更新。

试试看:修改数据集中的数据表

让我们在数据表中更新一行并添加一行。

  1. 选择 DataSetandDataAdapter 项目,右键单击,然后选择添加外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Windows 窗体。从打开的对话框中,确保选择了 Windows 窗体,并将Form1.cs重命名为ModifyDataTable.cs。单击“确定”将该表单添加到 DataSetandDataAdapter 项目中。

  2. 通过单击表单的标题栏选择 ModifyDataTable 表单,并将 Size 属性的宽度设置为 371,高度设置为 348。

  3. 将 GridView 控件拖到窗体上,并将其放在窗体的中央。选择此 GridView 控件,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 gvAddress。
    • 对于 Location 属性,将 X 设置为 12,将 Y 设置为 12。
    • 将 ScrollBars 属性设置为垂直。
    • 对于“大小”属性,将“宽度”设置为 331,将“高度”设置为 287。
  4. Now your ModifyDataTable form in the Design view should look like Figure 15-9. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 15-9。修改后的数据表的设计视图

  5. Double-click the empty surface of the ModifyDataTable.cs form, and it will open the code editor window, showing the ModifyDataTable _Load event. Modify the ModifyDataTable _Load event to look like Listing 15-4.

    ***清单 15-4。*T4ModifyDataTable.cs

    `Using System.Data.SqlClient;
            private void ModifyDataTable_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @“server=.\sql2012;database=AdventureWorks;Integrated
    Security=true”;

    // Query
                string sql = @“select  AddressLine2,City,StateProvinceID,PostalCode
                            from Person.Address
                            where City = ‘Bothell’”;

    // Create connection
                SqlConnection conn = new SqlConnection(connString);

    try
                {
                    // Create Data Adapter
                    SqlDataAdapter da = new SqlDataAdapter();
                    da.SelectCommand = new SqlCommand(sql, conn);

    // Create and Fill Dataset
                    DataSet ds = new DataSet();
                    da.Fill(ds, “Person.Address”);

    // Get data table reference
                    DataTable dt = ds.Tables[“Person.Address”];

    // FirstName column should be nullable
                    dt.Columns[“AddressLine2”].AllowDBNull = true;

    // Modify City in first row
                    dt.Rows[0][“City”] = “Wilmington”;

    // add a row
                    DataRow newRow = dt.NewRow();

    newRow[“PostalCode”] = “111111”;
                    newRow[“StateProvinceID”] = “80”;
                    newRow[“City”] = “Birmingham”;
                    dt.Rows.Add(newRow);

    // Display Rows
                    gvAddress.DataSource = dt;
                    gvAddress.Columns[0].Visible = false;
                    gvAddress.Rows[0].DefaultCellStyle.BackColor  =  Color.Red;             }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace);
                }

    finally
                {
                    // Connection close
                    conn.Close();
                }
            }`

  6. To set the ModifyDataTable form as the start-up form, modify the Program.cs statement. Application.Run(new DataView());

    表现为:

    Application.Run(new ModifyDataTable());

    构建项目,并通过按 Ctrl+F5 运行它。你的结果应该看起来像图 15-10 。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 15-10。*修改数据表

它是如何工作的

和以前一样,您在数据集中使用单个数据表。

// Get data table reference DataTable dt = ds.Tables["Person.Address"];

接下来,您可以看到一个如何更改模式信息的示例。您选择 FirstName 列,它的AllowNull属性在数据库中被设置为false,并且您将它更改为true

// AddressLine2 column should be nullable dt.Columns["AddressLine2"].AllowDBNull = true;

请注意,如果您知道列的索引是什么,您可以使用序号索引(例如,dt.Columns[ l ]),但是使用*选择所有列会降低可靠性,因为如果数据库表模式改变,列的位置可能会改变。

您可以使用相同的方法修改行。您只需选择适当的行,并将其列设置为您想要的任何值,当然,与列数据类型一致。下面一行显示数据集第一行的 City 列被更改为 Wilmington:

// Modify City in first row dt.Rows[o]["city"] = "Wilmington";

接下来,向数据表中添加一个新行。

`// Add a row
DataRow newRow = dt.NewRow();

newRow[“PostalCode”] = “111111”;
newRow[“StateProvinceID”] = “80”;
newRow[“City”] = “Birmingham”;
dt.Rows.Add(newRow);`

NewRow方法创建一个数据行(一个System.Data.DataRow实例)。使用数据行的索引器为其列赋值。最后,将新行添加到数据表中,调用数据表的Rows属性上的Add方法,该属性引用 rows 集合。

更新数据源需要了解有关数据适配器方法和属性的更多信息。现在让我们来看看这些。

将更改传播到数据源

您已经看到了数据适配器如何填充数据集的数据表。您还没有看到的是数据适配器如何更新和同步数据源与数据集中的数据。它有三个属性支持这一点(类似于它的SelectCommand属性,它支持查询)。

  • InsertCommand
  • UpdateCommandDeleteCommand

我们将简单描述一下InsertCommand,然后将其付诸实践。

插入命令属性

数据适配器使用InsertCommand属性将行插入到表中。在调用Update方法时,将搜索添加到数据表中的所有行,并将其传播到数据库。

尝试一下:向数据源传播新的数据集行

让我们向数据库传播一个新行,这是清单 15-5 中的另一个变体。

  1. 选择 DataSetandDataAdapter 项目,右键单击,然后选择“添加 Windows 窗体”。从打开的对话框中,确保选择了 Windows 窗体,并将Form1.cs重命名为PersistAdds.cs。单击“确定”将该表单添加到 DataSetandDataAdapter 项目中。

  2. 通过单击窗体的标题栏选择 PersistAdds 窗体,并将 Size 属性的宽度设置为 452,高度设置为 163。

  3. 将 TextBox 控件拖到窗体上,并将其放在窗体的中央。选择此 TextBox 控件,导航到“属性”窗口,并设置以下属性:

    • 将“名称”属性设置为 txtDepartment。
    • 对于 Location 属性,将 X 设置为 12,将 Y 设置为 12。
    • 对于“大小”属性,将“宽度”设置为 412,将“高度”设置为 95。
    • 将文本属性留空。
  4. Now your PersistAdds form in the Design view should look like Figure 15-11. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 15-11。*持久添加表单的设计视图

  5. Double-click the empty surface of the PersistAdds.cs form, and it will open the code editor window, showing the PersistAdds _Load event. Modify the PersistAdds _Load event to look like Listing 15-5.

    清单 15-5。PersistAdds.cs

    `Using System.Data.SqlClient;
           private void PersistAdds_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @" server=.\sql2012;database=AdventureWorks;Integrated
    Security=true";

    // Query
                string qry = @" select *
                               from HumanResources.Department
                                where GroupName = ‘Sales’";

    // SQL to insert employees
                string ins = @“insert into HumanResources.Department
                               (Name,GroupName, ModifiedDate)        
                                values(@Name, @GroupName, @ModifiedDate)”;

    // Create connection
                SqlConnection conn = new SqlConnection(connString);

    try
                {
                    // Create data adapter
                    SqlDataAdapter da = new SqlDataAdapter();
                    da.SelectCommand = new SqlCommand(qry, conn);

    // Create and fill data set
                    DataSet ds = new DataSet();
                    da.Fill(ds, “HumanResources.Department”);

    // Get data table reference
                    DataTable dt = ds.Tables[“HumanResources.Department”];

    // Add a row
                    DataRow newRow = dt.NewRow();
                    newRow[“Name”] = “Microsoft Development”;
                    newRow[“GroupName”] = “Global Development”;
                    newRow[“ModifiedDate”] = “2012-04-28”;
                    dt.Rows.Add(newRow);

    // Display rows
                    foreach (DataRow row in dt.Rows)
                    {

    txtDepartment.AppendText(row[“Name”].ToString());
                         txtDepartment.AppendText(“\t”);
                         txtDepartment.AppendText(row[“GroupName”].ToString());
                         txtDepartment.AppendText(“\t”);
                         txtDepartment.AppendText(row[“ModifiedDate”].ToString());                      txtDepartment.AppendText(“\n”);
                    }

    // Create command
                    SqlCommand cmd = new SqlCommand(ins, conn);
                    //
                    // Map parameters
                    cmd.Parameters.Add(“@Name”, SqlDbType.NVarChar, 50,“Name”);
                    cmd.Parameters.Add(“@GroupName”,SqlDbType.NVarChar,50,“GroupName”);
                    cmd.Parameters.Add(“@ModifiedDate”,SqlDbType.DateTime,25,“ModifiedDate”);

    // Insert department
                    da.InsertCommand = cmd;
                    da.Update(ds, “HumanResources.Department”);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace);
                }

    finally
                {
                    //Connection close
                    conn.Close();
                }
            }`

  6. To set the PersistAdds form as the start-up form, modify the Program.cs statement. Application.Run(new ModifyDataTable());

    表现为:

    Application.Run(new PersistAdds());

    构建项目,并通过按 Ctrl+F5 运行它。您应该在图 15-12 中看到结果。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 15-12。*增加一行

它是如何工作的

你添加一个INSERT语句。

string ins = @"insert into HumanResources.Department             (Name,GroupName, ModifiedDate)                     values(@Name, @GroupName, @ModifiedDate)";

INSERT查询创建一个命令。

// create command SqlCommand cmd = new SqlCommand(ins, conn);

然后配置命令参数。您将为其提供值的三列分别映射到一个命名的命令参数。您不需要提供主键值,因为它是由 SQL Server 生成的。

// Map parameters  cmd.Parameters.Add("@Name", SqlDbType.NVarChar, 50,"Name");  cmd.Parameters.Add("@GroupName",SqlDbType.NVarChar,50,"GroupName");  cmd.Parameters.Add("@ModifiedDate",SqlDbType.DateTime,25,"ModifiedDate");

最后,用插入到 Department 表中的命令设置数据适配器的InsertCommand属性,这样当您调用它的Update方法时,它将是数据适配器执行的 SQL。然后调用数据适配器上的Update,将更改传播到数据库。这里只添加了一行,但是由于 SQL 是参数化的,数据适配器将在 HumanResources 中查找所有新行。部门数据表,并向数据库提交所有这些数据表的插入内容。

// Insert department da.InsertCommand = cmd; da.Update(ds, "HumanResources.Department");

图 15-12 显示了新行,如果您使用数据库浏览器或 SQL Server 2012 的 SSMS 进行检查,您会看到该行已被传播到数据库。微软开发现在在部门表中。

指挥建设者

虽然很简单,但是为UpdateCommandInsertCommandDeleteCommand属性编写 SQL 语句有点麻烦,所以每个数据提供者都有自己的*命令构建器。*如果一个数据表对应一个数据库表,您可以使用命令生成器自动为数据适配器生成适当的UpdateCommandInsertCommandDeleteCommand属性。当调用数据适配器的Update方法时,这一切都是透明的。

为了能够动态生成INSERTDELETEUPDATE语句,命令生成器使用数据适配器的SelectCommand属性来提取数据库表的元数据。如果在调用Update方法后对SelectCommand属性进行了任何更改,您应该调用命令构建器上的RefreshSchema方法来相应地刷新元数据。

要创建命令生成器,需要创建数据提供程序的命令生成器类的实例,将数据适配器传递给其构造函数。例如,以下代码创建了一个 SQL Server 命令生成器:

SqlDataAdapter da = new SqlDataAdapter(); SqlCommandBuilder cb = new SqlCommandBuilder(da);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意为了使命令生成器工作,SelectCommand数据适配器属性必须包含一个查询,该查询返回数据库表的主键或唯一键。如果不存在,就会生成一个InvalidOperation异常,并且不会生成命令。

尝试一下:使用 SqlCommandBuilder

在这里,您将使用 SqlCommand Builder 向数据库中插入一行。

  1. 选择 DataSetandDataAdapter 项目,右键单击,然后选择添加外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Windows 窗体。从打开的对话框中,确保选择了 Windows 窗体,并将Form1.cs重命名为PersistAddsBuilder.cs。单击“确定”将该表单添加到 DataSetandDataAdapter 项目中。

  2. 通过单击窗体的标题栏选择 PersistAddsBuilder 窗体,并将 Size 属性的宽度设置为 483,高度设置为 151。

  3. 将 TextBox 控件拖到窗体上,并将其放在窗体的中央。选择此 TextBox 控件,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 txtDepartment。
    • 对于 Location 属性,将 X 设置为 12,将 Y 设置为 12。
    • 对于 Size 属性,将 Width 设置为 441,Height 设置为 89。
    • 将文本属性留空。
  4. Now your PersistAddsBuilder form in the Design view should look like Figure 15-13. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 15-13。*PersistAddsBuilder 表单的设计视图

  5. Double-click the empty surface of the PersistAddsBuilder.cs form, and it will open the code editor window, showing the PersistAddsBuilder _Load event. Modify the PersistAddsBuilder _Load event to look like Listing 15-6.

    清单 15-6。PersistAddsBuilder.cs

    `Using System.Data.SqlClient;
            private void PersistAddsBuilder_Load(object sender, EventArgs e)
            {
                // Connection string
                string connString = @“server=.\sql2012;database=AdventureWorks;Integrated
    Security=true”;

    // Query
                string qry = @" select *
                              from HumanResources.Department
                               where GroupName = ‘Research and Development’ ";

    // Create connection
                SqlConnection conn = new SqlConnection(connString);

    try
                {
                    // Create Data Adapter
                    SqlDataAdapter da = new SqlDataAdapter();
                    da.SelectCommand = new SqlCommand(qry, conn);

    // Create command builder
                    SqlCommandBuilder cb = new SqlCommandBuilder(da);

    // Create and Fill Dataset
                    DataSet ds = new DataSet();
                    da.Fill(ds, “HumanResources.Department”);

    // Get Data Table reference
                    DataTable dt = ds.Tables[“HumanResources.Department”];

    // Add a row
                    DataRow newRow = dt.NewRow();
                    newRow[“Name”] = “Language Design”;
                    newRow[“GroupName”] = “Research and Development”;
                    newRow[“ModifiedDate”] = “2012-04-29”;

    dt.Rows.Add(newRow);

    // Display rows
                    foreach (DataRow row in dt.Rows)
                    {
                        txtDepartment.AppendText(row[“Name”].ToString());
                        txtDepartment.AppendText(“\t\t”);
                        txtDepartment.AppendText(row[“GroupName”].ToString());
                        txtDepartment.AppendText(“\t”);
                        txtDepartment.AppendText(row[“ModifiedDate”].ToString());
                        txtDepartment.AppendText(“\n”);                  
                    }                 // Insert department
                    da.Update(ds, “HumanResources.Department”);
                  }

    catch (Exception ex)
                {
                    MessageBox.Show(ex.Message + ex.StackTrace);
                }

    finally
                {
                    //Connection close
                    conn.Close();
                }
            }`

  6. To set the PersistAddsBuilder form as the start-up form, modify the Program.cs statement. Application.Run(new PersistAdds());

    表现为:

    Application.Run(new PersistAddsBuilder());

    构建项目,并通过按 Ctrl+F5 运行它。您应该在图 15-14 中看到结果。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 15-14。*使用命令生成器添加一行

它是如何工作的

最值得注意的不是你添加的行(是的,只是一个加一个注释)和你替换的行一样多。

`// create command builder
SqlCommandBuilder cb = new SqlCommandBuilder(da);

// Add a row
 DataRow newRow = dt.NewRow();
 newRow[“Name”] = “Language Design”;
 newRow[“GroupName”] = “Research and Development”;  newRow[“ModifiedDate”] = “2012-04-29”;

dt.Rows.Add(newRow);`

显然,使用命令生成器比手工编码 SQL 更可取;但是,请记住,它们只对单个表有效,并且底层数据库表必须有一个主键或唯一键。此外,数据适配器SelectCommand属性必须有一个包含键列的查询。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意虽然所有五个数据提供者都在。NET Framework 类库有命令生成器类,但在定义它们的System.Data命名空间中不存在任何类或接口。因此,如果您想了解更多关于命令构建器的知识,最好从您感兴趣的构建器的描述开始。System.Data.DataSet类和System.Data.IDataAdapter接口定义了命令构建器与之交互的底层组件,它们的文档提供了对命令构建器的约束的非正式规范。

并发

您已经看到,用数据集和数据适配器更新数据库相对简单。然而,我们把事情过于简单化了;您一直假设在处理断开连接的数据集时,没有对数据库进行其他更改。

假设两个不同的用户试图对数据集中的同一行进行冲突性的更改,然后试图将这些更改传播到数据库。会发生什么?数据库如何解决冲突?哪一行首先更新,第二个更新,还是根本不更新?答案不清楚。正如许多现实世界中的数据库问题一样,这完全取决于各种因素。然而,ADO.NET 提供了基本级别的并发控制,旨在防止更新异常。细节已经超出了本书的范围,但是下面是一个很好的概念性的开始。

基本上,数据集标记所有添加、修改和删除的行。如果某一行被传播到数据库,但在填充数据集后被其他人修改了,则该行的数据操作操作将被忽略。这种技术被称为开放式并发,本质上是数据适配器的工作。当调用Update方法时,数据适配器试图协调所有的更改。这在用户很少争用相同数据的环境中工作得很好。

这种类型的并发不同于所谓的悲观并发,悲观并发在修改时锁定行(有时甚至在检索时)以避免冲突。大多数数据库管理器使用某种形式的锁定来保证数据完整性。

开放式并发的非连接处理对于成功的多层系统是必不可少的。鉴于数据库管理系统的悲观并发性,如何最有效地利用它是一个棘手的问题。现在不要担心这个问题,但是请记住,存在许多问题,您的应用越复杂,您就越有可能成为并发方面的专家。

使用数据集和 XML

XML 是. NET 中数据传输的基本媒介。事实上,XML 是 ADO.NET 的主要基础。数据集以 XML 格式在内部组织数据,并有多种方法来读写 XML。例如:

  • You can use ReadXmlSchema and WriteXmlSchema methods of System.Data.DataSet to import and export the data set structure as XML schema.
  • You can read the data (and optional mode) of the dataset from ReadXml() and use WriteXml(). to write it into an xml file, which is useful when exchanging data with another application or making a local copy of the dataset.
  • You can bind the dataset to an XML document (an instance of System.Xml.XmlDataDocument). Data set and data document are synchronized, so it can be modified by ADO.NET or XML operation.

让我们看看其中的一个例子:将数据集复制到 XML 文件中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意如果您不熟悉 XML,不要担心。ADO.NET 不需要任何详细的知识。当然,你知道的越多,你就越能清楚地理解正在发生的事情。

试试看:将数据集提取到 XML 文件中

您可以使用数据集的WriteXml方法将数据集的内容和模式保存在一个 XML 文件中,或者使用WriteXml()WriteXmlSchema()将它们保存在不同的文件中。WriteXml()被重载,在这个例子中,我们将展示一个提取数据和模式的版本。

  1. 选择 DataSetandDataAdapter 项目,右键单击,然后选择添加外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 Windows 窗体。从打开的对话框中,确保选择了 Windows 窗体,并将Form1.cs重命名为WriteXML.cs。单击“确定”将该表单添加到 DataSetandDataAdapter 项目中。

  2. 通过单击窗体的标题栏选择 WriteXML 窗体,并将 Size 属性的宽度设置为 289,高度设置为 124。

  3. 将 Button 控件拖到窗体上,并将其放在窗体的中央。选择此按钮控件,导航到“属性”窗口,并设置以下属性:

    • 将 Name 属性设置为 btnXML。
    • 对于“位置”属性,将 X 设置为 74,Y 设置为 30。
    • 对于“大小”属性,将“宽度”设置为 128,将“高度”设置为 23。
    • 设置 Text 属性以生成 XML。
  4. Now your WriteXML form in the Design view should look like Figure 15-15. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    图 15-15。WriteXML 表单的设计视图

  5. Double-click the empty surface of the WriteXML.cs form, and it will open the code editor window, showing the WriteXML _Load event. Modify the WriteXML _Load event to look like Listing 15-7.

    ***清单 15-7。*T4WriteXML.cs

    `Using System.Data.SqlClient;
            private void btnXML_Click(object sender, EventArgs e)
            {
                // Connection string
                string connString = @" server=.\sql2012; database=AdventureWorks;
                                 Integrated Security=true";

    // Query
                string qry = @“select Name ,ProductNumber
                               from Production.Product”;

    // Create connection
                SqlConnection conn = new SqlConnection(connString);

    try
                {
                    // Create Data Adapter
                    SqlDataAdapter da = new SqlDataAdapter();
                    da.SelectCommand = new SqlCommand(qry, conn);

    // Open connection
                    conn.Open();

    // Create and Fill Dataset
                    DataSet ds = new DataSet();
                    da.Fill(ds, “Production.Product”);

    // Extract data set to XML file
                    ds.WriteXml(@“c:\productstable.xml”);
                    MessageBox.Show(“The XML file is Created”);
                }

    catch (Exception ex)
                { MessageBox.Show(ex.Message + ex.StackTrace);
                }

    finally
                {
                    // Connection close
                    conn.Close();
                }
            }`

  6. To set the WriteXML form as the start-up form, modify the Program.cs statement. Application.Run(new PersistAddsBuilder());

    表现为

    Application.Run(new WriteXML());

    构建项目,并通过按 Ctrl+F5 运行它。你的结果应该看起来像图 15-16 。

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 15-16。*将数据表提取为 XML

  7. Not much seems to have happened, but that’s because you wrote to a file rather than to the screen. Open productstable.xml (the path we saved this table to was c:; if you have changed the path, please refer to that) to see the XML. (One way in Visual Studio is to use File  Open File.) Figure 15-17 shows the XML extracted for the first five product rows. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 15-17。*提取为 XML 的数据表

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 提示默认情况下,提取的 XML 文档是纯文本文件。您可以在任何编辑器中打开productstable.xml文件,甚至可以使用typemore命令从命令行查看它。

它是如何工作的

首先,我们需要创建一个 Select 查询来从数据库中提取数据。

// Query  string qry = @"select Name ,ProductNumber             from Production.Product";

接下来,我们需要创建数据集和数据适配器

  // Create Data Adapter   SqlDataAdapter da = new SqlDataAdapter();   da.SelectCommand = new SqlCommand(qry, conn);

创建数据集和数据适配器后,将数据集存储到 xml 文件中。

`// Create and Fill Dataset
 DataSet ds = new DataSet();
 da.Fill(ds, “Production.Product”);

// Extract dataset to XML file
ds.WriteXml(@“c:\productstable.xml”);`

注意,XML 只是将数据集映射为一个层次结构。第一个 XML 元素<NewDataSet>是数据集名称(默认为NewDataSet,因为您没有指定名称)。下一个元素<Production.Product>使用数据表名称(因为只使用一个查询来填充数据集,所以只有一个数据表),它嵌套在数据集元素中。数据列元素<Name><ProductNumber>嵌套在该元素中。

每列的数据(以纯文本形式)出现在每个列元素的开始标记(例如,<Name>)和结束标记(例如,</Name>)之间。注意,<Production.Product>元素代表单个行,而不是整个表格。因此,列元素包含在每行的开始标记<Production.Product>和结束标记</Production.Product>中。

如果您滚动到 XML 文件的底部,您会发现数据集的结束标记</NewDataSet>

了解类型化和非类型化数据集

数据集可以是有类型的无类型的。到目前为止,您使用的数据集都是无类型的。他们是System.Data.DataSet的实例。非类型化数据集没有内置架构。该模式只是隐式的。当您向数据集中添加表和列时,它会增长,但是这些对象是作为集合而不是 XML 模式元素公开的。但是,正如我们在上一节中提到的,您可以使用WriteXmlSchema(或WriteXml)显式导出非类型化数据集的模式。

类型化数据集是从System.Data.DataSet派生的数据集,在声明数据集类时使用 XML 模式(通常在.xsd文件中)。模式中的信息(表、列等)被提取出来,生成 C# 代码,并进行编译,因此新的数据集类是一个实际的。NET 类型与适当的对象和属性。

类型化或非类型化数据集都同样有效,但是类型化数据集更有效,并且可以使代码更简单。例如,使用非类型化数据集,您需要这样写:

ds.Tables[o].Rows[o]["CompanyName"];

获取 Customers 表的 CompanyName 列的值,假设该数据表是数据集中的第一个。使用类型化数据集,您可以将它的数据表和数据列作为类成员来访问。您可以用下面的代码替换前面的代码:

 ds.Customers[o].CompanyName;

使代码更加直观。此外,Visual Studio 代码编辑器具有对类型化数据集的智能感知支持。

类型化数据集比非类型化数据集更有效,因为类型化数据集有一个已定义的模式,当它们被数据填充时,运行时类型识别和转换是不必要的,因为这已经在编译时处理了。每次加载结果集时,非类型化数据集都有更多的工作要做。

然而,类型化数据集并不总是最佳选择。如果您正在处理定义不明确、定义动态变化或者只是暂时感兴趣的数据,非类型化数据集的灵活性可能会超过类型化数据集的优势。

这一章已经够长了。因为我们不关心小样本程序的效率,所以我们不会使用类型化数据集,我们也不需要在这里介绍如何创建它们。

本书的重点是通过向你展示如何编写基本操作来解释 C# 如何与 ADO.NET 一起工作。如果你能自己编写代码,你就能理解 C# 在为你生成东西时做了什么,就像下一章关于使用 Windows 窗体一样。这对理解如何配置生成的组件和调试使用它们的应用是非常宝贵的。

虽然您可以自己编写一个.xsd文件(或者用System.Data.DataSet.WriteXmlSchema()为非类型化数据集导出一个 XSL 模式并修改它),然后使用xsd.exe实用程序为类型化数据集创建一个类,但是这需要做大量的工作,容易出错,而且您很少(如果有的话)想要或需要这样做。

总结

在本章中,我们介绍了数据集和数据适配器的基础知识。数据集是具有数据表集合的数据的关系表示,每个数据表都具有数据行和数据列的集合。数据适配器是一个对象,它控制如何将数据加载到数据集(或数据表)中,以及如何将数据集数据的更改传播回数据源。

我们介绍了填充和访问数据集的基本技术,演示了如何过滤和排序数据表,并指出尽管数据集是独立于数据库的对象,但断开连接的操作并不是默认模式。

我们讨论了如何使用参数化 SQL 和数据适配器的UpdateCommandInsertCommandDeleteCommand属性将数据修改传播回数据库,以及命令构建器如何简化单表更新。

我们简要地提到了并发的重要问题,然后介绍了 XML,ADO.NET 背后的基础技术。

最后,我们讨论了类型化和非类型化数据集。

既然你已经看到、理解并实践了 Windows 应用的 ADO.NET,在下一章中,我们将探索在 ASP.NET 应用中使用数据控制。*

十六、在 ASP.NET 应用中使用数据控件

本章重点介绍 web 应用开发背后的概念和 web 环境的关键组件,并向您展示如何在开发 web 应用时使用 ASP.NET 网站项目。(详细讨论 ASP.NET 框架超出了本书的范围。)

通常,一个功能完整的 web 项目需要在计算机上安装和配置 Internet 信息服务(IIS)。但是在本章中,为了简单起见,并帮助您理解使用数据控件的 ASP.NET 网站项目的基本原理,IIS 不是必需的。但是,如果您有它,您不需要卸载它。

在本章中,我将介绍以下内容:

  • Understanding web functions
  • Learn about ASP.NET and Web pages
  • Understand Visual Studio 2012 website types
  • Understand the layout of ASP.NET website.
  • 了解 ASP.NET 网络应用的软件工程师
  • Use the Repeater control

了解网络功能

当你在。NET 框架,您使用 ASP.NET Web 窗体来构建 Web 应用。Web 窗体技术在 ASP.NET 环境中工作,并接受来自任何。NET 兼容的语言,如 C#。

在深入研究 web 表单和学习如何开发 Web 应用之前,您需要了解是什么组件驱动了这项技术,以及这些组件如何服务于运行在 Web 上的各种应用。

基本上,有三个关键因素使所有的 web 应用发挥作用:web 服务器、web 浏览器和超文本传输协议(HTTP)。我们来看看他们的沟通过程:

  1. web 浏览器向 web 服务器发起对资源的请求。
  2. HTTP 向 web 服务器发送 GET 请求,web 服务器处理该请求。web 服务器发起响应;HTTP 将响应发送到 web 浏览器。
  3. 网络浏览器处理响应并在网页上显示结果。
  4. 用户输入数据或执行某些操作,迫使数据再次发送到 web 服务器。
  5. HTTP 将数据发送回 web 服务器,web 服务器处理这些数据。
  6. HTTP 将响应发送到 web 浏览器。
  7. 网络浏览器处理响应并在网页上显示结果。

现在,您已经对沟通过程有了大致的了解,让我们更仔细地看一下每个关键组件。

网络服务器

web 服务器负责通过 HTTP 接收和处理来自浏览器的所有请求。收到请求后,web 服务器将处理该请求并将响应返回给浏览器。在这之后,通常 web 服务器会关闭它与数据库的连接,并释放所有资源、打开的文件、网络连接等等,这些都成为 web 服务器上要处理的请求的一部分。

web 服务器完成所有这些数据、资源等的清理,以达到无状态。术语状态指的是在发送给服务器的请求和发送给浏览器的响应之间存储的数据。

当今的大多数网站都是作为应用运行的,并且由许多网页组成,一个网页上的数据通常负责将在下一个网页上显示的输出。在这种情况下,无状态就违背了这类网站的全部目的;因此,维护状态变得很重要。

为了有状态,web 服务器将通过预期来自 web 浏览器的额外请求,在一段时间内保持连接和资源活动。ASP.NET 提供了各种技术来应对无状态行为,比如 cookie、视图状态、查询字符串、会话和应用状态等等。

网络浏览器和 HTTP

web 浏览器是显示网页的客户端应用。web 浏览器使用 HTTP 向 web 服务器发送请求,然后 web 服务器使用用户想要查看或使用的数据来响应 web 浏览器或 web 客户端的请求。

HTTP 是一种通信协议,用于从 web 服务器请求网页,然后将响应发送到 web 浏览器。

了解 ASP.NET 和网页

所有人都可以去 ASP.NET。NET 开发者,因为它是微软的。NET 框架。ASP.NET 提供了一个 web 开发模型,通过使用任何。NET 兼容的语言,如 C#。ASP.NET 代码是编译的而不是解释的,它支持。NET 框架,如强类型、性能优化等等。在编译完代码后。NET CLR 将进一步将 ASP.NET 代码编译成本机代码,从而提高性能。

网页为您的 Web 应用提供用户界面。ASP.NET 为网页增加了可编程性。ASP.NET 使用代码实现应用逻辑,这些代码将被发送到服务器端执行。ASP.NET 的网页有以下特点:

  • Based on Microsoft ASP.NET technology, the code running on the server dynamically generates web pages and outputs them to browsers or client devices.
  • They are compatible with any supported language. NET common language runtime, including Microsoft Visual Basic and Microsoft Visual C#.
  • They are based on Microsoft. NET framework. This provides all the benefits of the framework, including managed environment, type safety and inheritance.

网页由服务于用户请求的应用代码组成;为此,ASP.NET 将代码编译到程序集中。程序集是包含应用元数据的文件,文件扩展名为.dll。代码编译后被翻译成一种独立于语言、独立于 CPU 的格式,称为微软中间语言 (MSIL),也称为中间语言 (IL)。运行网站时,MSIL 在。并被翻译成 CPU 特定的指令,供运行 web 应用的 PC 上的处理器使用。

了解 Visual Studio 2012 网站类型

Visual Studio 2012 提供了多种创建 web 项目或网站的方法。尽管网站只适用于 Internet 或 intranets,但 Visual Studio 2012 根据位置有三种类型,可以作为 web 开发人员工作的任何网站的基础。拥有这些选项的目的是它们真正简化了开发人员机器上的系统需求。

如果你曾经使用过经典的 ASP 应用(不是 ASP。回想一下 Visual Studio 6.0 的时代,那时开发人员需要使用 Internet 信息服务(IIS)来处理和测试 ASP web 应用。随着 Visual Studio 的发展,这个问题已经得到解决;现在,您可以在计算机上不安装 IIS 的情况下开发网站。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意 IIS 是一个灵活、安全、易于管理的网络服务器,Windows 可以在网上托管任何东西。IIS 为其内部托管的 web 应用提供了完整的 web 管理工具。

通过访问文件外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传新建外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传网站,可以在 Visual Studio 2012 IDE 中构建一个新的网站项目。

让我们来看看 Visual Studio 2012 提供的网站类型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 在下面的部分中,我在可用的项目模板列表中选择了 ASP.NET 空网站。以下部分(关于文件系统、HTTP 和 FTP 网站)适用于列表中所有可用的网站模板,如图图 16-1 所示。

文件系统网站

基于文件系统的网站像任何其他文件夹结构一样存储在计算机上。这种类型的网站的主要特点是,它使用一个非常轻量级的 ASP.NET 开发服务器,该服务器是 Visual Studio 2012 的一部分,因此它不要求 IIS 在开发人员的本地计算机上可用。

为了查看或测试文件系统网站,ASP.NET 开发服务器充当 web 服务器的角色。ASP.NET 开发服务器是一个在您的 Windows 计算机上本地运行的服务器,为 ASP.NET 网页提供服务,这使它适合于测试您的基于文件系统的 web 应用。

图 16-1 显示新建网站对话框,网站位置设置为文件系统;还要注意存储该网站的文件夹的路径:磁盘上的本地路径。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

***图 16-1。*指定文件系统网站

FTP 网站

基于文件传输协议(FTP)的网站可以帮助您在本地计算机和远程网站之间管理和传输文件。FTP 网站提供了一个类似 Windows 资源管理器的界面,并公开了文件夹结构,文件、文档等保存在该结构中以供共享。

您可以访问 FTP 站点,将文件从远程 FTP 站点共享、传输或下载到本地计算机,也可以将文件上载到远程 FTP 站点。

若要查看或测试 FTP 网站,服务器计算机必须有一个可浏览的位置,即指向与 FTP 站点相同的文件的 HTTP URL。

图 16-2 显示了新网站对话框,其中网站位置设置为 FTP。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

***图 16-2。*指定一个 FTP 网站

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意建立 FTP 站点需要用户的凭证通过。通常没有匿名的 FTP 站点;您应该使用ftp://user:pwd@ftpaddress:port语法指定 FTP 地址。

http 网站

基于超文本传输协议(HTTP)的网站更适合于构建基于 web 的商业和企业产品。HTTP 网站需要开发人员本地计算机上的 IIS,因为它被配置为 IIS 虚拟目录中的应用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意 IIS Express 是包含在 Visual Studio 2012 中的 IIS 的简化版。IIS Express 服务器为 IIS Express 中的 web 应用带来了大量管理功能。

图 16-3 显示了网站位置设置为 HTTP 的新建网站对话框。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

***图 16-3。*指定 HTTP 网站

了解 ASP.NET 网站的布局

当您选择 Visual Studio 提供的模板而不是 ASP.NET 空网站时,Visual Studio 2012 提供了大多数大型 Web 应用所需的许多表单和组件。为了使事情简单并帮助您保持专注,让我们使用一个空网站,向它添加一个 web 表单,并探索它的布局。

打开 Visual Studio 2012 IDE,选择文件外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传新建外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传网站。在新建网站对话框中,选择 ASP.NET 空网站作为项目模板,然后选择文件系统作为位置,Visual C# 作为语言,如图图 16-1 所示。在“Web location”下拉框旁边的文本框中,将路径修改为目录路径,这表示您将在文件系统上创建一个名为 Chapter16 的网站。单击确定。创建项目后,它将会打开,如图 16-4 中的所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

***图 16-4。*空文件系统网站的布局

这个空的 web 应用加载时只有一个组件,那就是web.config文件,下面将会解释。

web.config 文件

web.config文件是一个 web 项目非常重要的文件。该文件通过提供一个中心位置来帮助开发人员,在该位置可以设置各种操作(如数据库连接、调试模式等)所需的所有设置,并且这些设置将在整个项目中应用和访问。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意如果选择文件系统作为存储位置,web.config文件不会自动添加到 ASP.NET 网站项目中。

web.config文件的另一个特性是它很容易读写,就像记事本文件一样,因为它是 XML 格式的。

web.config文件有许多预定义的标签,帮助您组织 web 应用的配置设置。需要记住的最重要的事情是,所有的标签都需要嵌入到父标签<Configuration> </Configuration>中。

了解 ASP.NET Web Apps 的 Web UI

ASP.NET 应用或网站的 UI 是一个带有扩展名.aspx的 web 表单或网页,这意味着活动服务器页面。

每个 web 表单或网页都将包含 HTML 格式的 UI 设计或表示,以及相关联的代码隐藏文件中的代码功能,扩展名为.cs。因此,如果你的表单是Default.aspx,这代表演示文稿,而Default.aspx.cs文件代表代码。您将在本章的后面使用这些文件。与被称为经典 ASP 的 ASP 的旧版本不同,这种方法有助于将表示与逻辑分开,并使开发人员易于使用。

此外,如前几章所示,ASP。基于. NET 的应用能够包含多个页面。您将一个表单作为加载应用的默认页面,然后在页面之间移动。与 Windows 窗体不同,ASP.NET 使用不同的机制在确认事件后切换到另一个窗体。该机制被称为重定向,这在 ASP.NET 的Response对象下可用。在本章的后面,你将看到这些最广泛使用的函数和对象是如何一起工作的。

试试看:使用 Web 表单

在本练习中,您将向项目中添加一个带有基本控件的 web 窗体,然后向控件中添加所需的功能。

  1. 导航到 Solution Explorer,选择 Chapter16 项目,右键单击它,并选择 Add New Item。

  2. 在“添加新项”对话框中,修改窗体名称以显示为 Login,并确保“语言”下拉列表显示 Visual C#。单击 OK 将登录表单添加到您的项目中。

  3. Right-click the Login.aspx web form, and select the View Designer option; this will open the Login.aspx page in the Design view, where you can drag and drop controls onto the web page, as shown in Figure 16-5. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 16-5。*新增网页的设计视图

  4. In the Toolbox, from the Standard bar, drag a Label control (named Label1) onto the form inside the area titled div, as shown in Figure 16-5. Select the Label control, and if the Properties window is not shown already, press F4. Go to the properties of the Label control, and set its Id property to lblUserName and its Text property to Enter User Name.

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传 注意就像 Windows 窗体和 Windows 控件的 Name 属性一样,ASP.NET 有一个控件名称的 Id 属性。

  5. 将一个 TextBox 控件(Id 为 TextBox1)拖到窗体上,并将其放在 Label 控件旁边。选择 TextBox,并将其 Id 属性更改为 txtUserName。

  6. Drag a Button control (named Button1) onto the form, and place it next to the TextBox control. Select the Button control, and set its Id property to btnLogin and its Text property to Login. All three controls should appear in one line, as shown in Figure 16-6. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 16-6。*添加控件后登录表单的设计视图

  7. 现在,让我们像在步骤 2 中那样向应用添加一个 web 表单,并将该表单命名为 WebDataForm。

  8. Your web project will now have two forms, Login and WebDataForm, as shown in Figure 16-7. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 16-7。*展示两个 web 表单的项目

  9. Now let’s add the functionality. Open Login.aspx in the Design view by selecting it, right-clicking, and choosing View Designer. It will open as shown in Figure 16-6. Double-click the Login button, which will open the blank template for the btnLogin_click event. Modify the btnLogin_click event to look like Listing 16-1.

    ***清单 16-1。*T4Login.aspx.cs

        protected void btnLogin_Click(object sender, EventArgs e)     {         if (txtUserName.Text == "agarwal")         {             Response.Redirect("WebDataForm.aspx");         }         else         {             Response.Write("Invalid User Name, please try again!");         }     }

  10. Build the project and run the application by pressing Ctrl+F5. The Login.aspx form will appear in the browser. Enter a name in the provided text box, and click the Login button. If you will enter any other string than Agarwal, you should receive an “Invalid User Name, please try again!”error similar to that shown in Figure 16-8. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

***图 16-8。**运行并测试登录表单*
  1. 理想情况下,进入阿加瓦尔,会被带到WebDataForm.aspx。但是在我们尝试之前,让我们首先向 WebDataForm 表单添加功能。
它是如何工作的

首先,验证您是否有一个成功的日志。

             if (txtUserName.Text == "agarwal")

一旦这个条件为真,您希望用户能够自动导航到 WebDataForm。为此,您使用了 ASP。NET 的Response对象和它的Redirect函数,它接受你想重定向到的页面名称。

             Response.Redirect("WebDataForm.aspx");

如果登录不正确,我们还会向用户显示一条错误消息。为了显示这个错误,我们使用了Response对象的Write方法,该方法获取您想要在页面上显示的字符串。

     Response.Write("Invalid User Name, please try again!");

使用中继器控制

Repeater 控件是一个 ASP。特定于网络的控制;换句话说,它不存在于 Windows 窗体中。Repeater 控件用作容器控件,它允许您创建自定义列表以在网页上显示数据。Repeater 控件的另一个特性是它没有自己的内置呈现,这意味着 web 开发人员必须借助模板为 Repeater 控件提供布局。Repeater 控件能够使用各种类型模板,如 ItemTemplate、HeaderTemplate、SeperatorTemplate 和 FooterTemplate。

当网页加载 Repeater 控件时,Repeater 控件遍历数据源中的数据行,并为找到的每条记录呈现数据。

与 ListBox、GridView、TextBox 等在前面章节中用于数据库的控件不同,Repeater 控件没有默认外观;因此,它非常灵活和强大,可用于任何类型的列表,包括表格布局、逗号分隔的列表或 XML 格式的列表。

现在你已经有一半的应用准备好并开始工作了,如图 16-8 所示。下一个任务是向 WebDataForm 添加功能;为此,您将使用 ASP。NET 特定的数据控件称为 Repeater 控件,如前所述。

试试看:使用中继器控制

在本练习中,您将向 web 窗体添加一个 Repeater 控件,然后编写所需的功能,用数据库中的数据填充该控件。

  1. 导航到解决方案资源管理器。右键单击WebDataForm.aspx web 表单,选择视图设计器选项;这将在设计视图中打开WebDataForm.aspx页面,在这里您可以将控件拖放到 web 页面上。

  2. 导航到工具箱,展开 Data 选项卡,然后将 Repeater 控件拖到WebDataForm.aspx,将其定位在表单的左上角。

  3. Now press F4 to open the Properties window. Select the Repeater control, navigate to the Properties window, and set the Id property to RepData. Your WebDataForm will now look like Figure 16-9. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 16-9。*带有 Repeater 控件的 WebDataForm 的设计视图

  4. 现在您需要将数据访问代码绑定到 WebDataForm。像在 Windows 窗体应用中一样,双击 WebDataForm 的空白图面。这将打开Page_Load事件。

  5. Modify the Page_Load event of WebDataForm.aspx.cs to look like Listing 16-2.

    **清单 16-2。**T3WebDataForm.aspx.cs

    `using System.Data;
           using System.Data.SqlClient

    protected void Page_Load(object sender, EventArgs e)
           {
            // Connection string
            string connString = @“server=.\sql2012;database=AdventureWorks;
                             Integrated Security=true”;

    //Query
            string query = @" SELECT  Title, BirthDate
                           FROM    HumanResources.Employee";

    DataTable dt = new DataTable();

    try
            {
                SqlDataAdapter da = new SqlDataAdapter(query, connString);
                da.Fill(dt);
            }         catch (Exception ex)
            {
                Response.Write(ex.Message.ToString());  
            }

    //Populate Repeater control with data
            RepData.DataSource = dt;
            RepData.DataBind();
        }`

  6. 通过按 Ctrl+Shift+B 保存项目并构建代码。应该会成功构建。

  7. 因为添加了数据访问代码,所以需要启用 Repeater 控件来访问这些数据。为此,您需要调整 WebDataForm 的 HTML。

  8. Close WebDataForm.aspx.cs if open. Go to Solution Explorer, right-click WebDataForm, and select View Markup; you should see the HTML of WebDataForm.aspx, as shown in Figure 16-10. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    ***图 16-10。*web data form 的标记视图

  9. Modify the <body> HTML code segment to look like Listing 16-3.

    清单 16-3。 HTML 代码为 WebDataForm.aspx

    `
        
        

    HumanResources - Employee data


          


          <asp:Repeater id=RepData runat=“server”>


                


                  
                      
                      
                  
            
    TitleBirthDate


                
                  
                      <%# DataBinder.Eval(Container.DataItem, “Title”) %>
                  
                  
                      <%# DataBinder.Eval(Container.DataItem, “BirthDate”) %>
                  
                
            


                
            
          </asp:Repeater>
          


        

    `
  10. 保存项目,并按 Ctrl+Shift+B 构建项目。成功构建后,按 Ctrl+F5 运行 web 应用。

项目将加载Login.aspx,如图图 16-8 ,但这次你将输入正确的用户名 agarwal ,它被硬编码在登录按钮的if条件中。输入用户名后,点击登录按钮;你将被重定向到WebDataForm.aspx,如图图 16-11 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

***图 16-11。*运行和测试 WebDataForm

它是如何工作的

首先,您必须向WebDataForm.aspx.cs添加数据访问代码,这将构建查询,然后加载数据表。

`//Query
        string query = @" SELECT  Title, BirthDate
                      FROM    HumanResources.Employee";

DataTable dt = new DataTable();`

构建数据表后,使用这个DataTable对象填充DataAdapter对象。

        try         {             SqlDataAdapter da = new SqlDataAdapter(query, connString);             da.Fill(dt);         }

接下来,用数据填充 Repeater 控件。

      //Populate Repeater control with data         RepData.DataSource = dt;         RepData.DataBind();

总结

在这一章中,你学习了一些关于叫做 ASP.NET 的网络技术的基础知识。您还了解了可以在 Visual Studio 2012 中创建的各种类型的网站。您了解了如何处理 web 页面,然后使用数据访问代码和 Repeater 控件。在下一章,你将学习如何处理文本和二进制数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值