三層架構中使用實體類填充泛型集合代替DataTable解決方案(ASP.NET+C#)
D層負責與資料庫互動,一般是得到DataTable或DataSet物件,然後返回給B層,B層進行類似的處理來讀取資料:dt.Rows[0][“xxx”];或者dt.Rows[0][1];(強烈不建議使用)。
有時DataTable也會被傳到UI層,與控制元件進行繫結,顯示資料。例如ASP.NET的repeater控制元件提取資料:<%# Eval(“xxx”)%>。
| 非常容易寫錯,而且編譯器不檢查。
| 必須瞭解資料庫的結構。
| 不符合面向物件程式設計思想。
| DataTable為弱型別,無法直觀的看出欄位的資料型別。
核心思想如圖:
實體類即資料庫的對映,因此實體類中的屬性和資料庫表中的欄位是相對應的。把DataTable中的每一行記錄視為一個實體類,把其中的欄位讀取出來,存放到實體類的屬性中,再把所有的實體類存在泛型集合中。因此,DataTable中有多少個記錄,泛型集合中就有多少個實體類,每個實體類的屬性和DataTable的欄位是相對應的。
這樣做的優點如下:
| 編寫B層的人員無需手動填寫需要的欄位,直接按一下點,全都提示出來了,想用哪個用哪個,不會出現寫錯的情況。
| 不必瞭解資料庫結構。
| 符合面向物件思想。
| 實體類的屬性是強型別,每個欄位的型別都是已知的。
那麼用程式碼如何實現呢?下面一一列舉。
注意:以下程式碼僅僅是為了模擬,有些不規範的地方,程式碼註釋中有提示,切勿模仿!
實體類程式碼:
[csharp] view plain copy print ?
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace Entity
- {
- /// <summary>
- /// Users表實體類
- /// </summary>
- public class Users
- {
- private long _id;
- private string _userName;
- private string _passWord;
- public long id
- {
- set {_id = value; }
- get { return _id; }
- }
- public string userName
- {
- set { _userName = value; }
- get { return _userName; }
- }
- public string passWord
- {
- set { _passWord = value; }
- get { return _passWord; }
- }
- }
- }
將DataTable轉換成List<T>泛型集合助手類,這個類我放在了Entity實體類層中:
[csharp] view plain copy print ?
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Data;
- using System.Data.SqlClient;
- using System.Reflection;
- namespace Entity
- {
- /// <summary>
- /// 將DataTable轉換成泛型集合IList<>助手類
- /// </summary>
- public class ConvertHelper
- {
- /// <summary>
- /// 單表查詢結果轉換成泛型集合
- /// </summary>
- /// <typeparam name="T">泛型集合型別</typeparam>
- /// <param name="dt">查詢結果DataTable</param>
- /// <returns>以實體類為元素的泛型集合</returns>
- public static IList<T> convertToList<T>(DataTable dt) where T : new()
- {
- // 定義集合
- List<T> ts = new List<T>();
- // 獲得此模型的型別
- Type type = typeof(T);
- //定義一個臨時變數
- string tempName = string.Empty;
- //遍歷DataTable中所有的資料行
- foreach (DataRow dr in dt.Rows)
- {
- T t = new T();
- // 獲得此模型的公共屬性
- PropertyInfo[] propertys = t.GetType().GetProperties();
- //遍歷該物件的所有屬性
- foreach (PropertyInfo pi in propertys)
- {
- tempName = pi.Name;//將屬性名稱賦值給臨時變數
- //檢查DataTable是否包含此列(列名==物件的屬性名)
- if (dt.Columns.Contains(tempName))
- {
- // 判斷此屬性是否有Setter
- if (!pi.CanWrite) continue;//該屬性不可寫,直接跳出
- //取值
- object value = dr[tempName];
- //如果非空,則賦給物件的屬性
- if (value != DBNull.Value)
- pi.SetValue(t, value, null);
- }
- }
- //物件新增到泛型集合中
- ts.Add(t);
- }
- return ts;
- }
- }
- }
ASP.NET Web頁面後臺程式碼(aspx.cs):
[csharp] view plain copy print ?
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.UI;
- using System.Web.UI.WebControls;
- using Entity;
- using System.Data;
- using System.Data.SqlClient;
- public partial class _Default : System.Web.UI.Page
- {
- protected void Page_Load(object sender, EventArgs e)
- {
- if (!Page.IsPostBack)
- {
- DataTable dt = new DataTable();
- IList<Users> users = new List<Users>();
- //注意:只是為了演示,才在這讀資料庫的!
- //在專案中千萬不要這麼做!另外sql連結字串也不應該寫在程式中,而且要加密。
- SqlConnection sqlCon = new SqlConnection("server=192.168.24.177;database=testDB;uid=sa;pwd=123");
- SqlCommand sqlCmd = new SqlCommand("select userName,passWord from t_testTable", sqlCon);
- SqlDataReader sqlRd;
- sqlCon.Open();
- sqlRd = sqlCmd.ExecuteReader();
- dt.Load(sqlRd); //DataTable構造完成,在這僅僅是為了演示,構造DataTable
- sqlCon.Close();
- users = ConvertHelper.convertToList<Users>(dt); //獲取DataTable轉換成的泛型集合
- repUsers.DataSource = users;
- repUsers.DataBind();
- }
- }
- }
ASP.NET Web頁面前臺程式碼(aspx):
[html] view plain copy print ?
- <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head runat="server">
- <title></title>
- </head>
- <body>
- <form id="frmDefault" runat="server">
- <div>
- <asp:Repeater ID="repUsers" runat="server">
- <ItemTemplate>
- <table>
- <tr>
- <td><%# ((Entity.Users)Container.DataItem).id.ToString() %></td>
- <td><%# ((Entity.Users)Container.DataItem).userName %></td>
- <td><%# ((Entity.Users)Container.DataItem).passWord %></td>
- </tr>
- </table>
- </ItemTemplate>
- </asp:Repeater>
- </div>
- </form>
- </body>
- </html>
這段程式碼在Web後臺讀取了資料中的資料,當然,只是為了模擬,這樣寫是非常不可取的。得到DataTable後利用ConvertHelper轉換助手類的convertToList方法將DataTable轉換成本例中的Users型別的泛型集合。最後把該集合與Repeater控制元件繫結,顯示資料。
Repeater之類的控制元件均支援繫結集合,而且支援的如此完美。個人認為介面顯示那段程式碼是最漂亮的。
例如:<%#((Entity.Users)Container.DataItem).id.ToString() %>。這和以往的顯示方法大不相同。首先,沒有使用Eval。資料是從Container.DataItem屬性中讀取的,強制轉換為實體類型別,然後直接呼叫實體類的id屬性。整個程式碼行雲流水般使用,完全不需要參照資料庫,想用哪個屬性用哪個。另外,這樣讀取效率比用Eval高。
在此,需要說明的是,我在這寫的DataTable到List<T>的類是比較可靠的。類似ConvertHelper.convertToList<Users>(dt),想把DataTable轉換成哪種實體類,呼叫方法的時候泛型引數(尖括號裡的)就寫哪個實體類,在這是Users。簡單說一下它的轉換原理。
它會自動獲取實體類的屬性名,然後DataTable中匹配有沒有該名稱的欄位,有的話就賦值。DataTable中的每一行記錄都這樣處理。
基於以上原理,使用本方法必須滿足以下條件,也可以說是注意事項吧:
| 實體類的屬性名必須和資料庫表中的欄位名一模一樣。
| 想用這種方法把DataTable轉換成實體類,必須有已知的實體類和DataTable中的資料想對應,也就是說必須明確你要轉換成的實體類型別,否則沒辦法指定泛型集 合的型別,也就沒辦法呼叫。
| 各個表的欄位儘量區別開來,不要相同。比如A表有個name欄位,B表也有一個name欄位,就不合理了。
相信你看到這已經躍躍欲試了。可仔細思考一下,會發現一個大問題:這裡涉及的僅僅是單表查詢,聯合查詢顯然搞不定。
聯合查詢搞不定的主要原因是它的資料來自多個表,沒有實體類與之對應,沒有實體類就沒有辦法轉換。
目前比普遍的解決方案就是建立一個包含多個表的實體類,只要實體類中包含聯合查詢的這些表就可以,實體類中表示的表多了沒事。但是,很多種聯合查詢,就要建立很多實體類,這也是沒有辦法的事。
也有比較變態的方法就是建立一個通用實體類,包含所有表的欄位。
要想用此方法,建立新的實體類是必然的了。但即使是這樣,我們也可以節省程式碼,避免重複。
節省程式碼首先想到的是繼承,可惜C#不支援多重繼承,兩個以上實體類組合成一個實體類就沒轍了。用介面雖然可以實現多重繼承,但顯然不合理,介面不是幹這個用的,這樣一點程式碼也沒省下,反而麻煩了。
經過網友RenYue的提示,用聚合解決是種比較好的方案。
聚合核心思想是:假如五個表聯合查詢,那麼新建一個實體類,這個新實體類的屬性是這個五個表對應的實體類,即用實體類封裝實體類,我們暫且叫它聚合實體類。
這個想法很好,很好的複用了程式碼,但是實現起來比較複雜。剛剛提供的單個表查詢的轉換方法(convertToList),顯然滿足不了。
經過研究,我確定沒辦法寫一個通用的多表查詢轉換方法。但是可以實現確定個數的轉換方法。我寫了一個兩個表聯合查詢的轉換方法。
把DataTable每一行記錄中的欄位分解給所屬表對應的實體類,然後把這些實體類橫向聚合到聚合實體類的屬性中。如圖:
為了演示,程式做如下變動:
在Entity專案中新增一個LoginLog類,使用者登入日誌:
[csharp] view plain copy print ?
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace Entity
- {
- /// <summary>
- /// LoginLog表實體類
- /// </summary>
- public class LoginLog
- {
- private long _id;
- private string _userName;
- private DateTime _loginTime;
- private DateTime _logoutTime;
- public long id
- {
- get { return _id; }
- set { _id = value; }
- }
- public string userName
- {
- get { return _userName; }
- set { _userName = value; }
- }
- public DateTime loginTime
- {
- get { return _loginTime; }
- set { _loginTime = value; }
- }
- public DateTime logoutTime
- {
- get { return _logoutTime; }
- set { _logoutTime = value; }
- }
- }
- }
在Entity專案中新增一個UsersJionLog類,該實體類用於Users表和LoginLog表聯合查詢,它的屬性是Users類和LoginLog類:
[csharp] view plain copy print ?
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace Entity
- {
- /// <summary>
- /// UsersJionLog表實體類,該實體類用於Users表和LoginLog表聯合查詢
- /// </summary>
- public class UsersJionLog
- {
- private Users _users;
- private LoginLog _loginLog;
- public Users users
- {
- set { _users = value; }
- get { return _users; }
- }
- public LoginLog loginLog
- {
- set { _loginLog = value; }
- get { return _loginLog; }
- }
- }
- }
在ConvertHelper類中新增兩個表聯合查詢的轉換方法:
- /// <summary>
- /// 兩表查詢結果轉換成泛型集合
- /// </summary>
- /// <typeparam name="T">包含了兩個表的實體類,聚合實體類</typeparam>
- /// <typeparam name="U">實體類中的第一個實體類</typeparam>
- /// <typeparam name="V">實體類中的第二個實體類</typeparam>
- /// <param name="dt">從資料庫中查詢獲得的DataTable</param>
- /// <returns>以實體類為元素的泛型集合</returns>
- public static IList<T> convertToJiontList<T, U, V>(DataTable dt)
- where T : new()
- where U : new()
- where V : new()
- {
- //定義一個聚合實體類泛型集合,用來做返回值
- List<T> ts = new List<T>();
- //定義一個泛型集合元素,用來填充集合
- T t = new T();
- //獲取聚合實體類的屬性
- PropertyInfo[] propertys = t.GetType().GetProperties();
- //利用單錶轉換,把DataTable資料填充到聚合實體類中第一個實體類
- IList<U> uList = new List<U>();
- uList = convertToList<U>(dt);
- //利用單錶轉換,把DataTable資料填充到聚合實體類中第二個實體類
- IList<V> vList = new List<V>();
- vList = convertToList<V>(dt);
- //經過以上兩步,uList和vList兩個集合的長度肯定是相同的
- //把兩個實體類填充到聚合實體類中
- for (int i = 0; i < uList.Count; i++)
- {
- propertys[0].SetValue(t, uList[i], null); //取uList中第i個元素,填充到聚合實體類的第一個屬性中
- propertys[1].SetValue(t, vList[i], null); //取vList中第i個元素,填充到聚合實體類的第二個屬性中
- ts.Add(t); //向聚合實體類集合中新增元素
- }
- return ts;
- }
ASP.NET Web後臺(aspx.cs)程式碼:
[csharp] view plain copy print ?
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.UI;
- using System.Web.UI.WebControls;
- using Entity;
- using System.Data;
- using System.Data.SqlClient;
- public partial class _Default : System.Web.UI.Page
- {
- protected void Page_Load(object sender, EventArgs e)
- {
- if (!Page.IsPostBack)
- {
- DataTable dt = new DataTable();
- IList<UsersJionLog> usersJionLog = new List<UsersJionLog>();
- //注意:只是為了演示,才在這讀資料庫的!
- //在專案中千萬不要這麼做!另外sql連結字串也不應該寫在程式中,而且要加密。
- SqlConnection sqlCon = new SqlConnection("server=192.168.24.177;database=testDB;uid=sa;pwd=123");
- SqlCommand sqlCmd = new SqlCommand("select t_Users.userName,t_LoginLog.loginTime,t_LoginLog.logoutTime from t_Users join t_LoginLog on t_LoginLog.userName = t_Users.userName where t_Users.userName = 'admin'", sqlCon);
- SqlDataReader sqlRd;
- sqlCon.Open();
- sqlRd = sqlCmd.ExecuteReader();
- dt.Load(sqlRd); //DataTable構造完成,在這僅僅是為了演示,構造DataTable
- sqlCon.Close();
- usersJionLog = ConvertHelper.convertToJiontList<UsersJionLog,Users,LoginLog>(dt);
- repUsers.DataSource = usersJionLog;
- repUsers.DataBind();
- }
- }
- }
ASP.NET Web前臺(aspx):
[html] view plain
相關推薦
三層架構中使用實體類填充泛型集合代替DataTable解決方案(ASP.NET+C#)
用三層架構開發專案,經常會遇到如下場景: D層負責與資料庫互動,一般是得到DataTable或DataSet物件,然後返回給B層,B層進行類似的處理來讀取資料:dt.Rows[0][“xxx”];或者dt.Rows[0][1];(強烈
淺談三層架構中的實體類(C#)
最近因為三層架構中的實體類,引發了不少小問題,下面列舉一下,談談自己的感想。 本文所指的實體類僅限於三層中的實體類,即資料庫表的對映。 一、為什麼要用實體類? | 使程式簡潔易懂,便於維護。
JavaWeb三層架構中Service和Dao層物件單例化可行性
宣告:以下個人觀點,僅作參考; 閱讀正文的前提知識: 一. 單例模式: 單例概念(百度): 單例模式,是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中,應用該模式的類一個類
橋接模式的應用之三層架構中的業務邏輯層(BLL)與資料訪問層(DAL)的解耦
各層的作用 ①使用者介面層:只負責顯示和採集使用者操作。 ②業務邏輯層:負責UI和DAL層之間的資料交換,是系統架構中體現核心價值的部分。它關注點主要集中在業務規則的制定、業務流程的實現和業務需求的有關係統設計。它與系統所對應的領域(Domain)有關。也可以做一些如使用
C#泛型入門學習泛型類、泛型集合、泛型方法、泛型約束、泛型委託
本章閱讀列表 泛型很難理解?不然 泛型集合和ArrayList的裝箱拆箱 常見的泛型型別 泛型類和泛型方法 泛型約束 泛型委託 泛型很難理解?不然 在接觸的一個新的概念的時候,總會感覺難以理解,當你掌握並能熟練地使用的時候,發現這個概念其實簡單的,我相信
EF-DbUpdateException--實體類和資料庫列不對應的解決方案
錯誤資訊 1.VS實體類裡面的欄位 2資料庫裡面的欄位 猜測是因為欄位數不匹配導致的 3刪除多餘欄位 5.結果 錯誤資訊貼上: -------------------------Log_Header-------------------------2015-06-06
C#非泛型集合類與泛型集合類的區別
C# 泛型集合之非泛型集合類與泛型集合類的對應: ArrayList對應List HashTable對應Dictionary Queue對應Queue Stack對應Stack SortedList對應SortedList C# 集合類 Array Arraylist L
關於C#三層架構增刪改查中的“刪除”問題
正在 font com 時間 convert strong int32 ring 三層架構 序: 剛學習C#,經過一段時間學習,現在正在做一個簡單的前後臺聯通的項目(主要是C#三層架構實現增刪改查)。分享一點兒小經驗,也供自己以後可以回頭看看自己的碼農之路。 內容: 主要分
關於C#三層架構增刪改查中的“查詢”問題
可能 一行 rep tro spa 結束 簡單 問題: .get 序:問題總是反復出現,可能只是一個小小的問題,但是就像肉中刺。 問題: 關於“姓名”字段的拼接問題 姓名字段的拼接:this.Repeater1.DataSource = db.GetList(" UNa
【Web篇08】Web中MVC設計理念 & 經典三層架構 & 五大主流框架
一、Web的MVC設計理念和MVC框架: 之前在Web篇04中已經詳述了Servlet、TomCat、JSP和Web.xml之間的聯絡; (1)瀏覽器傳送請求到收到響應,簡要的過程如下: 瀏覽器傳送請求後,由Web.xml中規定的協議,進入TomCat中特定的Servlet,伺服器先
hibernate中實體類的三種狀態和crud操作
實體類 private int uid; private String name; private String age; private String address; public int getUid() { return uid;
MVC 三層架構在各框架中的特徵
理論篇:花了整整一週多的時間來整理源生的jsp+servlet框架、struts1、struts2、springMVC這幾個框架,激動之中未免有些不足,在這些框架裡面要想從整體的角度考慮,就必須把客戶端到伺服器端所有的東西理清晰。感覺現在的還有很多不足,但雯雪會在學習的過程中不斷修改,不斷整理,望各位,多
.net中的三層架構簡介
三層架構(3-tier application) 通常意義上的三層架構就是將整個業務應用劃分為:表現層(UI)、業務邏輯層(BLL)、資料訪問層(DAL)。區分層次的目的即為了“高內聚,低耦合”的思想。概念簡介 1、表現層(UI):通俗講就是展現給使用者的介面,即使用者
C#中的三層架構+工廠模式BLL、DAL、IDAL、MODEL、DBUtility、DALFactory層級
三層架構,我們一般說的三層架構通常指的是: 1、表現層(UI):就是展現給使用者的介面,無論是網站前臺還是應用程式介面; 2、業務邏輯層(BLL):針對具體問題的邏輯操作; 3、資料訪問層(DAL):對資料進行操作。其他的層級基本都是在這三層之上的補充。
三層架構之抽象父類SQLHelper
剛用三層架構完成了一個‘酒店管理系統’,其實程式設計這麼久以來,發現這樣那樣的程式,不過就是和資料庫打交道,其核心就是資料庫的增加、刪除、修改、查詢。因為如此,在三層架構中,Model是這個程式的紐帶,是傳輸資料的載體,我們查詢資料,返回的是一泛型集合。修改資料,修改的是物件
在winform中使用三層架構學習總結
Winform 三層架構小例子 http://www.cnblogs.com/jacky73/archive/2009/09/01/1558083.html 在web開發中常常用到工廠模式三層架構,現在也在Winform中應用這種架構方式,嘗試了很多,也模仿了經典例子Pet
簡單區分軟體開發中幾個概念:C/S結構和B/S結構、三層結構和兩層結構、MVC和三層架構
C/S——客戶端/服務端,簡單講就是客戶端電腦上需要安裝專有的軟體來更伺服器交流,就像QQ。主要通過訊息的機制傳遞(當然也可以自己寫協議,遊戲就是這樣做的。) B/S——瀏覽器/服務端,你只要有瀏覽器就可以與伺服器進行通訊,不用再安裝專門的客戶端,通訊協議使用HTTP協議.
三層架構——三層登入類圖與序列圖
●前言 光說不會用,那就是一隻紙老虎。現在看C#版本的程式碼三遍,第一遍巨集觀瀏覽,第二遍照著敲,第三遍對應U層、B層、D層去研究每一部分的程式碼都有什麼作用,雖然現在還不是很透徹,但
ASP.NET中如何搭建三層架構
1、開啟VS2010,新建一個網站,更改名字後存放在1中的資料夾中,此時要記得給該網站建一個新的資料夾,專門用來存放該網站所有的檔案; 2、搭建資料訪問層,點選檔案——新增——新建專案——類庫,更改名字之後,存放在1中的資料夾中,同樣也為資料訪問層,建立一個新的資料夾,專門存
Entity Framework 三層架構--持久層使用封裝之實體模型
Entity Framework的橫空出世確立了其在.net領域官方ORM中的霸主地位,給我們開發者帶來了福音,但是使用使用上還是有些不便捷的地方,尤其是在三層架構的專案中,在業務層不容許出現直接操作ObjectContext 的情況下,需要針對不同實體編寫不同DAO的工作