C#教程之自己动手写映射第一节[动机]
前言撒:
最近在园子里看到不少老鸟们写一些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 }
通过上面的例子大家看到了吧,对象关系映射其实还是挺好用的,这也是为什么园子里的很多程序员都自己写着用的原因。
今天先到这里吧,关于怎么开发这样一个微型框架我们后面的教程接着捣鼓...