1.9 数据访问
1.9.1 数据访问概述
1.ADO.NET概述
ADO.NET是在用于直接满足用户开发可伸缩应用程序需求的ADO数据访问模型的基础上发展而来的。它是专门为Web设计的,并且考虑了伸缩性,无状态性和XML的问题。
ADO.NET使用了某些ADO的对象,如Connection和Command对象,并且还引入了新的对象。主要的新ADO.NET对象包括DataSet、DataReader和DataAdapter。
ADO.NET这一发展版本与以前的数据结构之间的重要区别就是DataSet对象的存在,该对象独立于任何数据存储区并且与之不同。正因为如此,DataSet对象作为一个独立实体运行。可以将数据集(DataSet)设想为始终断开的记录集,它对其所包含的数据的源或目标一无所知。DataSet的里面很像数据库,有表、列、关系、约束和视图等。
DataAdapter连接到数据库以填充DataSet的对象。然后,它又连接回数据库,根据DataSet保留数据时所执行的操作来更新数据库中的该数据。在过去,数据处理主要是基于连接的。现在,为了使多层应用程序更为高效,数据处理正转向基于消息的方式,围绕信息块进行处理。这种方式的中心是DataAdapter,它起着桥梁的作用,在DataSet和其源数据存储区之间进行数据检索和保存。这一操作是通过请求对数据存储区进行适当的SQL命令来完成的。
基于XML的DataSet对象提供一致的编程模型,该模型可配合所有的数据存储模型使用,无论是单层的、关系型的、还是分层的。为做到这一点,DataSet对象对其数据源“一无所知”,并且将它拥有的数据表示为集合和数据类型。无论DataSet中数据的源是什么,它都是通过由DataSet与其从属对象所公开的同一套标准API来操纵的。
虽然DataSet对其数据的源一无所知,但托管提供程序具有详细而具体的信息。托管提供程序的作用是在DataSet与数据存储区之间来回进行连接、填充和保持。OLE DB数据提供者与SQL Server.NET数据提供者(System.Data.OleDb和System.Data.SqlClient)是.Net框架的一部分,它们提供4个基本对象,即Command、Connection、DataReader和DataAdapter。在本节的余下部分,将介绍DataSet和OLE DB/SQL Server.NET数据提供程序的每一部分,解释它们是什么及如何对它们进行编程。
下面部分将介绍一些对象,其中有些是经过发展的,有些是全新的。这些对象如下:
- Connection 用于连接到数据库和管理对数据库的事务。
- Command 用于对数据库发出SQL命令。
- DataReader 用于从SQL Server数据源读取只进数据记录流。
- DataSet 用于对单层数据、XML数据和关系数据进行存储、远程处理和编程。
- DataAdapter 用于将数据推入DataSet,并使数据与数据库保持一致。
注意:当处理到数据库的连接时,有两个不同的选项:SQL Server.NET数据提供程序(System.Data.SqlClient)和OLE DB.NET数据提供程序(System.Data.OleDb)。在这些示例中,我们将使用SQL Server.NET数据提供程序。这些示例是为了直接与Microsoft SQL Server交互。OLE DB.NET数据提供程序则用于与任何OLE DB提供程序交互(因为它在底层使用OLE DB)。
2.Connection(连接)
连接用于与数据库“对话”,并由SQLConnection等特定于提供程序的类来表示。命令(Command)将遍历连接并以流的形式返回结果集,该结果集可由DataReader对象读取,或者被推入DataSet对象。
下面的示例说明如何创建连接对象。可通过调用连接上的Open方法来显式打开连接,也可在使用DataAdapter时隐式打开连接。
namespace HowTo.Samples.ADONET { using System; using System.Data.SqlClient; public class adooverview1 { public static void Main() { adooverview1 myadooverview1 = new adooverview1(); myadooverview1.Run(); } public void Run() { SqlConnection mySqlConnection = new SqlConnection("server=(local)\\netSDK;Trusted_Connection=yes;database=northwind"); try { mySqlConnection.Open(); Console.WriteLine("打开的连接 {0} ",mySqlConnection.ConnectionString); // Close the connection explicitly mySqlConnection.Close(); Console.WriteLine("已关闭。显式地关闭连接是很重要的。"); } catch { Console.WriteLine("未能打开到 {0} 的连接",mySqlConnection. ConnectionString); } } } }
3.Command(命令)
命令包含向数据库提交的信息,并且由SQLCommand等特定于提供程序的类来表示。命令可以是存储过程调用、UPDATE语句或返回结果的语句。还可将输入和输出参数,以及返回值用做命令语法的一部分。下面的示例说明如何对Northwind数据库发出INSERT语句。
namespace HowTo.Samples.ADONET { using System; using System.Data.SqlClient; public class adooverview2 { public static void Main() { adooverview2 myadooverview2 = new adooverview2(); myadooverview2.Run(); } public void Run() { string Message = null; SqlConnection myConnection = new SqlConnection("server=(local)\\netSDK; Trusted_ Connection=yes;database=northwind"); SqlCommand mySqlCommand = new SqlCommand("INSERT INTO Customers(CustomerId,CompanyName,ContactName,ContactTitle,Address) Values ('ABC','ABC Company','John Smith','Owner','One My Way')",myConnection); SqlCommand mySqlCleanup = new SqlCommand("DELETE FROM Customers WHERE CustomerId = 'ABC'",myConnection); try { myConnection.Open(); mySqlCleanup.ExecuteNonQuery(); // remove record that may have been entered previously. mySqlCommand.ExecuteNonQuery(); Message = "新记录已插入 northwind 中的 Customers 表。"; } catch(Exception e) { Message= "未能插入记录:" + e.ToString(); } finally { myConnection.Close(); } Console.Write(Message); } } }
4.DataReader
DataReader对象对于数据有点像只读/只进游标。DataReader API既支持平面数据,也支持分层数据。在对数据库执行命令之后返回一个DataReader对象。返回的DataReader对象的格式与记录集不同。例如,可以使用DataReader在Web页上显示搜索列表的结果。
namespace HowTo.Samples.ADONET { using System; using System.Data; using System.Data.SqlClient; public class adooverview3 { public static void Main() { adooverview3 myadooverview3 = new adooverview3(); myadooverview3.Run(); } public void Run() { SqlDataReader myReader = null; SqlConnection mySqlConnection = new SqlConnection("server=(local)\\netSDK; Trusted_Connection=yes;database=northwind"); SqlCommand mySqlCommand = new SqlCommand("select * from customers",mySqlConnection); try { mySqlConnection.Open(); myReader = mySqlCommand.ExecuteReader(); Console.Write("客户 ID "); Console.WriteLine("公司名称"); while (myReader.Read()) { Console.Write(myReader["CustomerId"].ToString() + " "); Console.WriteLine(myReader["CompanyName"].ToString()); } } catch(Exception e) { Console.WriteLine(e.ToString()); } finally { if (myReader != null) myReader.Close(); if (mySqlConnection.State == ConnectionState.Open) mySqlConnection.Close(); } } } }
5.DataSet和DataAdapter
(1)DataSet
DataSet对象与ADO Recordset对象相似,但功能更为强大,并具有另一重要区别,即DataSet始终是断开的。DataSet对象表示数据的缓存,具有类似数据库的结构,如表、列、关系和约束。但是,尽管DataSet可以并且的确像数据库那样运行,但重要的是要记住,DataSet对象不直接与数据或其他源数据进行交互。这使得开发人员能够使用始终保持一致的编程模型,而不用理会源数据的驻留位置。所有来自于数据库、XML文件、代码或用户输入的数据都可添加到DataSet对象中。这样,由于对DataSet进行了更改,所以在更新源数据之前可以对这些更改进行跟踪和验证。DataSet对象的GetChanges方法实际上是创建了另一个DatSet,该DatSet只包含对数据做出的更改。然后,DataAdapter(或其他对象)使用此DataSet来更新原始的数据源。
DataSet具有许多XML特性,包括生成和使用XML数据和XML架构的能力。XML架构可以用来描述通过WebServices交换的架构。实际上,为了类型安全和语句结束,可以对带有架构的DataSet进行编译。
(2)DataAdapter(OLEDB/SQL)
DataAdapter对象在DataSet与源数据之间起到桥梁的作用。在使用Microsoft SQL Server数据库时,使用提供程序特定的SqlDataAdapter(以及与其关联的SqlCommand和SqlConnection)能够提高整体性能。对于其他支持OLE DB的数据库,将使用OleDbDataAdapter对象及其关联OleDbCommand和OleDbConnection对象。
在更改DataSet之后,DataAdapter对象使用命令来更新数据源。使用DataAdapter的Fill方法调用SELECT命令;使用Update方法为每一发生更改的行调用INSERT、UPDATE或DELETE命令。可显式设置这些命令,以控制运行时用来解析更改的语句,包括对存储过程的使用。在特殊情况下,CommandBuilder对象可以根据Select语句在运行时生成这些命令。但是,在运行时生成需要一个额外的服务器往返以收集元数据,所以在设计时显式提供INSERT、UPDATE和DELETE命令将会获得更佳的运行时性能。代码如下所示:
SqlConnection myConnection = new SqlConnection("server=(local)\VSdotNET; Trusted_ Connection=yes;database=northwind"); SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter("select * from customers",myConnection); mySqlDataAdapter.InsertCommand.CommandText = "sp_InsertCustomer"; mySqlDataAdapter.InsertCommand.CommandType = CommandType.StoredProcedure; mySqlDataAdapter.DeleteCommand.CommandText = "sp_DeleteCustomer"; mySqlDataAdapter.DeleteCommand.CommandType = CommandType.StoredProcedure; mySqlDataAdapter.UpdateCommand.CommandText = "sp_UpdateCustomers"; mySqlDataAdapter.UpdateCommand.CommandType = CommandType.StoredProcedure; mySqlDataAdapter.Update(myDataSet);
记录将适当地映射到相应的给定命令,如图1-21所示。
图1-21 DataAdapter结构
下面示例阐释了通过SELECT语句加载一个DataAdapter。然后它在DataSet中更新、删除和添加一些记录。最后,通过DataAdapter将那些更新返回到源数据库。构造的DeleteCommand、InsertCommand和UpdateCommand将显示在页中。它还阐释了使用多个DataAdapter对象将多个表(Customers和Orders)加载到DataSet中。
namespace HowTo.Samples.ADONET { using System; using System.Data; using System.Data.SqlClient; public class adooverview4 { public static void Main() { adooverview4 myadooverview4 = new adooverview4(); myadooverview4.Run(); } public void Run() { // Create a new Connection and SqlDataAdapter SqlConnection myConnection = new SqlConnection("server=(local)\\netSDK; Trusted_ Connection=yes;database=northwind"); SqlDataAdapter mySqlDataAdapter = new SqlDataAdapter("Select * from Region",myConnection); SqlParameter workParam = null; // Restore database to it's original condition so sample will work correctly. Cleanup(); // Build the insert Command mySqlDataAdapter.InsertCommand = new SqlCommand("Insert into Region(RegionID,RegionDescription) VALUES (@RegionID,@RegionDescription)",myConnection); workParam = mySqlDataAdapter.InsertCommand.Parameters.Add("@RegionID",SqlDbType. Int); workParam.SourceColumn = "RegionID"; workParam.SourceVersion = DataRowVersion.Current; workParam = mySqlDataAdapter.InsertCommand.Parameters.Add("@Region Description",SqlDbType.NChar,50); workParam.SourceVersion = DataRowVersion.Current; workParam.SourceColumn = "RegionDescription"; // Build the update command mySqlDataAdapter.UpdateCommand = new SqlCommand("Update Region Set RegionDescription = @RegionDescription WHERE RegionID = @RegionID" ,myConnection); workParam = mySqlDataAdapter.UpdateCommand.Parameters.Add("@RegionID",SqlDbType. Int); workParam.SourceColumn = "RegionID"; workParam.SourceVersion = DataRowVersion.Original; workParam = mySqlDataAdapter.UpdateCommand.Parameters.Add("@Region Description",SqlDbType.NChar,50); workParam.SourceVersion = DataRowVersion.Current; workParam.SourceColumn = "RegionDescription"; DataSet myDataSet = new DataSet(); // Set the MissingSchemaAction property to AddWithKey because Fill will not cause // primary key & unique key information to be retrieved unless AddWithKey is specified mySqlDataAdapter.MissingSchemaAction = MissingSchemaAction.AddWithKey; mySqlDataAdapter.Fill(myDataSet,"Region"); DataRow myDataRow1 = myDataSet.Tables["Region"].Rows.Find(2); myDataRow1[1] = "已更改此地区说明"; DataRow myDataRow2 = myDataSet.Tables["Region"].NewRow(); myDataRow2[0] = 901; myDataRow2[1] = "新地区"; myDataSet.Tables["Region"].Rows.Add(myDataRow2); try { mySqlDataAdapter.Update(myDataSet,"Region"); Console.Write("已成功更新数据集!"); } catch(Exception e) { Console.Write(e.ToString()); } } public void Cleanup() { SqlConnection myConnection = new SqlConnection("server=(local)\\netSDK;Trusted_ Connection=yes;database=northwind"); try { // Restore database to it's original condition so sample will work correctly myConnection.Open(); SqlCommand CleanupCommand = new SqlCommand("DELETE FROM Region WHERE RegionID = '901'",myConnection); CleanupCommand.ExecuteNonQuery(); } catch(Exception e) { Console.Write(e.ToString()); } finally { myConnection.Close(); } } } }
1.9.2 Windows窗体中的数据绑定
1.介绍
数据绑定为开发人员提供一种在窗体上的控件和其应用程序数据(其数据模型)之间创建读/写链接的方法,该方法简单、方便、功能强大而且透明。
Windows窗体支持将数据绑定到ADO.NET DataSet控件、Array控件、ArrayList控件等。控件可以绑定到支持对集合中的元素进行索引访问的任何集合,具体地说,可以绑定到实现IList接口的任何集合。
(1)简单数据绑定
简单数据绑定意味着将数据模型内的单个值绑定到控件的单个属性。例如,将TextBox1.Text绑定到Customer.Name。使用每个控件上的Bindings集合管理简单绑定。
(2)复杂数据绑定
复杂数据绑定意味着将控件绑定到集合(而不是将控件绑定到集合内的单个项)。例如,DataGrid具有可设置为整个DataSet或Array的DataSource属性。DataGrid从DataSource中提取信息并显示它。ListBox和ComboBox也使用复杂数据绑定。
(3)单向和双向数据绑定
单向数据绑定描述以只读或表示为目的将控件的属性绑定到数据模型的过程。以此方式设置数据绑定后,属性反映数据的值,但对属性的直接更改不反射到数据模型中。
双向数据绑定描述以读/写的方式将控件的属性绑定到数据模型的过程。以此方式设置数据绑定后,属性反映数据的值,而且对属性的更改传播到数据模型中。
(4)BindingContext
每个Form都有一个BindingContext。BindingContext负责管理控件所绑定到的数据集合。它管理当前位置和依赖项。
① 当前位置
BindingContext为每个集合维护一个当前位置。简单数据绑定使用此当前位置确定将集合中的哪个对象绑定到控件属性。当前位置更改时,控件属性所绑定到的对象也更改。
② 依赖项
BindingContext维护集合之间的依赖关系。这允许创建主/详细信息窗体。
2.简单数据绑定
下面示例演示简单数据绑定,将一组TextBox控件上的Text属性绑定到作为客户列表存储的Customer对象的属性。在控件上使用DataBindings集合添加简单数据绑定。
textBoxID.DataBindings.Add("Text",custList,"CustomerID"); textBoxTitle.DataBindings.Add("Text",custList,"ContactTitle"); textBoxLastName.DataBindings.Add("Text",custList,"ContactName"); textBoxFirstName.DataBindings.Add("Text",custList,"CompanyName"); textBoxAddress.DataBindings.Add("Text",custList,"Address");
每个TextBox.Text都绑定到当前Customer对象,如同BindingContext进行维护一样。若要更改当前对象,可使用BindingContext递增或递减集合的Position属性。例如,通过按如下所示处理按钮的Click事件实现Move Next按钮。
private void buttonMoveNext_Click(object sender,System.EventArgs e) { this.BindingContext[custList].Position++; }
每当位置更改时,BindingContext引发一个事件。代码如下所示:
this.BindingContext[custList].PositionChanged += new System.EventHandler(customer_ PositionChanged); private void customer_PositionChanged(object sender,System.EventArgs e) { textBoxPosition.Text = "Record " + (this.BindingContext[custList]. Position + 1)+ " of " + custList.Length; }
3.绑定到ComboBox或ListBox
此示例演示将数据绑定到ComboBox。将数据绑定到ListBox遵循同一模型。
若要将数据绑定到所显示的项的列表,请设置ComboBox的DataSource和Display Member属性。DisplayMember属性用于确定在ComboBox中显示State对象的哪个属性。例如,下面的代码将ComboBox绑定到State对象的一个数组。
public struct State { private string shortName,longName; public State(string longName ,string shortName) { this.shortName = shortName ; this.longName = longName ; } public string ShortName { get { return shortName; } } public string LongName { get { return longName; } } } private State[] States = new State[] { new State("Alabama","AL"), …… new State("Washington" ,"WA), …… } comboBoxState.DataSource=States; comboBoxState.DisplayMember="LongName";
通常,在数据绑定窗体中,ComboBox用于查找值。在此示例中,窗体显示Customer对象。每个Customer对象都具有一个包含状态缩写名的Region属性。这里希望显示一列完整的状态名,以便用户可从中进行选择。当用户选定某特定状态时,希望用该状态的缩写名而不是全名更新Customer区域。为了实现此目的,可以执行下列操作。
- 像上例所示那样设置ComboBox。
- 将ValueMember属性设置为指向State对象上的状态缩写属性State.ShortName。
- 将SelectedValue简单绑定到Customer.Region。
ValueMember属性确定将哪个值移入SelectedValue中。在该示例中,每当用户按State.LongName选择状态时,SelectedValue变为State.ShortName。每当SelectedValue更改时,数据绑定将新值移入Customer对象中。代码如下所示:
comboBoxState.DataSource=States; comboBoxState.DisplayMember="LongName"; comboBoxState.ValueMember="ShortName"; comboBoxState.DataBindings.Add("SelectedValue",customersDataSet1,"Customers. Region");
4.绑定到DataGrid
DataGrid将数组、集合或ADO.NET DataTable中的所有信息显示为一系列行。每行都可就地进行编辑。当用户在行与行之间移动时,更改将自动移动回对象的基础集合中。
当使用DataGrid查看DataSet时,用户可通过DataTable对象间的关系在DataSet中的DataTable对象之间移动。
为了使用DataGrid,只需将DataSource属性设置为要显示的对象的列表即可。如果DataSource为DataSet,还需将DataMember属性设置为要显示的DataTable。代码如下所示:
dataGrid1.Size = new System.Drawing.Size(584,336); dataGrid1.DataSource = customersDataSet1; dataGrid1.DataMember = "Customers";
在DataGrid上有一组属性,使得可以更改它显示数据的方式。例如,可以设置AlternatingBackColor属性以用不同的BackColor属性显示交替行。代码如下所示:
dataGrid1.DataSource = customersDataSet1; dataGrid1.DataMember = "Customers"; dataGrid1.ForeColor = System.Drawing.Color.Navy; dataGrid1.BackColor = System.Drawing.Color.Gainsboro; dataGrid1.AlternatingBackColor = System.Drawing.Color.WhiteSmoke;
DataGrid示例演示如何在DataGrid中加载DataSet并显示其内容。代码如下所示:
namespace Microsoft.Samples.WinForms.Cs.Grid { using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using System.Data; using System.Data.SqlClient; public class Grid : System.Windows.Forms.Form { private System.ComponentModel.Container components; private System.Windows.Forms.StatusBar statusBar1; private Microsoft.Samples.WinForms.Cs.Grid.Data.CustomersDataSet customersDataSet1; private System.Windows.Forms.Button buttonLoad; private System.Windows.Forms.DataGrid dataGrid1; public Grid() { // Windows 窗体设计器所必需的 InitializeComponent(); } //处理 Load 按钮单击事件 //加载 Customers、Orders 和 OrderDetails 表并显示在网格中 private void buttonLoad_Click(object sender,System.EventArgs e) { Cursor currentCursor = Cursor.Current; try { Cursor.Current = Cursors.WaitCursor; //填充数据集 SqlConnection con = new SqlConnection("server=(local)\\netSDK;Trusted_ Connection=yes;database=northwind"); SqlDataAdapter cmdCustomers = new SqlDataAdapter("Select * from Customers",con); SqlDataAdapter cmdOrders = new SqlDataAdapter("Select * from Orders",con); SqlDataAdapter cmdOrderDetails = new SqlDataAdapter("Select* from [Order Details]",con); statusBar1.Text ="正在加载客户..."; cmdCustomers.Fill(customersDataSet1,"Customers"); statusBar1.Text ="正在加载订单..."; cmdOrders.Fill(customersDataSet1,"Orders"); statusBar1.Text ="正在加载订单详细信息..."; cmdOrderDetails.Fill(customersDataSet1,"Order_Details"); statusBar1.Text ="正在更新网格..."; } finally { statusBar1.Text ="完成"; Cursor.Current = currentCursor; } } protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } } base.Dispose(disposing); } private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.dataGrid1 = new System.Windows.Forms.DataGrid(); this.statusBar1 = new System.Windows.Forms.StatusBar(); this.customersDataSet1 = new Microsoft.Samples.WinForms.Cs.Grid. Data.CustomersDataSet(); this.buttonLoad = new System.Windows.Forms.Button(); dataGrid1.BeginInit(); if ( dataGrid1.TableStyles.Count == 0 ) { dataGrid1.TableStyles.Add(new DataGridTableStyle()); } this.dataGrid1.Text = "dataGrid1"; this.dataGrid1.TableStyles[0].PreferredRowHeight = 16; this.dataGrid1.TableStyles[0].AlternatingBackColor = System. Drawing.Color.WhiteSmoke; this.dataGrid1.Size = new System.Drawing.Size(584,336); this.dataGrid1.DataSource = customersDataSet1; this.dataGrid1.DataMember = "Customers"; this.dataGrid1.ForeColor = System.Drawing.Color.Navy; this.dataGrid1.TabIndex = 0; this.dataGrid1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom| AnchorStyles.Left | AnchorStyles.Right; this.dataGrid1.Location = new System.Drawing.Point(8,8); this.dataGrid1.BackColor = System.Drawing.Color.Gainsboro; this.AutoScaleBaseSize = new System.Drawing.Size(5,13); this.Text = "客户详细信息"; this.AcceptButton = buttonLoad; this.ClientSize = new System.Drawing.Size(600,413); this.statusBar1.BackColor = System.Drawing.SystemColors.Control; this.statusBar1.Location = new System.Drawing.Point(0,397); this.statusBar1.Size = new System.Drawing.Size(600,16); this.statusBar1.TabIndex = 2; this.statusBar1.Text = "单击"加载""; this.customersDataSet1.DataSetName = "CustomersDataSet"; this.buttonLoad.Anchor = AnchorStyles.Right | AnchorStyles.Bottom; this.buttonLoad.Click += new System.EventHandler(buttonLoad_ Click); this.buttonLoad.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.buttonLoad.Location = new System.Drawing.Point(480,352); this.buttonLoad.Size = new System.Drawing.Size(112,32); this.buttonLoad.TabIndex = 1; this.buttonLoad.Text = "加载(&L)"; this.Controls.AddRange(new System.Windows.Forms.Control[]{statusBar1,buttonLoad,dataGrid1}); this.dataGrid1.EndInit(); } [STAThread] public static void Main(string[] args) { Application.Run(new Grid()); } } }
5.使用XML Web服务检索DataSet
基于.NET框架的应用程序将服务作为XML Web服务公开。XML Web服务可以返回数据。Windows窗体应用程序可使用XML Web服务检索数据并绑定到那些返回的数据。此示例演示绑定到从Web服务返回的数据。
在演练此示例之前,应先熟悉XML Web服务。请读者参阅XML Web服务概述。
第一步是创建返回DataSet的XML Web服务。代码如下所示:
<%@ WebService Language="C#" class="CustomersList" %> using System.Web.Services; using System.Data; using System.Data.SqlClient; using Microsoft.Samples.WinForms.Cs.WebServiceBinding.Data; public class CustomersList : WebService { [ WebMethod ] public DataSet GetCustomers() { CustomersDataSet customersDataSet1 = new CustomersDataSet(); SqlConnection con = new SqlConnection("server=(local)\VSdotNET; Trusted_ Connection=yes;database=northwind"); SqlDataAdapter cmdCustomers = new SqlDataAdapter("Select * from Customers",con); SqlDataAdapter cmdOrders = new SqlDataAdapter("Select * from Orders",con); cmdCustomers.Fill(customersDataSet1,"Customers"); cmdOrders.Fill(customersDataSet1,"Orders"); return customersDataSet1 ; } }
可在CustomersList中查看此XML Web服务。
创建Web服务后,需要创建一个定义该Web服务架构的WSDL文件。可以通过使用WSDL参数即CustomersList WSDL连接到XML Web服务来获取WSDL。
拥有WSDL文件后,可使用WebServiceUtil.exe创建客户端代理源文件。代码如下所示:
wsdl.exe /l:CS /n:SimpleWebService /o:SimpleCustomersWebService.cs Simple Customers WebService.WSDL
有了代理源文件后,可将其编译到应用程序中,然后使用它检索来自Web服务的数据。代码如下所示:
CustomersList custList1 = new CustomersList(); DataSet ds1 = custList1.GetCustomers(); dataGrid1.DataSource=ds1;
下面XML Web服务示例演示如何从XML Web服务加载DataSet并在DataGrid中显示其内容。代码如下所示:
namespace Microsoft.Samples.Windows.Forms.Cs.WebServiceClient { using System; using System.ComponentModel; using System.Drawing; using System.Windows.Forms; using System.Data; using System.Data.OleDb; using Microsoft.Samples.Windows.Forms.Cs.WebServiceBinding.localhost; using System.Net; using System.IO; public class WebServiceClient : System.Windows.Forms.Form { private System.ComponentModel.Container components; private System.Windows.Forms.StatusBar statusBar1; private Microsoft.Samples.Windows.Forms.Cs.WebServiceBinding.Data. CustomersAndOrdersDataSet customersAndOrdersDataSet1; private System.Windows.Forms.Button buttonLoad; private System.Windows.Forms.DataGrid dataGrid1; public WebServiceClient() { // Windows 窗体设计器必需的 InitializeComponent(); } protected void buttonLoad_Click(object sender,System.EventArgs e) { Cursor currentCursor = Cursor.Current; try { Cursor.Current = Cursors.WaitCursor; statusBar1.Text ="正在加载客户..."; //执行 WebService 以返回数据集 CustomersList custList1 = new CustomersList(); DataSet ds1 = custList1.GetCustomers(); //将新数据集合并到 customersDataSet customersAndOrdersDataSet1.Merge(ds1); statusBar1.Text ="正在更新网格..."; } finally { Cursor.Current = currentCursor; statusBar1.Text ="完成"; } } protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } } base.Dispose(disposing); } private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.dataGrid1 = new System.Windows.Forms.DataGrid(); this.statusBar1 = new System.Windows.Forms.StatusBar(); this.customersAndOrdersDataSet1 = new Microsoft.Samples.Windows. Forms.Cs. WebServiceBinding.Data.CustomersAndOrdersDataSet(); this.buttonLoad = new System.Windows.Forms.Button(); dataGrid1.BeginInit(); if ( dataGrid1.TableStyles.Count == 0 ) { dataGrid1.TableStyles.Add(new DataGridTableStyle()); } dataGrid1.Text = "dataGrid1"; dataGrid1.TableStyles[0].PreferredRowHeight = 16; dataGrid1.TableStyles[0].AlternatingBackColor = System.Drawing. Color.WhiteSmoke; dataGrid1.Size = new System.Drawing.Size(584,336); dataGrid1.DataSource = customersAndOrdersDataSet1; dataGrid1.DataMember = "Customers"; dataGrid1.ForeColor = System.Drawing.Color.Navy; dataGrid1.TabIndex = 0; dataGrid1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles. Left | AnchorStyles.Right; dataGrid1.Location = new System.Drawing.Point(8,8); dataGrid1.BackColor = System.Drawing.Color.Gainsboro; this.AutoScaleBaseSize = new System.Drawing.Size(5,13); this.Text = "客户详细信息"; this.AcceptButton = buttonLoad; this.ClientSize = new System.Drawing.Size(600,413); statusBar1.BackColor = System.Drawing.SystemColors.Control; statusBar1.Size = new System.Drawing.Size(600,16); statusBar1.TabIndex = 2; statusBar1.Text = "单击"加载""; statusBar1.Location = new System.Drawing.Point(0,397); customersAndOrdersDataSet1.DataSetName = "CustomersDataSet"; buttonLoad.FlatStyle = System.Windows.Forms.FlatStyle.Flat; buttonLoad.Size = new System.Drawing.Size(112,32); buttonLoad.TabIndex = 1; buttonLoad.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; buttonLoad.Text = "加载(&L)"; buttonLoad.Location = new System.Drawing.Point(480,352); buttonLoad.Click += new System.EventHandler(buttonLoad_Click); this.Controls.Add(statusBar1); this.Controls.Add(buttonLoad); this.Controls.Add(dataGrid1); dataGrid1.EndInit(); } [STAThread] public static void Main(string[] args) { Application.Run(new WebServiceClient()); } } }
6.主/详细信息窗体
在数据库应用程序中,查看带有一组相关记录的记录通常很有用。例如,可能希望查看带有自己的当前订单的客户。实现此功能的一个常用方法是创建主/详细信息窗体。
此示例显示客户的Datagrid及每个客户的订单的DataGrid。第一个DataGrid显示客户列表。第二个DataGrid显示订单列表。当所选客户更改时,第二个DataGrid更新以显示该客户的订单。
为了链接两个DataGrid对象,需要将每个DataGrid的DataSource设置为同一个DataSet。还需要设置DataMember属性以向Windows窗体BindingContext指示它们是相关的。通过将第二个DataGrid的DataMember设置为Customers和Orders表之间关系的名称Customers.CustomersOrders来实现此目的。代码如下所示:
dataGrid1.DataSource = customersAndOrdersDataSet1; dataGrid1.DataMember = "Customers"; dataGrid2.DataSource = customersAndOrdersDataSet1; dataGrid2.DataMember = "Customers.CustomersOrders";