朋友炒股兩個月賺了10萬,我幫他推廣一下公眾號,把錢用來投資總比放銀行連通貨膨脹都跑不過里強, 硬核離職,在家炒股 ,這是他每天的日志,有些經驗是花錢也買不到的。
前言撒:
最近在園子里看到不少老鳥們寫一些orm相關的文章。。。做為菜鳥的我有感而發,因為本人也一直在完善自己的om框架做為平時的娛樂。
所謂“授人以魚,不如授之以漁”,當我看到老鳥們寫的文章時,大部份只寫了部分核心代碼和使用方法並且開源。站在使用價值的角度來說,確實實用代碼量少,重用性高,操作簡單啦等等.....可是站在學習的角度來看,用咱們專業的詞語“抽象”來描述也不為過。初學者在看代碼的時候在思想的層面上很難理解。我腳得吧,學習進步的一個重要體現首先是思想上進步了,然后才能達到技術上的進步。有想法然后去實現,你說對不。
對於“C#教程之自己動手寫映射”這個分類,為什么沒有叫自己動手寫orm,首先是基於對orm的理解:對象關系映射,在我自己寫的框架中只體現了object與table的一對一映射,至於對象與對象的關系[繼承、組合、聚合等],在我自己寫的框架里並沒有體現出來,我寫這個小東西的目地就是為了解決底層重復開發的問題,如:對於簡單的增,刪除改查 ,分頁等,我們每個項目的每個表幾乎都會用到,對於這樣重復的工作,我們會盡量想辦法減少我們的工作量,這就是我寫這個小東西的初忠。這個系列的文章的意義並不在於應用,更多描述的是一個開發的過程和一種思想的演變過程。
作為一個博客園的一員,“代碼改變世界”的苦逼程序員,我希望通過該系列文章能夠讓和我一樣的菜鳥童鞋們共同進步 ,少走彎路,把學習的曲線拉的更平,同時希望園子里們的大牛啦,大大牛啦,老鳥啦,老老鳥啦,多多給予批評指正。
注:對於解決業務上的復雜關系我還沒想過往底層整,如果你覺得不完善的話可以在我的框架基礎上接着寫自己的東西出來,本框架在系列教程的最后開源。
正文撒:
同樣是寫一個添加新員工和獲取新員工分頁列表的程序,我們大概可能經歷以下幾個階段的寫法。
有圖有真相:
一、初識.net,相信大家初學.net 的時候都是這樣寫代碼的:
頁面:
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head runat="server"> 4 <title></title> 5 </head> 6 <body> 7 <form id="form1" runat="server"> 8 ---------------------添加員工---------------------<br /> 9 員工姓名:<asp:TextBox ID="Name" runat="server"></asp:TextBox><br /> 10 登陸密碼:<asp:TextBox ID="Password" runat="server" TextMode="Password"></asp:TextBox><br /> 11 部門:<asp:TextBox ID="Department" runat="server"></asp:TextBox><br /> 12 職位:<asp:TextBox ID="Position" runat="server"></asp:TextBox><br /> 13 <asp:Button ID="Submit" runat="server" Text="確定" OnClick="btnSubmit_Click" /> 14 <asp:Button ID="Button1" runat="server" Text="取消" OnClick="Button1_Click" /><br /> 15 <br /> 16 <br /> 17 ---------------------員工列表---------------------<br /> 18 <asp:GridView ID="GridView1" runat="server" AllowPaging="True" PageSize="2" CellPadding="4" 19 ForeColor="#333333" GridLines="None" 20 OnPageIndexChanging="GridView1_PageIndexChanging" AutoGenerateColumns="False"> 21 <AlternatingRowStyle BackColor="White" /> 22 <EditRowStyle BackColor="#2461BF" /> 23 <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" /> 24 <Columns> 25 <asp:BoundField DataField="ID" HeaderText="編號" /> 26 <asp:BoundField DataField="Name" HeaderText="員工姓名" /> 27 <asp:BoundField DataField="Password" HeaderText="密碼" /> 28 <asp:BoundField DataField="Department" HeaderText="部門" /> 29 <asp:BoundField DataField="Position" HeaderText="職位" /> 30 </Columns> 31 <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" /> 32 <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" /> 33 <RowStyle BackColor="#EFF3FB" /> 34 <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" /> 35 <SortedAscendingCellStyle BackColor="#F5F7FB" /> 36 <SortedAscendingHeaderStyle BackColor="#6D95E1" /> 37 <SortedDescendingCellStyle BackColor="#E9EBEF" /> 38 <SortedDescendingHeaderStyle BackColor="#4870BE" /> 39 </asp:GridView> 40 </form> 41 </body> 42 </html>
處理程序:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.UI; 6 using System.Web.UI.WebControls; 7 using System.Data; 8 using System.Data.SqlClient; 9 10 namespace CSharp.WebSite.Begin 11 { 12 public partial class Index : System.Web.UI.Page 13 { 14 protected void Page_Load(object sender, EventArgs e) 15 { 16 if (!Page.IsPostBack) 17 { 18 InitPage(); 19 } 20 } 21 22 public void InitPage() 23 { 24 SqlConnection conn = new SqlConnection(); 25 conn.ConnectionString = "Data Source=192.168.1.8;Initial Catalog=Test;User ID=sa;Password=123!@#abc"; 26 string select = "select * from employee"; 27 SqlDataAdapter ad = new SqlDataAdapter(select, conn); 28 DataSet ds = new DataSet(); 29 ad.Fill(ds); 30 GridView1.DataSource = ds; 31 GridView1.DataBind(); 32 } 33 34 protected void btnSubmit_Click(object sender, EventArgs e) 35 { 36 string name = this.Name.Text; 37 string password = this.Password.Text; 38 string department = this.Department.Text; 39 string position = this.Position.Text; 40 SqlConnection conn = new SqlConnection(); 41 conn.ConnectionString = "Data Source=192.168.1.8;Initial Catalog=Test;User ID=sa;Password=123!@#abc"; 42 string insert = "insert into Employee (Name,Password,Department,Position) values ('" + name + "','" + password + "','" + department + "','" + position + "')"; 43 SqlCommand cmd = new SqlCommand(insert, conn); 44 conn.Open(); 45 cmd.ExecuteNonQuery(); 46 conn.Close(); 47 Response.Write("<script>alert('成功')</script>"); 48 } 49 50 protected void Button1_Click(object sender, EventArgs e) 51 { 52 this.Name.Text = ""; 53 this.Password.Text = ""; 54 this.Department.Text = ""; 55 this.Position.Text = ""; 56 } 57 58 protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e) 59 { 60 GridView1.PageIndex = e.NewPageIndex; 61 InitPage(); 62 } 63 } 64 }
就這樣,我們用了一個很牛逼Gridview和一個很牛逼的CodeBehind,實現了所有的東西....顯示、業務、數據操作......等..
二、自己封裝了SQLHelper和使用了開源程序集Aspnetpager。
我們就這樣寫了一段時間后,發現應該做點什么,然后就自己動手寫了個sql幫助類用來處理底層與數據庫的常用操作,實現了幾個基本的方法GetDataSet、ExecuteNonQuery等等。接觸了AspnetPager控件,並針對這個控件在網上淘了分頁的存儲過程,再次針對我們常用的分頁控件的底層進行了封裝,這樣我們每次寫代碼的時候就直接調用方法即可了。
依然上圖:
那個分頁就是沒有加任何樣式的Aspnetpager控件。
代碼如下[前台]:
1 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="CSharp.WebSite.AboutOneYear.Index" %> 2 3 <%@ Register Assembly="AspNetPager" Namespace="Wuqi.Webdiyer" TagPrefix="webdiyer" %> 4 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 5 <html xmlns="http://www.w3.org/1999/xhtml"> 6 <head id="Head1" runat="server"> 7 <title></title> 8 </head> 9 <body> 10 <form id="form1" runat="server"> 11 ---------------------添加員工---------------------<br /> 12 員工姓名:<asp:TextBox ID="Name" runat="server"></asp:TextBox><br /> 13 登陸密碼:<asp:TextBox ID="Password" runat="server" TextMode="Password"></asp:TextBox><br /> 14 部門:<asp:TextBox ID="Department" runat="server"></asp:TextBox><br /> 15 職位:<asp:TextBox ID="Position" runat="server"></asp:TextBox><br /> 16 <asp:Button ID="Submit" runat="server" Text="確定" OnClick="btnSubmit_Click" /> 17 <asp:Button ID="Button1" runat="server" Text="取消" OnClick="Button1_Click" /><br /> 18 <br /> 19 <br /> 20 ---------------------員工列表---------------------<br /> 21 <asp:Repeater ID="repList" runat="server"> 22 <ItemTemplate> 23 <div style="width:300px;"> 24 <span> 25 <%#Eval("ID")%></span> 26 <span> 27 <%#Eval("Name")%></span> 28 <span> 29 <%#Eval("Password")%></span> 30 <span> 31 <%#Eval("Department")%></span> 32 <span> 33 <%#Eval("Position")%></span> 34 </div> 35 </ItemTemplate> 36 </asp:Repeater> 37 <br /> 38 <webdiyer:AspNetPager ID="Pager" CssClass="Pager" runat="server" AlwaysShow="True" 39 FirstPageText="首頁" InvalidPageIndexErrorMessage="請輸入數字頁碼!" LastPageText="末頁" 40 NextPageText="下一頁" PageIndexOutOfRangeErrorMessage="頁碼超出范圍!" PrevPageText="上一頁" 41 ShowNavigationToolTip="True" SubmitButtonText="確定" CenterCurrentPageButton="True" 42 PageIndexBoxType="TextBox" PageSize="2" ShowPageIndexBox="never" TextAfterPageIndexBox=" 頁 " 43 TextBeforePageIndexBox="轉到 " HorizontalAlign="NotSet" CurrentPageButtonClass="" 44 OnPageChanged="Pager_PageChanged"> 45 </webdiyer:AspNetPager> 46 </form> 47 </body> 48 </html>
處理程序:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.UI; 6 using System.Web.UI.WebControls; 7 using System.Data; 8 9 namespace CSharp.WebSite.AboutOneYear 10 { 11 public partial class Index : System.Web.UI.Page 12 { 13 public readonly static string conn = "Data Source=192.168.1.8;Initial Catalog=Test;User ID=sa;Password=123!@#abc"; 14 15 protected void Page_Load(object sender, EventArgs e) 16 { 17 if (!Page.IsPostBack) 18 { 19 GetList(); 20 } 21 } 22 23 protected void btnSubmit_Click(object sender, EventArgs e) 24 { 25 string name = this.Name.Text; 26 string password = this.Password.Text; 27 string department = this.Department.Text; 28 string position = this.Position.Text; 29 30 31 string insert = "insert into Employee (Name,Password,Department,Position) values ('" + name + "','" + password + "','" + department + "','" + position + "')"; 32 SqlHelper.ExecuteNonQuery(conn, CommandType.Text, insert); 33 Response.Write("<script>alert('成功')</script>"); 34 } 35 36 /// <summary> 37 /// 獲取列表 38 /// </summary> 39 protected void GetList() 40 { 41 int TotalCount; 42 repList.DataSource = SqlCommon.GetList(conn, "ID", this.Pager.PageSize, Pager.PageSize * (Pager.CurrentPageIndex - 1), "Employee", "1=1", out TotalCount); 43 repList.DataBind(); 44 Pager.RecordCount = TotalCount; 45 } 46 47 48 protected void Button1_Click(object sender, EventArgs e) 49 { 50 this.Name.Text = ""; 51 this.Password.Text = ""; 52 this.Department.Text = ""; 53 this.Position.Text = ""; 54 } 55 56 #region 分頁 57 /// <summary> 58 /// 分頁 59 /// </summary> 60 /// <param name="sender"></param> 61 /// <param name="e"></param> 62 protected void Pager_PageChanged(object sender, EventArgs e) 63 { 64 GetList(); 65 } 66 #endregion 67 68 } 69 }
三、又經過了一個段時間的學習后,我們接觸了大名頂頂的“三層架構”,知道了以前的寫法有些弊端,知道怎么把表
示層(UI)-->業務邏輯層(BLL)-->數據操作層(DAL)分離開,這時我們可能還在用SQLHelper和AspnetPager控件..
代碼如下[CodeBehind]:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.UI; 6 using System.Web.UI.WebControls; 7 8 namespace CSharp.WebSite.AboutTwoYear 9 { 10 public partial class Index : System.Web.UI.Page 11 { 12 13 protected void Page_Load(object sender, EventArgs e) 14 { 15 if (!Page.IsPostBack) 16 { 17 GetList(); 18 } 19 } 20 21 protected void btnSubmit_Click(object sender, EventArgs e) 22 { 23 Model.Employee employee = new Model.Employee(); 24 employee.Name = this.Name.Text; 25 employee.Password = this.Password.Text; 26 employee.Department = this.Department.Text; 27 employee.Position = this.Position.Text; 28 if (BLL.Employee.Insert(employee)) 29 Response.Write("<script>alert('成功')</script>"); 30 else 31 Response.Write("<script>alert('失敗')</script>"); 32 33 } 34 35 /// <summary> 36 /// 獲取列表 37 /// </summary> 38 protected void GetList() 39 { 40 int TotalCount; 41 repList.DataSource = BLL.Employee.GetList(Pager.PageSize, Pager.CurrentPageIndex, out TotalCount); 42 repList.DataBind(); 43 Pager.RecordCount = TotalCount; 44 } 45 46 47 protected void Button1_Click(object sender, EventArgs e) 48 { 49 this.Name.Text = ""; 50 this.Password.Text = ""; 51 this.Department.Text = ""; 52 this.Position.Text = ""; 53 } 54 55 #region 分頁 56 /// <summary> 57 /// 分頁 58 /// </summary> 59 /// <param name="sender"></param> 60 /// <param name="e"></param> 61 protected void Pager_PageChanged(object sender, EventArgs e) 62 { 63 GetList(); 64 } 65 #endregion 66 67 } 68 }
BLL:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace BLL 7 { 8 public class Employee 9 { 10 public static bool Insert(Model.Employee employee) 11 { 12 return DAL.Employee.Insert(employee); 13 } 14 15 public static List<Model.Employee> GetList(int PageSize, int CurrentPageIndex, out int TotalCount) 16 { 17 return DAL.Employee.GetList(PageSize, CurrentPageIndex, out TotalCount); 18 } 19 } 20 }
DAL:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Data; 6 7 8 namespace DAL 9 { 10 public class Employee 11 { 12 public readonly static string conn = "Data Source=192.168.1.8;Initial Catalog=Test;Employee ID=sa;Password=123!@#abc"; 13 14 public static bool Insert(Model.Employee employee) 15 { 16 string insert = "insert into Employee (Name,Password,Department,Position) values ('" + employee.Name + "','" + employee.Password + "','" + employee.Department + "','" + employee.Position + "')"; 17 if (Convert.ToInt32(SqlHelper.ExecuteNonQuery(conn, CommandType.Text, insert)) > 0) 18 return true; 19 else 20 return false; 21 } 22 23 public static List<Model.Employee> GetList(int PageSize, int CurrentPageIndex, out int TotalCount) 24 { 25 List<Model.Employee> listEmployee = new List<Model.Employee>(); 26 DataSet ds = SqlCommon.GetList(conn, "ID", PageSize, PageSize * (CurrentPageIndex - 1), "Employee", "1=1", out TotalCount); 27 if (ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0) 28 { 29 foreach (DataRow dr in ds.Tables[0].Rows) 30 { 31 Model.Employee employee = new Model.Employee(); 32 employee.ID = Convert.ToInt32(dr["ID"]); 33 employee.Name = dr["Name"].ToString(); 34 employee.Password = dr["Password"].ToString(); 35 employee.Department = dr["Department"].ToString(); 36 employee.Position = dr["Position"].ToString(); 37 listEmployee.Add(employee); 38 } 39 } 40 return listEmployee; 41 } 42 43 } 44 }
四、又經過一段時間的學習,我們觸到了PetShop教學實例,接觸了linq、nhibernate、企業庫,泥馬,這就是一條不歸路啊,搞設計模式,基於框架開發,研究框架的思想等....
代碼略...
五、在經過了一痛徹心菲的學習以后,我們總結經驗,准備動手寫一些自己的東西,把一些基本的功能封裝起來,對外提供一致的接口去調用...好吧,我們“搞個對象”關系映射,解決一些一直重復寫的 insert select update delete 等問題....
代碼如下:
DAL:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using HZYT.DBUtility; 6 7 namespace DAL 8 { 9 public class EmployeeORM 10 { 11 public static bool Add(Model.Employee employee) 12 { 13 return ORM.Add(employee, Constant.ASSEMBLYPATH, Constant.CONNSTRING); 14 } 15 16 public static List<Model.Employee> GetList(int PageSize, int CurrentCount, out int TotalCount) 17 { 18 List<Model.Employee> returnList = new List<Model.Employee>(); 19 List<object> employeeList = ORM.GetList(new Model.Employee(), PageSize, CurrentCount, out TotalCount, Constant.ASSEMBLYPATH, Constant.CONNSTRING); 20 foreach (object tempobject in employeeList) 21 { 22 returnList.Add((Model.Employee)tempobject); 23 } 24 return returnList; 25 } 26 } 27 }
在上面的例子中,我們沒有看到一個sql語句【當然sql肯定會用到的】,我們只要把Employee:new 一個,然后交給orm類就可以了,下面所有的工作都由:orm來處理啦。
以下是orm的幾個常用的接口:
1 public class ORM 2 { 3 public ORM(); 4 5 public static bool Add(object classObject, string AssemblyName, string ConnString); 6 public static bool Add(object classObject, out int intMaxID, string AssemblyName, string ConnString); 7 public static object Get(object classObject, string AssemblyName, string ConnString); 8 public static object Get(object classObject, string strWHERE, string AssemblyName, string ConnString); 9 public static List<object> GetList(object classObject, string AssemblyName, string ConnString); 10 public static List<object> GetList(object classObject, string strWHERE, string AssemblyName, string ConnString); 11 public static List<object> GetList(object classObject, int intPageSize, int intCurrentCount, out int intTotalCount, string AssemblyName, string ConnString); 12 public static List<object> GetList(object classObject, int intPageSize, int intCurrentCount, string strWhere, out int intTotalCount, string AssemblyName, string ConnString); 13 public static bool Remove(List<object> listClassObject, string AssemblyName, string ConnString); 14 public static bool Remove(object classObject, string AssemblyName, string ConnString); 15 public static bool Save(object classObject, string AssemblyName, string ConnString); 16 }
通過上面的例子大家看到了吧,對象關系映射其實還是挺好用的,這也是為什么園子里的很多程序員都自己寫着用的原因。
今天先到這里吧,關於怎么開發這樣一個微型框架我們后面的教程接着搗鼓...