更新 TableAdapter 以使用 JOIN (C#)
使用資料庫時,通常會要求分散於多個數據表的數據。 若要從兩個不同的數據表擷取數據,我們可以使用相互關聯的子查詢或 JOIN 作業。 在本教學課程中,我們會先比較相互關聯的子查詢和 JOIN 語法,再瞭解如何在其主要查詢中建立包含 JOIN 的 TableAdapter。
簡介
使用關係資料庫時,我們感興趣的數據通常會分散到多個數據表。 例如,顯示產品資訊時,我們可能會想要列出每個產品的對應類別和供應商名稱。 數據表Products
有 CategoryID
和 SupplierID
值,但實際的類別和供應商名稱分別位於 和 Suppliers
數據表中Categories
。
若要從另一個相關的數據表擷取資訊,我們可以使用相互關聯的子查詢或JOIN
。 相互關聯的子查詢是巢狀 SELECT
查詢,可參考外部查詢中的數據行。 例如,在 建立數據存取層 教學課程中,我們在主要查詢中使用 ProductsTableAdapter
兩個相互關聯的子查詢來傳回每個產品的類別和供應商名稱。 JOIN
是一種 SQL 建構,可合併來自兩個不同數據表的相關數據列。 我們在查詢數據中使用 SqlDataSource 控制件教學課程中的 ,JOIN
將類別資訊與每個產品一起顯示。
由於 TableAdapter 精靈中對於自動產生對應INSERT
、 UPDATE
和 DELETE
語句的限制,因此我們已從JOIN
使用 搭配 TableAdapters 使用 s 的原因。 更具體來說,如果 TableAdapter 的主要查詢包含任何 JOIN
,TableAdapter 就無法自動為其 InsertCommand
、 UpdateCommand
和 DeleteCommand
屬性建立臨機操作 SQL 語句或預存程式。
在本教學課程中,我們將先簡短比較和對比相互關聯的子查詢和 JOIN
,再探索如何建立包含 在其主要查詢中的 TableAdapter JOIN
。
比較和對比相互關聯的子查詢和JOIN
s
回想一下,在 DataSet 的第一個教學課程中建立的 ProductsTableAdapter
Northwind
會使用相互關聯的子查詢來傳回每個產品的對應類別和供應商名稱。 主要 ProductsTableAdapter
查詢如下所示。
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued,
(SELECT CategoryName FROM Categories WHERE Categories.CategoryID =
Products.CategoryID) as CategoryName,
(SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID =
Products.SupplierID) as SupplierName
FROM Products
兩個相互關聯的子查詢 - (SELECT CategoryName FROM Categories WHERE Categories.CategoryID = Products.CategoryID)
和 - 是SELECT
每個產品傳回單一值的查詢,作為外部SELECT
語句數據行清單中的(SELECT CompanyName FROM Suppliers WHERE Suppliers.SupplierID = Products.SupplierID)
其他數據行。
或者, JOIN
可以用來傳回每個產品的供應商和類別名稱。 下列查詢會傳回與上述相同的輸出,但會使用 JOIN
取代子查詢:
SELECT ProductID, ProductName, Products.SupplierID, Products.CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued,
Categories.CategoryName,
Suppliers.CompanyName as SupplierName
FROM Products
LEFT JOIN Categories ON
Categories.CategoryID = Products.CategoryID
LEFT JOIN Suppliers ON
Suppliers.SupplierID = Products.SupplierID
會 JOIN
根據一些準則,將來自某個數據表的記錄與另一個數據表中的記錄合併。 例如,在上述查詢中,會LEFT JOIN Categories ON Categories.CategoryID = Products.CategoryID
指示 SQL Server 將每個產品記錄與值符合產品CategoryID
值的類別記錄CategoryID
合併。 合併的結果可讓我們處理每個產品 (的對應類別欄位,例如 CategoryName
) 。
注意
JOIN
從關係資料庫查詢數據時,通常會使用 s。 如果您不熟悉JOIN
語法,或需要對其使用方式進行筆刷,我建議 W3 Schools 的 SQL Join 教學課程。 另外值得閱讀的是JOIN
《SQL 在線叢書》的基本概念和子查詢基本概念小節。
由於 JOIN
s 和相互關聯的子查詢都可以用來從其他數據表擷取相關數據,因此許多開發人員都會留下頭頭,並想知道要使用的方法。 我所討論的所有 SQL 同義字都說了大致相同的事,這實際上並不重要,因為 SQL Server 會產生大致相同的執行計劃。 其建議是使用您和小組最熟悉的技術。 請注意,在進行這項建議之後,這些專家會立即表達其喜好 JOIN
,而不是相互關聯的子查詢。
使用具型別數據集建置數據存取層時,工具在使用子查詢時會更好。 特別是,如果主查詢包含任何 JOIN
,TableAdapter 精靈將不會自動產生對應的 INSERT
、 UPDATE
和 DELETE
語句,但會在使用相互關聯的子查詢時自動產生這些語句。
若要探索此缺點,請在資料夾中建立暫存的具類型數據集 ~/App_Code/DAL
。 在 TableAdapter 設定精靈期間,選擇使用臨機操作 SQL 語句並輸入下列 SELECT
查詢 (請參閱圖 1) :
SELECT ProductID, ProductName, Products.SupplierID, Products.CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder,
ReorderLevel, Discontinued,
Categories.CategoryName,
Suppliers.CompanyName as SupplierName
FROM Products
LEFT JOIN Categories ON
Categories.CategoryID = Products.CategoryID
LEFT JOIN Suppliers ON
Suppliers.SupplierID = Products.SupplierID
圖 1:輸入包含 JOIN
(按單擊即可檢視完整大小的影像)
根據預設,TableAdapter 會自動根據主要查詢建立 INSERT
、 UPDATE
和 DELETE
語句。 如果您按下 [進階] 按鈕,您可以看到此功能已啟用。 儘管有此設定,但 TableAdapter 將無法建立、 和 語句,INSERT
因為主要查詢包含 JOIN
。DELETE
UPDATE
圖 2:輸入包含 JOIN
的主要查詢
按一下 [完成] 以完成精靈。 此時,您的 DataSet Designer 會包含具有 DataTable 的單一 TableAdapter,其中包含查詢數據行清單中傳SELECT
回之每個字段的數據行。 CategoryName
這包括 和 SupplierName
,如圖 3 所示。
圖 3:D ataTable 包含數據行清單中傳回之每個字段的數據行
雖然 DataTable 具有適當的數據行,但 TableAdapter 缺少其 InsertCommand
、 UpdateCommand
和 DeleteCommand
屬性的值。 若要確認這一點,請按兩下 Designer中的 TableAdapter,然後移至 屬性視窗。 您會看到 InsertCommand
、 UpdateCommand
屬性 DeleteCommand
設定為 (None) 。
圖 4: InsertCommand
、 UpdateCommand
和 DeleteCommand
屬性設定為 [無 (],) (按兩下即可檢視完整大小的影像)
為了解決這個問題,我們可以透過 屬性視窗 手動提供、 UpdateCommand
和 屬性的 InsertCommand
SQL 語句和DeleteCommand
參數。 或者,我們可以從將 TableAdapter 的主要查詢設定為 不包含 任何 JOIN
項目開始。 這可讓我們 INSERT
自動產生、 UPDATE
和 DELETE
語句。 完成精靈之後,我們可以從 屬性視窗 手動更新 TableAdapter ,SelectCommand
使其包含 JOIN
語法。
雖然這個方法可運作,但使用臨機操作 SQL 查詢時會非常不符,因為每當透過精靈重新設定 TableAdapter 的主要查詢時,都會重新建立自動產生的 INSERT
、 UPDATE
和 DELETE
語句。 這表示我們稍後所做的所有自定義都會遺失,如果我們以滑鼠右鍵按下 TableAdapter,從操作功能表中選擇 [設定],然後再次完成精靈。
TableAdapter 自動產生的 INSERT
、 UPDATE
和 DELETE
語句的相差性,幸運的是,僅限於臨機操作 SQL 語句。 如果您的 TableAdapter 使用預存程式,您可以自定義 SelectCommand
、 InsertCommand
、 UpdateCommand
或 DeleteCommand
預存程式,然後重新執行 TableAdapter 組態精靈,而不需擔心預存程式將會修改。
在接下來的幾個步驟中,我們將建立 TableAdapter,一開始會使用省略任何 JOIN
專案的主要查詢,以便自動產生對應的插入、更新和刪除預存程式。 接著,我們會更新 SelectCommand
,以便使用 JOIN
從相關資料表傳回其他資料行的 。 最後,我們將建立對應的 Business Logic Layer 類別,並示範如何在 ASP.NET 網頁中使用 TableAdapter。
步驟 1:使用簡化的主要查詢建立 TableAdapter
在本教學課程中,我們將為 Employees
DataSet 中的 NorthwindWithSprocs
數據表新增 TableAdapter 和強型別 DataTable。 數據表Employees
包含指定ReportsTo
EmployeeID
員工經理的欄位。 例如,員工 Anne Dodsworth 的 ReportTo
值為 5,其為一個為一個,而其為 EmployeeID
一位在一起。 因此,Anne 會向她的經理,向其經理報告。 除了報告每位員工 ReportsTo
的值之外,我們也可能想要擷取其經理的名稱。 這可以使用 來完成 JOIN
。 JOIN
但是,一開始建立 TableAdapter 時使用 ,會防止精靈自動產生對應的插入、更新和刪除功能。 因此,我們會從建立主查詢不包含任何 JOIN
的 TableAdapter 開始。 然後,在步驟 2 中,我們將更新主要查詢預存程式,以透過 JOIN
擷取管理員的名稱。
從開啟資料夾中的 NorthwindWithSprocs
~/App_Code/DAL
DataSet 開始。 以滑鼠右鍵按下 Designer,從操作功能表中選取 [新增] 選項,然後挑選 TableAdapter 功能表項。 這會啟動 TableAdapter 組態精靈。 如圖 5 所述,讓精靈建立新的預存程式,然後按 [下一步]。 如需從 TableAdapter 精靈建立新預存程式的重新整理,請參閱 建立新預存程式以取得具類型的 DataSet s TableAdapters 教學課程。
圖 5:選取 [建立新的預存程式] 選項 (按兩下即可檢視完整大小的映像)
針對 TableAdapter 的主要查詢使用下列 SELECT
語句:
SELECT EmployeeID, LastName, FirstName, Title, HireDate, ReportsTo, Country
FROM Employees
由於此查詢不包含任何 JOIN
,因此 TableAdapter 精靈會自動建立具有對應 INSERT
、 UPDATE
和 DELETE
語句的預存程式,以及執行主查詢的預存程式。
下列步驟可讓我們命名 TableAdapter 預存程式。 使用名稱 Employees_Select
、 Employees_Insert
、 Employees_Update
和 Employees_Delete
,如圖 6 所示。
圖 6:將 TableAdapter s 預存程式命名 (按兩下即可檢視完整大小的影像)
最後一個步驟會提示我們命名 TableAdapter s 方法。 使用 Fill
和 GetEmployees
作為方法名稱。 此外,請務必保留 [建立方法],將更新直接傳送至資料庫, (GenerateDBDirectMethods) 複選框。
圖 7:命名 TableAdapter 的方法 Fill
和 GetEmployees
(按兩下即可檢視完整大小的影像)
完成精靈之後,請花點時間檢查資料庫中的預存程式。 您應該會看到四個新專案: Employees_Select
、 Employees_Insert
、 Employees_Update
、 和 Employees_Delete
。 接下來,檢查 剛EmployeesTableAdapter
建立的 EmployeesDataTable
。 DataTable 包含主查詢所傳回之每個欄位的數據行。 按兩下 TableAdapter,然後移至 屬性視窗。 您會看到 InsertCommand
、 UpdateCommand
和 DeleteCommand
屬性已正確設定為呼叫對應的預存程式。
圖 8:TableAdapter 包含插入、更新和刪除功能 (按兩下即可檢視完整大小的映射)
自動建立、更新和刪除預存程式,並 InsertCommand
正確設定、 UpdateCommand
和 DeleteCommand
屬性之後,我們就可以自定義 SelectCommand
預存程式,以傳回每個員工經理的其他資訊。 具體而言,我們需要更新 Employees_Select
預存程式,以使用 JOIN
並傳回管理員的 FirstName
和 LastName
值。 在更新預存程式之後,我們必須更新 DataTable,使其包含這些額外的數據行。 我們將在步驟 2 和 3 中處理這兩項工作。
步驟 2:自定義要包含 的預存程式JOIN
從移至 [伺服器總管] 開始,向下切入 Northwind 資料庫的 [預存程式] 資料夾,然後開啟 Employees_Select
預存程式。 如果您沒有看到此預存程式,請以滑鼠右鍵按兩下 [預存程式] 資料夾,然後選擇 [重新整理]。 更新預存程式,使其使用 LEFT JOIN
傳回管理員的名字和姓氏:
SELECT Employees.EmployeeID, Employees.LastName,
Employees.FirstName, Employees.Title,
Employees.HireDate, Employees.ReportsTo,
Employees.Country,
Manager.FirstName as ManagerFirstName,
Manager.LastName as ManagerLastName
FROM Employees
LEFT JOIN Employees AS Manager ON
Employees.ReportsTo = Manager.EmployeeID
更新 SELECT
語句之後,請移至 [檔案] 功能表並選擇 [儲存 Employees_Select
] 來儲存變更。 或者,您也可以按下工具列中的 [儲存] 圖示,或按 Ctrl+S。 儲存變更之後,以滑鼠右鍵按兩下 Employees_Select
[伺服器總管] 中的預存程式,然後選擇 [執行]。 這會執行預存程式,並在 [輸出] 視窗中顯示其結果, (請參閱圖 9) 。
圖 9:預存程式結果會顯示在輸出視窗中, (按兩下即可檢視大小完整的影像)
步驟 3:更新 DataTable 的數據行
此時,預 Employees_Select
存程式會傳 ManagerFirstName
回 和 ManagerLastName
值,但 EmployeesDataTable
遺漏這些數據行。 這些遺漏的數據行可以透過下列兩種方式之一新增至 DataTable:
- 手動 - 以滑鼠右鍵按兩下 DataSet 中的 DataTable Designer,然後從 [新增] 選單選擇 [資料行]。 然後,您可以命名數據行,並據以設定其屬性。
- 自動 - TableAdapter 組態精靈會更新 DataTable 數據行,以反映預存程式所傳回的
SelectCommand
欄位。 使用臨機操作 SQL 語句時,精靈也會移除InsertCommand
、UpdateCommand
和DeleteCommand
屬性,因為SelectCommand
現在包含JOIN
。 但是,使用預存程式時,這些命令屬性會保持不變。
我們已探索在先前的教學課程中手動新增 DataTable 資料行,包括使用具有詳細數據清單和上傳檔案的主記錄專案符號清單,我們將在下一個教學課程中更詳細地查看此程式。 不過,在本教學課程中,讓我們透過 TableAdapter 組態精靈使用自動方法。
首先,以滑鼠右鍵按下 , EmployeesTableAdapter
然後從操作功能表中選取 [設定]。 這會顯示 TableAdapter 組態精靈,其中列出用於選取、插入、更新和刪除的預存程式,以及傳回值和參數,如果有任何) ,則會 (。 圖 10 顯示此精靈。 在這裡,我們可以看到 Employees_Select
預存程式現在會傳 ManagerFirstName
回 和 ManagerLastName
欄位。
圖 10:精靈顯示預存程式的更新數據行清單 Employees_Select
(按兩下即可檢視大小完整的映像)
按兩下 [完成] 來完成精靈。 傳回 DataSet Designer 時,包含EmployeesDataTable
兩個額外的數據行:ManagerFirstName
和 ManagerLastName
。
圖 11:包含 EmployeesDataTable
兩個新的數據行, (按兩下即可檢視大小完整的影像)
為了說明更新 Employees_Select
的預存程式生效,而且 TableAdapter 的插入、更新和刪除功能仍然正常運作,讓我們建立一個網頁,讓用戶能夠檢視和刪除員工。 不過,在建立這類頁面之前,我們必須先在商業規則層中建立新的類別,以便與 DataSet 的員工合作 NorthwindWithSprocs
。 在步驟 4 中,我們將建立類別 EmployeesBLLWithSprocs
。 在步驟 5 中,我們將從 ASP.NET 頁面使用此類別。
步驟 4:實作商業規則層
在名為EmployeesBLLWithSprocs.cs
的~/App_Code/BLL
資料夾中建立新的類別檔案。 這個類別會模擬現有 EmployeesBLL
類別的語意,只有這個新類別提供較少的方法,並使用 NorthwindWithSprocs
DataSet (,而不是 Northwind
DataSet) 。 將下列程式碼新增至 EmployeesBLLWithSprocs
類別。
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindWithSprocsTableAdapters;
[System.ComponentModel.DataObject]
public class EmployeesBLLWithSprocs
{
private EmployeesTableAdapter _employeesAdapter = null;
protected EmployeesTableAdapter Adapter
{
get
{
if (_employeesAdapter == null)
_employeesAdapter = new EmployeesTableAdapter();
return _employeesAdapter;
}
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Select, true)]
public NorthwindWithSprocs.EmployeesDataTable GetEmployees()
{
return Adapter.GetEmployees();
}
[System.ComponentModel.DataObjectMethodAttribute
(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteEmployee(int employeeID)
{
int rowsAffected = Adapter.Delete(employeeID);
// Return true if precisely one row was deleted, otherwise false
return rowsAffected == 1;
}
}
類別 EmployeesBLLWithSprocs
s Adapter
屬性會傳回 DataSet s 的NorthwindWithSprocs
EmployeesTableAdapter
實例。 這由類別 s GetEmployees
和 DeleteEmployee
方法使用。 方法GetEmployees
會呼叫 對應的 GetEmployees
方法,它會叫Employees_Select
用預存程式,並在 中填入EmployeeDataTable
EmployeesTableAdapter
其結果。 方法DeleteEmployee
同樣會呼叫 會叫EmployeesTableAdapter
Employees_Delete
用預存程式的 s Delete
方法。
步驟 5:使用呈現層中的數據
完成課程之後 EmployeesBLLWithSprocs
,我們即可透過 ASP.NET 頁面來處理員工數據。 JOINs.aspx
開啟資料夾中的頁面AdvancedDAL
,並將 GridView 從 [工具箱] 拖曳至 Designer,並將其ID
屬性設定為 Employees
。 接下來,從 GridView 的智慧標記,將網格線系結至名為 EmployeesDataSource
的新 ObjectDataSource 控制件。
將 ObjectDataSource 設定為使用 EmployeesBLLWithSprocs
類別,然後從 SELECT 和 DELETE 索引標籤中,確定 GetEmployees
已從下拉式清單中選取 和 DeleteEmployee
方法。 按兩下 [完成] 以完成 ObjectDataSource 的設定。
圖 12:將 ObjectDataSource 設定為使用 EmployeesBLLWithSprocs
類別 (按兩下即可檢視大小完整的映像)
圖 13:讓 ObjectDataSource 使用 GetEmployees
和 DeleteEmployee
方法 (按兩下即可檢視完整大小的影像)
Visual Studio 會將 BoundField 新增至每個數據行的 EmployeesDataTable
GridView。 移除除了 、、、 和 以外的所有 BoundField,並將最後四個 BoundField Title
的屬性分別重新命名HeaderText
為 [姓氏]、[名字]、[管理員名字] 和 [管理員姓氏]。ManagerLastName
ManagerFirstName
FirstName
LastName
若要允許使用者從此頁面刪除員工,我們需要執行兩件事。 首先,指示 GridView 從其智慧標記檢查 [啟用刪除] 選項,以提供刪除功能。 其次,將 ObjectDataSource OldValuesParameterFormatString
精靈 original_{0}
所設定的值變更為 () 的預設值 {0}
() 。 進行這些變更之後,您的 GridView 和 ObjectDataSource 宣告式標記看起來應該如下所示:
<asp:GridView ID="Employees" runat="server" AutoGenerateColumns="False"
DataKeyNames="EmployeeID" DataSourceID="EmployeesDataSource">
<Columns>
<asp:CommandField ShowDeleteButton="True" />
<asp:BoundField DataField="Title"
HeaderText="Title"
SortExpression="Title" />
<asp:BoundField DataField="LastName"
HeaderText="Last Name"
SortExpression="LastName" />
<asp:BoundField DataField="FirstName"
HeaderText="First Name"
SortExpression="FirstName" />
<asp:BoundField DataField="ManagerFirstName"
HeaderText="Manager's First Name"
SortExpression="ManagerFirstName" />
<asp:BoundField DataField="ManagerLastName"
HeaderText="Manager's Last Name"
SortExpression="ManagerLastName" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="EmployeesDataSource" runat="server"
DeleteMethod="DeleteEmployee" OldValuesParameterFormatString="{0}"
SelectMethod="GetEmployees" TypeName="EmployeesBLLWithSprocs">
<DeleteParameters>
<asp:Parameter Name="employeeID" Type="Int32" />
</DeleteParameters>
</asp:ObjectDataSource>
透過瀏覽器瀏覽頁面來測試頁面。 如圖 14 所示,頁面會列出每位員工及其經理的名稱, (假設他們有一個) 。
圖 14:預存程式中的 Employees_Select
會JOIN
傳回管理員的名稱 (按兩下即可檢視完整大小的映像)
按兩下 [刪除] 按鈕會啟動刪除工作流程,這會導致預存程序執行 Employees_Delete
。 不過,預存程式中的 DELETE
嘗試語句因為外鍵條件約束違規而失敗, (請參閱圖 15) 。 具體來說,每位員工在 Orders
數據表中有一或多個記錄,導致刪除失敗。
圖 15:刪除具有對應訂單的員工會導致外鍵條件約束違規, (按兩下即可檢視大小完整的影像)
若要允許員工刪除,您可以:
- 更新外鍵條件約束以串聯刪除,
- 針對您想要刪除的員工 () ,手動刪除數據表中的記錄
Orders
,或 - 更新預
Employees_Delete
存程式,先刪除資料表中的Orders
相關記錄,再刪除Employees
記錄。 我們已在 針對具型別數據集的 TableAdapters 使用現有預存程式 教學課程中討論過這項技術。
我將此保留為讀者的練習。
摘要
使用關係資料庫時,查詢通常會從多個相關數據表提取其數據。 相互關聯的子查詢和 JOIN
提供兩種不同的技術,可從查詢中的相關數據表存取數據。 在先前的教學課程中,我們最常使用相互關聯的子查詢,因為 TableAdapter 無法針對涉及 JOIN
的查詢自動產生 INSERT
、 UPDATE
和 DELETE
語句。 雖然可以手動提供這些值,但當使用臨機操作 SQL 語句時,當 TableAdapter 設定精靈完成時,將會覆寫任何自定義。
幸運的是,使用預存程式建立的 TableAdapters 不會受到與使用臨機操作 SQL 語句所建立的相同彈性。 因此,使用預存程式時,建立主要查詢使用的 JOIN
TableAdapter 是可行的。 在本教學課程中,我們已瞭解如何建立這類 TableAdapter。 我們開始使用 JOIN
TableAdapter 主要查詢的 -less SELECT
查詢,以便自動建立對應的插入、更新和刪除預存程式。 透過 TableAdapter 的初始設定完成,我們已增強 SelectCommand
預存程式,以使用 JOIN
並重新執行 TableAdapter 組態精靈來更新 EmployeesDataTable
s 資料行。
重新執行 TableAdapter 組態精靈會自動更新數據 EmployeesDataTable
行,以反映預存程式所 Employees_Select
傳回的數據欄位。 或者,我們可以手動將這些數據行新增至 DataTable。 我們將在下一個教學課程中,探索手動將數據行新增至 DataTable。
快樂的程序設計!
關於作者
Scott Mitchell 是七份 ASP/ASP.NET 書籍的作者,以及 自 1998 年以來與 Microsoft Web 技術合作的 4GuysFromRolla.com 作者。 Scott 是獨立顧問、訓練員和作者。 他的最新書籍是 Sams 在 24 小時內自行 ASP.NET 2.0。 您可以透過mitchell@4GuysFromRolla.com部落格來連線到 ,您可以在 找到http://ScottOnWriting.NET。
特別感謝
本教學課程系列是由許多實用的檢閱者檢閱。 本教學課程的首席檢閱者是「Geisenow」、「David Suru」和「Teresa Murphy」。 有興趣檢閱即將推出的 MSDN 文章嗎? 如果是,請將一行 mitchell@4GuysFromRolla.com放在 。
意見反應
https://aka.ms/ContentUserFeedback。
即將登場:在 2024 年,我們將逐步淘汰 GitHub 問題作為內容的意見反應機制,並將它取代為新的意見反應系統。 如需詳細資訊,請參閱:提交並檢視相關的意見反應