摘要:ADO.NET 和
SqlDataSource 使得人們可以很容易地訪問 ASP.NET 2.0 中的兩層資料。但是,它們在 n 層應用程式中就不是那麼有效了,而
ObjectDataSource 卻能在 n 層應用程式中為業務對象提供相同的易用性。學習如何使用 ASP.NET 2.0 Framework 並利用
ObjectDataSource 控制項產生嚴格意義上的多層 Web 應用程式。
簡介
在 Microsoft ASP.NET 2.0 Framework 中,資料庫訪問得到了極大的簡化。利用全新的 SqlDataSource 控制項,您無需編寫一行代碼就可以選擇、更新、插入和刪除資料庫資料。
產生簡單的應用程式時,SqlDataSource 控制項是一個很好的選擇。如果您需要迅速產生一個使使用者可以顯示和編輯資料庫記錄的 Web 頁,使用 SqlDataSource 控制項在幾分鐘之內就能完成此工作。
例如,我自己就曾計時產生了這麼一個頁面。通過結合使用 SqlDataSource 控制項與 GridView 控制項,我在 1 分 15秒 內就能產生一個用於顯示 Northwind Products 資料庫表的內容的頁面。就有這麼快!
但是,SqlDataSource 控制項存在一個問題。如果您使用 SqlDataSource 控制項,那您就是在做不太妙的事情。SqlDataSource 控制項的缺點在於它迫使您將使用者介面層與商務邏輯層混合在一起。任何應用程式架構師都會告訴您:混合多個層的行為是不可取的。
產生嚴格意義上的多層 Web 應用程式時,您應該具有清晰的使用者介面層、商務邏輯層和資料訪問層。僅僅由於 SqlDataSource 控制項的強制而在使用者介面層引用 SQL 陳述式或預存程序是完全錯誤的。
那麼為什麼您要關心這些東西呢?不錯,在很多情況下,您不必在意。如果您正在建立一個簡單的 Web 應用程式,完全可以使用 SqlDataSource 控制項。例如,如果您需要產生一個由單獨頁面組成的應用程式來顯示資料庫的表的內容,那麼將應用程式劃分為多個應用程式層就很不明智。
遺憾的是(如果您已經為此“交過學費”,則會感到幸運),並非所有的 Web 應用程式都很簡單。應用程式達到一定的複雜程度之後,如果將其劃分為多個應用程式層,則產生和維護它們就更輕鬆。
將應用程式劃分為多個應用程式層有很多優點。如果您有一個清晰的商務邏輯層,就能夠建立一個可以從多個頁面調用的方法庫。換句話說,建立一個清晰的商務邏輯層提升了代碼重用。此外,建立清晰而獨立的應用程式層使得應用程式更易於修改。例如,清晰的層次使您無需修改資料存取碼就可以修改使用者介面。
如果您需要使用 ASP.NET Framework 產生多層 Web 應用程式,那麼您可以使用 ASP.NET 2.0 Framework 所引入的另一個新控制項:ObjectDataSource 控制項ObjectDataSource 控制項使您可將諸如 GridView 和 DropDownList 這樣的使用者介面控制項綁定到一個中介層組件。
這篇文章的主題就是 ObjectDataSource 控制項。在這篇文章中,您將學習如何使用此控制項來顯示和編輯資料庫資料。我們還將討論如何結合使用 ObjectDataSource 控制項和 SqlDataSource 控制項以簡化資料庫訪問。
使用 ObjectDataSource 控制項編輯資料
ObjectDataSource 控制項包含 4 個重要屬性:SelectMethod 屬性、UpdateMethod 屬性、InsertMethod 屬性和 DeleteMethod 屬性。綜合利用這些屬性,您能夠指定執行標準資料庫操作所需的所有方法。
例子:
綁定到資料訪問層
資料訪問層組件封裝 ADO.NET 代碼以通過 SQL 命令查詢和修改資料庫。它通常提煉建立 ADO.NET 串連和命令的詳細資料,並通過可使用適當的參數調用的方法公開這些詳細資料。典型的資料訪問層組件可按如下方式公開:
public class MyDataBllLayer {
public DataView GetRecords();
public int UpdateRecord(int recordID, String recordData);
public int DeleteRecord(int recordID);
public int InsertRecord(int recordID, String recordData);
}
通常是在商務邏輯訪問層定義對資料庫裡記錄的操作,上面就定義了GetRecords、UpdateRecord、DeleteRecord和InsertRecord四個方法來讀取、更新、刪除和插入資料庫裡的資料,這些方法基本上是根據SQL裡的Select、Update、Delete和Insert語句而定義。
和上面方法相對應, ObjectDataSource提供了四個屬性來設定該控制項引用的資料處理,可以按照如下方式關聯到該類型,代碼如下
<asp:ObjectDataSource TypeName="MyDataLayer" runat="server"
SelectMethod="GetRecords"
UpdateMethod="UpdateRecord"
DeleteMethod="DeleteRecord"
InsertMethod="InsertRecord"
/>
這裡的SelectMethon設定為MyDataBllLayer裡的GetRecords()方法,在使用時需要注意ObjectDataSource旨在以聲明的方式簡化資料的開發,所以這裡設定SelectMethod的值為GetRecords而不是GetRecords()。
同樣依次類推,UpdateMethod/DeleteMethod/InsertMethod分別對應的是UpdateRecord
/DeleteRecord/InsertRecord方法。
在上面GetRecords()的定義時,可以看到該方法返回的類型是DataView,由於ObjectDataSource將來需要作為繫結控制項的資料來源,所以它的傳回型別必須如下的傳回型別之一:
Ienumerable、DataTable、DataView、DataSet或者Object。
除此以外,ObjectDataSource還有一個重要的屬性TypeName,ObjectDataSource控制項使用反射技術來從來從商務邏輯程式層的類對象調用相應的方法,所以TypeName的屬性值就是用來標識該控制項工作時使用的類名稱,下面的例子示範了ObjectDataSource的基本使用。
在該例子裡定義了一個資料商務邏輯層來處理資料庫連結和商務邏輯,這些處理都是有App_Code檔案夾下的NorthwndDB.cs檔案完成,先大致瀏覽一下該檔案裡定義的方法:
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.WebControls;
public class EmployeeInfo
{
private string _connectionString;
public EmployeeInfo()
{ _connectionString =
ConfigurationManager.ConnectionStrings["Northwind"].ConnectionString;
}
public SqlDataReader GetEmployees()
{
SqlConnection con = new SqlConnection(_connectionString);
string selectString = "SELECT EmployeeID,LastName,Firstname,Title,Address,City,Region,PostalCode FROM Employees ORDER BY EmployeeID";
SqlCommand cmd = new SqlCommand(selectString, con);
con.Open();
SqlDataReader dtr =
cmd.ExecuteReader(CommandBehavior.CloseConnection);
return dtr;
}
public void UpdateEmployee(int EmployeeId, string LastName, string Firstname, string Title, string Address, string City, String Region, string PostalCode)
{
SqlConnection con = new SqlConnection(_connectionString);
string updateString = "UPDATE Employees SET Address=@Address,City=@City WHERE EmployeeID=@EmployeeID";
SqlCommand cmd = new SqlCommand(updateString, con);
cmd.Parameters.AddWithValue("@Address", Address);
cmd.Parameters.AddWithValue("@City", City);
cmd.Parameters.AddWithValue("@EmployeeID", EmployeeId);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
public void DeleteEmployee(int EmployeeId)
{
SqlConnection con = new SqlConnection(_connectionString);
string deleteString = "DELETE Employees WHERE EmployeeID=@EmployeeID";
SqlCommand cmd = new SqlCommand(deleteString, con);
cmd.Parameters.AddWithValue("@EmployeeId", EmployeeId);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
}
在這段代碼裡,定義了GetEmployees方法來擷取所有員工層級資訊,UpdateEmployee方法更新員工的基本資料,DeleteEmployee方法用來刪除員工資料,這樣就可以在ObjectDataSource1.aspx裡使用如下代碼調用商務邏輯的處理,如下
<asp:GridView
ID="GridView1"
DataSourceID="ObjectDataSource1"
DataKeyNames="EmployeeId"
AutoGenerateColumns="true"
AutoGenerateEditButton="True"
AutoGenerateDeleteButton="True"
Runat="Server" CellPadding="4" Font-Names="Verdana" Font-Size="X-Small" ForeColor="#333333" GridLines="None">
… …
</asp:GridView>
<asp:ObjectDataSource
ID="ObjectDataSource1"
TypeName="EmployeeInfo"
SelectMethod="GetEmployees"
UpdateMethod="UpdateEmployee"
DeleteMethod="DeleteEmployee"
Runat="Server">
<UpdateParameters>
<asp:Parameter Name="EmployeeId" Type="Int32" />
<asp:Parameter Name="Address" />
<asp:Parameter Name="City" />
</UpdateParameters>
</asp:ObjectDataSource>
上面使用了GrieView控制項後面會有專門介紹,這裡我們專註ObjectDataSource控制項的使用,在該控制項裡設定 TypeName為"EmployeeInfo"表示將來調用的類名稱為EmployeeInfo,調用SelectMethond/UpdateMethod/DeleteMethod調用的的方法分別是EmployeeInfo類裡的GetEmployees/UpdateEmployee/DeleteEmployee方法。
具體運行可以自己實驗。從運行結果裡單擊“Edit”可以更新員工的資料,單擊“Delete”將刪除員工的資料。
在運行上面的例子裡,可能看到UpdateEmployee的定義如下:
public void UpdateEmployee(int EmployeeId, string LastName, string Firstname, string Title, string Address, string City, String Region, string PostalCode)
{ ... ...
cmd.Parameters.AddWithValue("@Address", Address);
cmd.Parameters.AddWithValue("@City", City);
cmd.Parameters.AddWithValue("@EmployeeID", EmployeeId);
... ...
}
這段代碼錶示更新員工資料時,其實只更新了員工的地址(Address)和所在的程式(City),而對于姓名(FirstName,LastName)、職稱(Title)等並沒有更新。
既然FistName、LastName、Title等並沒有更新,為什麼不將UpdateEmployee寫成如下方式呢?
public void UpdateEmployee(int EmployeeId string Address, string City,)
{ ... ...
cmd.Parameters.AddWithValue("@Address", Address);
cmd.Parameters.AddWithValue("@City", City);
cmd.Parameters.AddWithValue("@EmployeeID", EmployeeId);
... ...
}
也就是只保留需要的三個參數而將其它變數不寫,如果這樣做將發生錯誤如下:
提示錯誤為:ObjectDataSource1找不到一個包含Address,City,LastName,Firstname,Title,Region,PostalCode和Employee參數的非泛型方法(具體原因我還沒有進行驗證,我的感覺是ObjectDataSource是根據Select語句自動產生Delete/Insert/Update等的,所以在更新時,同樣需要該參數,你可以到如下MSDN查看SelectMethod的說明:
http://msdn2.microsoft.com/en-us/library/system.web.ui.webcontrols.objectdatasource.selectmethod(VS.80).aspx
)。
而我看到的例子,大多都是如下寫法:
public void UpdateEmployee(int EmployeeId, string LastName, string Firstname, string Title, string Address, string City, String Region, string PostalCode)
{
UpdateEmployee(EmployeeId,Address,City)
}
public void UpdateEmployee(int EmployeeId, string Address, string City )
{
SqlConnection con = new SqlConnection(_connectionString);
string updateString = "UPDATE Employees SET Address=@Address,City=@City WHERE EmployeeID=@EmployeeID";
SqlCommand cmd = new SqlCommand(updateString, con);
cmd.Parameters.AddWithValue("@Address", Address);
cmd.Parameters.AddWithValue("@City", City);
cmd.Parameters.AddWithValue("@EmployeeID", EmployeeId);
con.Open();
cmd.ExecuteNonQuery();
con.Close();
}
通過重載UpdateEmployee方法,便於資料的擴充和維護。
綁定到商務邏輯
為了更好的進行業務處理,需要進一步封裝商務邏輯,以便返回強型別,舉例定義一個Product來封裝資料庫,具體由Product.cs類來實現並放在App_Code目錄.
using System;
public class Product
{
protected int _productID;
protected String _productName;
protected int _categoryID;
protected decimal _price;
protected int _inStore;
protected String _description;
public int ProductID
{
get { return _productID; }
set { _productID = value; }
}
public String ProductName
{
get { return _productName; }
set { _productName = value; }
}
public int CategoryID
{
get { return _categoryID; }
set { _categoryID = value; }
}
public decimal Price
{
get { return _price; }
set { _price = value; }
}
public int InStore
{
get { return _inStore; }
set { _inStore = value; }
}
public String Description
{
get { return _description; }
set { _description = value; }
}
public Product()
{ }
public Product(int productID, string productName, int categoryID, decimal price, int instore, string description)
{
this._productID = productID;
this._productName = productName;
this._categoryID = categoryID;
this._price = price;
this._inStore = instore;
this._description = description;
}
}
然後定義商務邏輯類ProductDB.cs來進行處理:
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 System.Collections.Generic;
using System.Data.SqlClient;
/// <summary>
/// ProductDB 的摘要說明
/// </summary>
public class ProductDB
{
public ProductDB()
{
//
// TODO: 在此處添加建構函式邏輯
//
}
public List<Product> GetProduct()
{
List<Product> products = new List<Product>();
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString);
string commandText = "select * from Products";
SqlCommand command = new SqlCommand(commandText,conn);
conn.Open();
SqlDataReader dr = command.ExecuteReader();
while (dr.Read())
{
Product prod = new Product();
prod.ProductID = (int)dr["ProductID"];
prod.ProductName = (string)dr["ProductName"];
prod.CategoryID = (int)dr["CategoryID"];
prod.Price = (decimal)dr["price"];
prod.InStore = (Int16)dr["InStore"];
prod.Description = (String)dr["Description"];
products.Add(prod);
}
dr.Close();
conn.Close();
return products;
}
public void UpdateProduct(Product pro)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString);
SqlCommand updatecmd = new SqlCommand("UPDATE Products set ProductName=@ProductName,CategoryID=@CategoryID,Price=@Price,InStore=@InStore,Description=@Description where ProductID=@ProductID", conn);
updatecmd.Parameters.Add(new SqlParameter("@ProductName", pro.ProductName));
updatecmd.Parameters.Add(new SqlParameter("CategoryID", pro.CategoryID));
updatecmd.Parameters.Add(new SqlParameter("@Price", pro.Price));
updatecmd.Parameters.Add(new SqlParameter("@InStore", pro.InStore));
updatecmd.Parameters.Add(new SqlParameter("@Description", pro.Description));
updatecmd.Parameters.Add(new SqlParameter("@ProductID", pro.ProductID));
conn.Open();
updatecmd.ExecuteNonQuery();
conn.Close();
}
public void DeleteProduct(Product pro)
{
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["MyDatabase"].ConnectionString);
SqlCommand delcmd = new SqlCommand("delete from Products where ProductID=@ProductID", conn);
delcmd.Parameters.Add(new SqlParameter("@ProductID", pro.ProductID));
conn.Open();
delcmd.ExecuteNonQuery();
conn.Close();
}
}
最後建立前台分頁檔,添加objectdatasource控制項顯示資料
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" DataObjectTypeName="Product"
DeleteMethod="DeleteProduct" SelectMethod="GetProduct" TypeName="ProductDB" UpdateMethod="UpdateProduct">
</asp:ObjectDataSource>
<asp:GridView ID="GridView1" runat="server" AutoGenerateDeleteButton="True" AutoGenerateEditButton="True"
CellPadding="4" DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" Font-Names="Verdana"
Font-Size="XX-Small" ForeColor="#333333" GridLines="None">
<FooterStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" />
<RowStyle BackColor="#E3EAEB" />
<EditRowStyle BackColor="#7C6F57" />
<SelectedRowStyle BackColor="#C5BBAF" Font-Bold="True" ForeColor="#333333" />
<PagerStyle BackColor="#666666" ForeColor="White" HorizontalAlign="Center" />
<HeaderStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" />
<AlternatingRowStyle BackColor="White" />
</asp:GridView>