文章編號: 320301 - 上次校閱: 2004年2月1日 - 版次: 3.4

如何: 藉由使用 Visual C#.NET 中的 Web 服務的識別資料行從 Windows Form 應用程式更新父子式資料

系統提示本文適用於您使用的作業系統之外的作業系統。與您不相關的文章內容已停用。

在此頁中

全部展開 | 全部摺疊

結論

本文將逐步告訴您,如何擷取 DataSet 物件從 Web 服務、 如何 DiffGram 更新傳送至 Web] 服務以及然後如何合併更新的 DiffGram 回用戶端 的資料集。此程序包括下列步驟執行:
  1. 用戶端應用程式會從 Web 服務擷取具有父子式關聯性中的兩個 DataTable 物件的 資料集 物件。父 資料表 有識別/自動編號資料行作為主索引鍵。
  2. 在用戶端應用程式中使用者可以新增、 刪除,並修改父資料錄和子資料錄。新的父資料錄會收到一個暫時的主索引鍵值在本機產生的。
  3. 用戶端應用程式將變更傳回給 Web 服務為一個 DiffGram。
  4. Web 服務將會更新資料庫、 擷取實際主索引鍵值的新父資料錄並傳播變更子記錄的索引鍵值。
  5. 用戶端應用程式從 Web 服務接收更新的值,然後將之更新的值合併回本機 資料集
如需有關如何使用 Web 服務更新單一資料表的詳細資訊,按一下下面的文件編號,檢視 「 Microsoft 知識庫 」 中的發行項:
310143? (http://support.microsoft.com/kb/310143/EN-US/ ) 如何: 使用 ADO.NET 和 Visual C#.NET 更新透過 Web 服務的伺服器資料

需求

下列清單列出建議的硬體、 軟體、 網路基礎結構及所需的 Service Pack:
  • Microsoft Windows 2000 專業版、 Windows 2000 Server Windows 2000 進階伺服器或 Windows NT 4.0 伺服器
  • Microsoft Visual Studio.NET
本文假設您已熟悉下列主題:
  • Visual Studio.NET
  • ADO.NET 基本原則及語法
  • ASP.NET 基礎概念和語法
本文的程式碼範例會將 http://localhost 作為 Web 伺服器。此外,程式碼範例會使用 北風貿易 資料庫與資料庫。隨附於 Microsoft SQL Server 北風 資料庫。

建立 Web 服務

  1. 若要建立新的 Visual C# ASP.NET Web 服務專案,請依照下列步驟執行:
    1. 啟動 Visual Studio.NET。
    2. 在 [檔案] 功能表上指向 [新增],然後按一下 [專案]。
    3. 在 [專案類型 下, 按一下 [Visual C# 專案,然後按一下 [範本] 下方的 [ASP.NET Web 服務
    4. 在 [位置] 方塊預設位置會顯示為 http://localhost/WebService1。輸入您的伺服器 (如範例為 http://localhost 執行 Web 服務在您本機的 Web 伺服器上) 的 URL。與 CSharpUpdateData 取代 WebService1。在 [位置] 方塊中 URL 應該會出現,如下所示:
      http://localhost/CSharpUpdateData
    5. 若要關閉 [新增專案] 對話方塊中,按一下 [確定]
  2. 上 Service1.asmx.cs[Design] 頁面上,切換至 [程式碼] 檢視。 請注意 Web 服務的 [程式碼] 視窗會出現。
  3. 程式碼] 視窗頂端加入下列的 使用 陳述式:
    using System.Data.SqlClient;
  4. 將下列程式碼加入至類別 Service1 實作:
            [WebMethod]
            public DataSet GetData()
            {
                SqlConnection conn = new SqlConnection ("server=vcdb02;uid=sa;pwd=ricka;database=northwind");
                //Pull back the recent orders for the parent rows.
                SqlDataAdapter daOrder = new SqlDataAdapter("SELECT * FROM Orders WHERE OrderDate >= '05/01/1998'",conn);
                //Get only the appropriate child rows for the parent rows.
                SqlDataAdapter daDetails = new SqlDataAdapter("SELECT * FROM [Order Details] WHERE OrderID in ( SELECT OrderID FROM Orders WHERE OrderDate >= '05/01/1998')",conn);
    
                DataSet ds = new DataSet();
                try
                {
    				
                    //Fill DataSet, and then set DataRelation to move through the DataGrid.
                    conn.Open();
    
                    daOrder.FillSchema(ds,SchemaType.Mapped,"Orders");
                    daOrder.Fill(ds,"Orders");
    
                    daDetails.FillSchema(ds,SchemaType.Mapped,"Details");
                    daDetails.Fill(ds,"Details");
    
                    ds.Relations.Add("OrdDetail", ds.Tables["Orders"].Columns["OrderID"], ds.Tables["Details"].Columns["OrderID"]);
    			
                    DataColumn dc = ds.Tables["Orders"].Columns["OrderID"];
                    dc.AutoIncrement = true;
                    dc.AutoIncrementSeed = -1;
                    dc.AutoIncrementStep = -1;
                }
                catch(SqlException ex)
                {
                    Console.Write (ex.Message.ToString ());
                    Console.Write(ex.InnerException.ToString ());
    
                }
    
                return ds;
            }
    		
            [WebMethod]
            public DataSet UpdateData(DataSet ds)
            {
                SqlConnection conn = new SqlConnection ("server=vcdb02;uid=sa;pwd=ricka;database=northwind");
                //Pull back the recent orders for the parent rows.
                SqlDataAdapter daOrders = new SqlDataAdapter("SELECT * FROM Orders WHERE OrderDate >= '05/01/1998'",conn);
                //Get only the appropriate child rows for the parent rows.
                SqlDataAdapter daDetails = new SqlDataAdapter("SELECT * FROM [Order Details] WHERE OrderID in ( SELECT OrderID FROM Orders WHERE OrderDate >= '05/01/1998')",conn);
    
    			
                try
                {
                    conn.Open();
                    // Get commands for the Orders table.
                    // Reselect record after insert to get new Identity value.
                    // You must get the schema, which you did in GetData(), before you get commands; 
                    // otherwise, the Command builder tries to insert new rows, based 
                    // on the Identity column.
                    SqlCommandBuilder cb = new SqlCommandBuilder(daOrders);
                    daOrders.DeleteCommand = cb.GetDeleteCommand();
                    daOrders.UpdateCommand = cb.GetUpdateCommand();
                    daOrders.InsertCommand = cb.GetInsertCommand();
                    daOrders.InsertCommand.CommandText = String.Concat(daOrders.InsertCommand.CommandText, "; Select * From Orders Where OrderID = @@IDENTITY");
    
                    //UpdateRowSource tells the DataAdapter that there will be a re-selected record.
          
                    daOrders.InsertCommand.UpdatedRowSource = UpdateRowSource.FirstReturnedRecord;
                    //cb = null;
            
    
                    // Get commands for the Order Details table.
                    // Must set the QuotePrefix and QuoteSuffix; 
                    // otherwise, the CommandBuilder does not put brackets ([])
                    // around the table name.
    
                    SqlCommandBuilder cb1 = new SqlCommandBuilder(daDetails);
                    cb1.QuotePrefix = "[";
                    cb1.QuoteSuffix = "]";
                    daDetails.DeleteCommand = cb1.GetDeleteCommand();
                    daDetails.InsertCommand = cb1.GetInsertCommand();
                    daDetails.UpdateCommand = cb1.GetUpdateCommand();
    
    
                    // Create a new DataAdapter based on the original one to prevent the
                    // CommandBuilder from modifying the SQL statements, 
                    // specifically the custom InsertCommand.
                    // You do not need this if you roll your own commands and parameters 
                    // or if you use the Visual Tools to do it.
               
                    SqlDataAdapter daOrd2 = new SqlDataAdapter();
                    daOrd2.DeleteCommand = daOrders.DeleteCommand;
                    daOrd2.InsertCommand = daOrders.InsertCommand;
                    daOrd2.UpdateCommand = daOrders.UpdateCommand;
    
                    // Use a delegate to prevent AcceptChanges from occurring on Deletes and Inserts.
                    // This is for a limitation of the DataAdapter; see Q313540. 
    
                    daOrd2.RowUpdated += new SqlRowUpdatedEventHandler(OnOrd1RowUpdated);
                    daDetails.RowUpdated += new SqlRowUpdatedEventHandler(OnDetailsRowUpdated);
    
    
                    daDetails.Update(GetDeletedRows(ds.Tables["Details"]));
                    daOrd2.Update(GetDeletedRows(ds.Tables["Orders"]));
                    DataRow [] dsArray = ds.Tables["Orders"].Select("", "", DataViewRowState.ModifiedCurrent);
                    daOrd2.Update(ds.Tables["Orders"].Select("", "", DataViewRowState.ModifiedCurrent));
                    daDetails.Update(ds.Tables["Details"].Select("", "", DataViewRowState.ModifiedCurrent));
    
                    daOrd2.Update(ds.Tables["Orders"].Select("", "", DataViewRowState.Added));
    
                    ds.EnforceConstraints = false;
                    daDetails.Update(ds.Tables["Details"].Select("","", DataViewRowState.Added));
                    ds.EnforceConstraints = true;
    
                    conn.Close();
    
    				
    
                }
                catch(SqlException ex)
                {
                    Console.Write (ex.Message.ToString ());
                    Console.Write(ex.InnerException.ToString ());
                }
                return ds;
            }
    
    
            protected static void OnOrd1RowUpdated(object sender, SqlRowUpdatedEventArgs args)
            {
                if(args.StatementType == StatementType.Insert || args.StatementType == StatementType.Delete )
                    args.Status = UpdateStatus.SkipCurrentRow;
    
            }
            protected static void OnDetailsRowUpdated(object sender, SqlRowUpdatedEventArgs args)
            {
                if(args.StatementType == StatementType.Insert )
                {
                    // Do not allow the AcceptChanges to occur on this row.
                    args.Status = UpdateStatus.SkipCurrentRow;
    
                    // Get the current, actual primary key value so that you can plug it back
                    // in after you get the correct original value that was generated for the child row.
                    int currentkey = (int)args.Row["OrderID"];
                    // This is where you get a correct original value key that is stored to the child row. 
                    // You pull the original, pseudo key value from the parent, plug it in as the child row's primary key
                    // field, and then accept changes on it. Specifically, this is why you turned off EnforceConstraints.
                    args.Row["OrderID"] = args.Row.GetParentRow("OrdDetail")["OrderID",DataRowVersion.Original];
                    args.Row.AcceptChanges();
                    // Store the actual primary key value in the foreign key column of the child row.
                    args.Row["OrderID"] = currentkey;
                }
    
                if(args.StatementType == StatementType.Delete )
                    args.Status = UpdateStatus.SkipCurrentRow;
    
            }
            private DataRow [] GetDeletedRows(DataTable dt)
            {
                DataRow [] dr ;
                if(dt == null)
                    return null;
                dr = dt.Select("","",DataViewRowState.Deleted );
                if(dr.Length ==0 || dr[0] != null)
                    return dr;
                // Workaround:
                // With a remoted DataSet, Select returns the array elements
                // that are filled with Nothing/null instead of DataRow objects.
    					
                for(int i=0; i < (int)dt.Rows.Count; i++)
                {
                    if(dt.Rows[i].RowState ==DataRowState.Deleted )
                        dr[i]=dt.Rows[i];
                }
                return dr;
    						
    
            } 
    					
  5. 修改 SqlConnection 字串,以連線到執行 SQL Server 的伺服器。

測試 Web 服務

  1. 請按 F5 編譯並執行 Web 服務。請注意,網頁會傳回具有 URL http://localhost/CSharpUpdateData/Service1.asmx。此網頁中您可以與 Microsoft Internet Explorer 從 Web 服務互動。
  2. 在 [Service1.asmx Web] 頁面中,按一下 GetData。請注意 Web 網頁會傳回,顯示關於 GetData Web 方法的詳細資料。
  3. 關閉 Web 網頁。

建立用戶端應用程式

  1. 若要建立新的 Visual C# Windows 應用程式專案,請依照下列步驟執行:
    1. 在 Visual Studio.NET 中的 [檔案] 功能表上指向 [新增],然後按一下 [專案]。
    2. 在 [新增專案] 對話方塊按一下 [專案類型,] 下的 [Visual C# 專案,然後再按一下 [範本] 下方的 [Windows 應用程式。預設情況下,Form1 加入至專案。
  2. Button 控制項] 和 [DataGrid 控制項從工具箱拖曳至 Form1。預設情況下,控制項的命名 button1dataGrid1 分別。
  3. button1] 的 [名稱] 屬性變更為 btnSave,並以 儲存 變更 button1] 的 [文字] 屬性。
  4. 在 [專案] 功能表上按一下 [加入 Web 參考]。您的 Web 服務 (在此情況,類型 http://localhost/CSharpUpdateData/Service1.asmx),按下 ENTER,接著再按一下 [加入參考],請鍵入 URL。請注意,這個 Web 參考的項目會出現在方案總管。
  5. 將下列程式碼加入 Form1 類別來宣告的 資料集 的表單層級成員:
           private System.Data.DataSet ds;
    					
  6. 連按兩下 [切換至 [程式碼] 檢視,並且請注意 Visual Studio.NET 就會建立 Form1_Load 方法] 表單。將下列程式碼加入至 Form1_Load 方法:
        localhost.Service1 sv = new localhost.Service1();
        ds = sv.GetData ();
           
        dataGrid1.DataSource = ds;
        dataGrid1.DataMember = "Orders";
    					
  7. 切換至表單檢視。
  8. 開啟 儲存檔案] 按鈕的 [程式碼] 視窗,然後將下列的程式碼加入 btnSave_Click 事件處理常式:
        localhost.Service1 sv = new localhost.Service1();
    
        DataSet MyChangedRows;
    
        dataGrid1.DataMember = "";
        dataGrid1.DataSource = null;
        //Pull out only what you must send over the wire.
        MyChangedRows = ds.GetChanges();
        MyChangedRows = sv.UpdateData(MyChangedRows);
    
        //You must accept changes on the DataSet because of a known problem. See Q313540.
    
        ds.AcceptChanges();
    
        ds.EnforceConstraints = false;
    
        //Merge in the parent rows first and then the child rows.
        ds.Merge(MyChangedRows.Tables["Orders"], false, MissingSchemaAction.Ignore);
        ds.Merge(MyChangedRows.Tables["details"], false, MissingSchemaAction.Ignore);
    
        //Accept changes that you have made to the DataSet.
        ds.AcceptChanges();
        //Turn on the integrity constraints that you turned off earlier.
        ds.EnforceConstraints = true;
    
        dataGrid1.DataSource = ds;
        dataGrid1.DataMember = "Orders";
    					

測試用戶端應用程式

  1. 請按 F5 編譯並執行用戶端應用程式。
  2. dataGrid1,修改某些資料],然後按一下 [儲存]。

    注意: 請勿變更索引鍵欄位。如果您將金鑰的欄位變更您會收到錯誤訊息,指出您在伺服器上中斷的參考完整性。
  3. 新增子列,請到現有的父資料列。請注意子資料列自動接收正確的外部索引鍵值。
  4. 新增新的父資料列和新的子系列以下的值,然後按一下 [儲存檔案。請注意下列金鑰值:
    • Orders.CustomerID=RATTC
    • Orders.EmployeeID=1
    • Orders.OrderDate=2/2/2002
    • Details.ProductID=1
    • Details.UnitPrice=18
    • Details.quantity=1
    • Details.discount=0

?考

如需詳細資訊按一下面的文件編號,檢視 「 Microsoft 知識庫 」 中 「 文件:
310143? (http://support.microsoft.com/kb/310143/EN-US/ ) 如何: 使用 ADO.NET 和 Visual C#.NET 更新透過 Web 服務的伺服器資料
313483? (http://support.microsoft.com/kb/313483/EN-US/ ) ADO.NET 資料配接器物件的資訊: 藍圖

這篇文章中的資訊適用於:
  • Microsoft Visual C# .NET 2003 標準版
  • Microsoft Visual C# .NET 2002 Standard Edition
  • Microsoft ADO.NET 1.1
  • Microsoft ADO.NET 1.0
  • Microsoft ASP.NET 1.1
  • Microsoft ASP.NET 1.0
關鍵字:?
kbmt kbdatabinding kbhowtomaster kbsqlclient kbsystemdata KB320301 KbMtzh
機器翻譯機器翻譯
重要:本文是以 Microsoft 機器翻譯軟體翻譯而成,而非使用人工翻譯而成。Microsoft 同時提供使用者人工翻譯及機器翻譯兩個版本的文章,讓使用者可以依其使用語言使用知識庫中的所有文章。但是,機器翻譯的文章可能不盡完美。這些文章中也可能出現拼字、語意或文法上的錯誤,就像外國人在使用本國語言時可能發生的錯誤。Microsoft 不為內容的翻譯錯誤或客戶對該內容的使用所產生的任何錯誤或損害負責。Microsoft也同時將不斷地就機器翻譯軟體進行更新。
按一下這裡查看此文章的英文版本:320301? (http://support.microsoft.com/kb/320301/en-us/ )
Microsoft及(或)其供應商不就任何在本伺服器上發表的文字資料及其相關圖表資訊的恰當性作任何承諾。所有文字資料及其相關圖表均以「現狀」供應,不負任何擔保責任。Microsoft及(或)其供應商謹此聲明,不負任何對與此資訊有關之擔保責任,包括關於適售性、適用於某一特定用途、權利或不侵權的明示或默示擔保責任。Microsoft及(或)其供應商無論如何不對因或與使用本伺服器上資訊或與資訊的實行有關而引起的契約、過失或其他侵權行為之訴訟中的特別的、間接的、衍生性的損害或任何因使用而喪失所導致的之損害、資料或利潤負任何責任。