ObjectDataSource的用途
ObjectDataSource是VS2005新增的資料來源元件,在VS2005中,List類型之UI元件都支援DataSource定義資料來源的能力,而DataSource可以是下列的任何一項:- SqlDataSource - 直接連接資料庫
- XMLDataSource - 連結XML檔案
- AccessDataSource - 連結Access檔案
- SiteMap格式的SiteMapDataSource
- ObjectDataSource - 自行撰寫Class寫出資料來源的界接,讓VS2005的UI元件聯繫個功能的Method來做資料的取得。

每一個具備List功能的UI元件都可以去聯繫DataSource,而其中ObjectDataSource是可以做客製化設計的一個資料來源。

要提供給ObjectDataSource的Class必須提供Static的Method,可以分別針對特定功能(Select/Insert/Delete/Update)設計該Static Method,每個特定功能的Method都規範的傳遞值與回傳值。
- Select - Select Method允許自行定義0個以上任意型態的傳入參數,輸入參數可以對應至其他UI元件所選擇的數值、QueryString參數、Session儲存值、Cookie值或自行輸入數值來決定Select範圍。對於回傳值部分則限制必須為下列其中一項:DataTable、DataView、DataSet、具備IEnumerable型態的Object。
- Insert/Delete/Update - 此類Method是提供給GridView、DetailView或FormView等同時具備新增/編輯/刪除功能的UI元件,這些Method都不需要有回傳值,而傳入參數則是由該UI元件呼叫填入而必須存在,參數建議是採用較易管理的商業邏輯,透過一個獨立的class設計,而該class具備各種資料名稱的Property。當Method被呼叫的同時,DataBind對應的個資料欄位,就會填入各相同名稱的Property內,以方便後續的處理。
/// 單位資料
class Department{
private string pShortName, pFullName, pDeptCode;
public Department() {
};
/// 單位縮寫
public string ShortName {
get {
return this.pShortName;
}
set {
this.pShortName = value;
}
}
/// 單位全銜
public string FullName {
get {
return this.pFullName;
}
set {
this.pFullName = value;
}
}
/// 單位代碼
public string DeptCode {
get {
return this.pDeptCode;
}
set {
this.pDeptCode = value;
}
}
};
在擁有一個可以存放單位資料的Class之後,我在另外設計一個Collection負責來存放n筆Department Class,由於此類型的Collection我想針對Department,但是又不想分別為每個Unit Class重新設計,所以我把它設計成泛型的Class並且繼承自CollectionBase元件,因為CollectionBase具備IEnumerable介面,因此符合Select Method回傳值定義。
/// 抽象化資料集合元件
public class DataCollection : CollectionBase {
/// 新增物件
public int Add(T pT) {
return this.List.Add(pT);
}
/// 新增物件
public void Insert(int pIndex, T pT) {
this.List.Insert(pIndex, pT);
}
/// 刪除物件
public void Remove(T pT) {
this.List.Remove(pT);
}
/// 檢查物件
public bool Contains(T pT) {
return this.List.Contains(pT);
}
/// 取得物件位置
public int IndexOf(T pT) {
return this.List.IndexOf(pT);
}
/// 取得索引之物件
public T this[int pIndex] {
get {
return (T)this.List[pIndex];
}
set {
this.List[pIndex] = value;
}
}
}
再來我們就要針對資料的取得以及如果將窗口建立好讓UI原件可以透過ObjectDataSource取得資料來做努力了,首先我們先針對資料的取得分別設計Insert/Delete/Update/Select等功能的Class(範例將只有針對Select實作),在此我們稱呼他為DepartmentProvider。
/// Department資料實現
public class DepartmentProvider {
private Oracle.DataAccess.Client.OracleConnection pConnection = null;
private string ConnectionString="請自己填上ConnecitonString";
/// 建立預設資料庫連結元件
public DepartmentProvider() {
pConnection = new Oracle.DataAccess.Client.OracleConnection(ConnectionString);
}
/// 取得一級單位(限定學院)
public DataCollection SelectFirstClass() {
string pSql = @"select DEPT_CODE,DEPT_CNAME,DEPT_CNAME_A from DEPARTMENT where dept_code like 'B_X'";
DataCollection pDepartmentCollection = new DataCollection();
try {
this.pConnection.Open();
Oracle.DataAccess.Client.OracleDataAdapter pAdapter = new Oracle.DataAccess.Client.OracleDataAdapter("", pConnection);
pAdapter.SelectCommand.CommandText = pSql;
pAdapter.SelectCommand.Parameters.Clear();
DataSet pDataSet = new DataSet();
pAdapter.Fill(pDataSet);
pConnection.Close();
foreach (DataRow pRow in pDataSet.Tables[0].Rows) {
string pDeptCode = Convert.ToString(pRow["DEPT_CODE"]).Trim();
string pFullName = Convert.ToString(pRow["DEPT_CNAME"]).Trim();
string pShortName = Convert.ToString(pRow["DEPT_CNAME_A"]).Trim();
Department pDepartment = new Department(pShortName, pFullName, pDeptCode);
pDepartmentCollection.Add(pDepartment);
}
}
finally {
if (pConnection.State != System.Data.ConnectionState.Closed) {
pConnection.Close();
}
}
return pDepartmentCollection;
}
/// 取得二級單位(限定系或所)
public DataCollection SelectSecondClass(string pClass) {
if (pClass == "")
return null;
string pSql = @"select DEPT_CODE,DEPT_CNAME,DEPT_CNAME_A from DEPARTMENT where MASTER_DEPTCODE=:MASTER_DEPTCODE AND SUBSTR(DEPT_CODE,1,1) in ('U','G') ORDER BY DEPT_CODE DESC";
if (pClass == "ALL") {
pSql = @"select DEPT_CODE,DEPT_CNAME,DEPT_CNAME_A from DEPARTMENT where SUBSTR(DEPT_CODE,1,1) in ('U','G') ORDER BY DEPT_CODE DESC";
}
DataCollection pDepartmentCollection = new DataCollection();
try {
pConnection.Open();
Oracle.DataAccess.Client.OracleDataAdapter pAdapter = Oracle.DataAccess.Client.OracleDataAdapter("", pConnection);
pAdapter.SelectCommand.CommandText = pSql;
pAdapter.SelectCommand.Parameters.Clear();
pAdapter.SelectCommand.Parameters.Add(new Oracle.DataAccess.Client.OracleParameter("MASTER_DEPTCODE", pClass));
DataSet pDataSet = new DataSet();
pAdapter.Fill(pDataSet);
pConnection.Close();
foreach (DataRow pRow in pDataSet.Tables[0].Rows) {
string pDeptCode = Convert.ToString(pRow["DEPT_CODE"]).Trim();
string pFullName = Convert.ToString(pRow["DEPT_CNAME"]).Trim();
string pShortName = Convert.ToString(pRow["DEPT_CNAME_A"]).Trim();
Department pDepartment = new Department(pShortName, pFullName, pDeptCode);
pDepartmentCollection.Add(pDepartment);
}
}
finally {
if (pConnection.State != System.Data.ConnectionState.Closed) {
pConnection.Close();
}
}
return pDepartmentCollection;
}
}
[System.ComponentModel.DataObject]
public class DepartmentServices {
private static DepartmentProvider _DepartmentProvider = null;
/// 取得資料庫連結元件
public static DepartmentProvider LoadProvider() {
if (_DepartmentProvider == null) {
_DepartmentProvider = new DepartmentProvider();
}
return _DepartmentProvider;
}
/// 取得一級單位
[System.ComponentModel.DataObjectMethod(System.ComponentModel.DataObjectMethodType.Select)]
public static DataCollection SelectFirstClass() {
return LoadProvider().SelectFirstClass();
}
/// 取得二級單位
/// pClass:所屬一級單位
[System.ComponentModel.DataObjectMethod(System.ComponentModel.DataObjectMethodType.Select)]
public static DataCollection SelectSecondClass(string pClass) {
return LoadProvider().SelectSecondClass(pClass);
}
}

下一步則是設定該ObjectDataSource所對應的Class,在此我們將對應至DepartmentServices。

設定ObjectDataSource的SelectMethod為SelectFirstClass,讓此UI元件設計顯示一級單位的資料。

設定此UI元件Display/Value分別為Department Class的哪一個Property。

先看看到目前為止的結果,符合預期狀況,DropDownList內在測試下直接存放一級單位資料於Items內提供挑選。

透過GridView可以列出多筆資料的優點,我在開一個新的ObjectDataSource而該SelectMethod這次選擇SelectSecondClass,由於該Method需要傳遞一個pClass來設定一級單位,因此將pClass來源設定為Control並且指向剛剛成功可以選擇一級單位的DropDownList,並記得將DropDownList的AutoPostBack設為True以方便立即反應。

我們再次Demo原本的網頁看是否符合預期。

以ObjectDataSource來做資料面的管控雖然需要多寫幾個Class,但是我們可以將Data/View兩者之間很乾淨的切開來,當然View部分可能還是需要部分的程式碼控制來增強互動,但是ObjectDataSource的方式不僅僅是讓資料面回歸到單純的資料提供者的角色,也同時避免UI經常性用程式碼在填入資料。而ObjectDataSource在此例子中是透過讀取DataBase的方式來做使用,雖然已經具備SqlDataSource元件可以提供讀取資料庫了,那還有需要這麼做嗎?我個人覺得是必要的。當然簡單的系統是可以直接透過SqlDataSource去存取資料庫即可,不需要如此複雜,而採用ObjectDataSource是為了方便在現在資料在作加工的動作,畢竟Class的Property是自行定義的,並不是絕對必須要跟資料庫的Table保持一致性,可以自行增加一些暫存型資訊(如從Session/Cookie取得),或降低複雜度減少Property的數量(如50個field的Table只用到10個field),當然也能多個Table資料合併成一個Class來使用,或部分Property的值在被提取的時候才實際作DataBase的Select,最重要的是由於資料來源是自行設計的,因此資料不必是DataBase、XML、PlainText、WebService、HTML,只要有辦法產生對應的資料即可,所以在暫時需要TestPartten的時候,也可以在Provider上作手腳。方法可以在Provider內自行決定與採用,畢竟這是一個架構並沒有要求實作方式。
沒有留言:
張貼留言