看了园子里面的两篇关于数据绑定的文章(使用objectdatasource结合数据绑定控件进行简单三层架构的开发 和 使用objectdatasource结合数据绑定控件进行简单三层架构的开发 [绝版]),终于让我按捺不住想写点我的看法。
对于数据绑定的相关使用,我跟许多人一样,也有着许多的感受。
一开始先是使用VS 2003中的DataGrid+SqlConnection、SqlCommand、SqlDataAdapter,到后来使用VS 2005中的GridView+ObjectDataSource。当那些SqlConnection、SqlCommand、SqlDataAdapter都被ObjectDataSource一次性搞定,其简洁的数据绑定代码让我兴奋不已,的确帮我省去了不少的工夫。然而好景不长,在项目的实际需求开发中,逐渐发觉它的存在反而降低了代码的灵活性和重用性,程序似乎是变得“傻瓜化”了,同时把我也弄得有些“傻瓜化”了,^_^ 以至于后来忍痛割爱,但凡数据绑定控件的相关问题干脆用代码实现。
现在还来谈论GridView以及ObjectDataSource的使用,的确有点老生常谈的感觉,但我希望通过这个切入点来讨论一下我对服务器控件的使用心得,以及相关的一些看法:
一、 我个人觉得,对于服务器控件,能不用的地方就尽量不要使用。比如说,能用html的table来展现的数据,就不必要使用Repeater,而能用Repeater很好展现数据的时候就不必要使用DataList,逼不得已才使用GridView以及DetailsView等等。牛刀也能杀鸡,但更合理的做法应该是让牛刀在杀牛的时候才使用。所以,我在使用控件的时候,考虑更多的是让代码尽量简洁高效,但绝不是偷工减料。
二、 我个人觉得,即使在使用服务器控件的时候,相关的代码应该尽量在后台完成。这样做的原因,一是提高了代码的灵活性;二是提高了代码的可重用性。毕竟aspx前台页面承载的东西太多,HTML、CSS、JavaScript已经让它变得很臃肿,加之在VS 2005中,aspx前台页面是不能调试的(VS 2008中应该可以了),要是再把一些服务器控件的代码过多的堆砌上去,可想而知,它的可读性和维护性是多么让人晦涩的。
三、 我个人觉得,对于数据库访问方面的一些技术和控件,应该尽量使用权威性和大众化的。权威性的东西(我这里意指得到大多数人认同的东西)在一定程度上就表示了它的健壮性、安全性以及可行性。观众的眼睛是雪亮的,所以我更相信群众。而大众化的东西在团队编程和版本更新等等方面更容易融合。所以,在简单的项目开发中,数据库访问方面我认为一般使用SqlHelper或者企业库就能够应付;而对于复杂庞大的系统,能够用上LINQ或者NHibernate也应该足够。毕竟我们是在帮客户开发产品,客户喜欢的应该是最美的结果,而不是最复杂的技术。
四、 我个人觉得,从事编程不久(或者说内力不深)的程序员,更应该有一个务实的心态。比如我,刚来园子的时候,特别好奇于各种新鲜的技术,首页的文章惟恐一篇被我漏过。结果囫囵吞枣,似懂非懂,导致自己对技术的掌握反倒华而不实。就拿当时刚刚兴起的AJAX来说,在DotNET中其实三个控件就可以搞定。所以,一般情况下只需要掌握了它的相关原理并能灵活的使用,就足已应对大部分的情况了。如果在一个对局部刷新要求并不高的系统中,硬是给它安个AJAX框架之类的东东,那恐怕不只是画蛇添足的问题了。我这样说只是强调,我们应该花更多的时间来看一些全面深入的书籍以及系统的帮助上,以提高自己的内力。只有内功深厚了,才能以不变应万遍的面对各种接踵而来的技术革新和项目难题。
当然,这些观点都只是我的片面之词,不具有权威性和可靠的正确性,错漏之处还请给予斧正。
为了免除我在这里纸上谈兵的尴尬,特地编写了一个Demo以体现我上述的相关观点和编程习惯。下面的这种设计方式应该足够应付小型系统的开发。
它的特点是:没有使用ObjectDataSource控件,数据绑定控件的设置全部是以代码实现,还有就是三层结构的思想+改进的SqlHelper数据库操作。而优点就是我上面所提及的。
先来看看相关截图:
页面运行前的GridView控件
页面运行后的GridView控件展现
页面运行后GridView控件的编辑状态
实现步骤:
一、在aspx页面上,只放入一个GridView控件并指定其ID。
Default.aspx
<%@ 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>GridView_Demo</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:GridView ID="myGrid" runat="server" >
</asp:GridView>
</div>
</form>
</body>
</html>
二、在aspx.cs类文件中,把GridView控件的各种外观设置,数据绑定等等都用代码的形式控制,这样做的好处是对GridView能够灵活的控制,代码的重用性极高。弱点应该是代码量比较大(或许还有我看不见的)。
Default.aspx.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;
//myself
using System.Drawing;
public partial class _Default : System.Web.UI.Page
{
private Employees.Employees_BLL bll = new Employees.Employees_BLL();
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
//设置GridView外观样式
setGridViewStyle();
//创建及设置Fields字段
setFields();
//设置 GridView 数据源绑定
GridBind();
}
//以后台的方式添加 GridView 的各类事件
myGrid.RowEditing += new GridViewEditEventHandler(myGrid_RowEditing);
myGrid.RowUpdating += new GridViewUpdateEventHandler(myGrid_RowUpdating);
myGrid.RowCancelingEdit += new GridViewCancelEditEventHandler(myGrid_RowCancelingEdit);
myGrid.RowDeleting += new GridViewDeleteEventHandler(myGrid_RowDeleting);
}
方法#region 方法
//设置GridView外观样式
private void setGridViewStyle()
{
myGrid.AutoGenerateColumns = false;
//设置Row的键值组成,具有唯一性
string[] KeyNames = new string[] { "EmployeeID" };
myGrid.DataKeyNames = KeyNames;
//设置GridView属性
myGrid.AllowPaging = true; //设置分页
myGrid.AllowSorting = true; //设置排序
myGrid.Font.Size = 10; //设置字号大小
myGrid.GridLines = GridLines.Both; //设置网格线
myGrid.PageSize = 15; //分页大小
myGrid.PagerSettings.Position = PagerPosition.TopAndBottom; //分页位置
myGrid.PagerStyle.HorizontalAlign = HorizontalAlign.Center; //分页对齐
myGrid.HeaderStyle.BackColor = Color.Tan;
myGrid.RowStyle.BackColor = Color.LightGoldenrodYellow;
myGrid.AlternatingRowStyle.BackColor = Color.PaleGoldenrod;
myGrid.HeaderStyle.ForeColor = Color.Black;
myGrid.PagerStyle.BackColor = Color.Goldenrod;
myGrid.SelectedRowStyle.BackColor = Color.LightBlue; //设置选择行背景颜色
//myGrid.ShowFooter = true;
}
//创建及设置Fields字段
private void setFields()
{
//创建"编辑"命令字段
CommandField editField = new CommandField();
editField.ButtonType = ButtonType.Button;
editField.ShowEditButton = true; //显示"编辑"按钮
editField.ShowCancelButton = true; //显示"取消"按钮
editField.EditText = "编辑";
editField.UpdateText = "更新";
editField.CancelText = "取消";
editField.ControlStyle.BackColor = Color.LightPink;
editField.ItemStyle.Wrap = false;
//创建"删除"命令字段
CommandField deleteField = new CommandField();
deleteField.ButtonType = ButtonType.Button;
deleteField.ShowDeleteButton = true; //显示"删除"按钮
deleteField.DeleteText = "删除";
deleteField.ControlStyle.BackColor = Color.LightPink;
deleteField.ItemStyle.Wrap = false;
//创建数据绑定字段
BoundField employeeidField = new BoundField();
BoundField lastnameField = new BoundField();
BoundField firstnameField = new BoundField();
BoundField titleField = new BoundField();
BoundField addressField = new BoundField();
BoundField cityField = new BoundField();
employeeidField.DataField = "EmployeeID";//指定数据源字段
employeeidField.HeaderText = "员工代号"; //设置字段头名称
employeeidField.ItemStyle.Wrap = false; //设置字段不换行
employeeidField.ReadOnly = true; //只读,编辑模式不能修改
lastnameField.DataField = "LastName";
lastnameField.HeaderText = "名字";
lastnameField.ItemStyle.Wrap = false;
lastnameField.ReadOnly = true; //只读,编辑模式不能修改
firstnameField.DataField = "FirstName";
firstnameField.HeaderText = "姓氏";
firstnameField.ItemStyle.Wrap = false;
//firstnameField.ReadOnly = true;
titleField.DataField = "Title";
titleField.HeaderText = "职称";
titleField.ItemStyle.Wrap = false;
addressField.DataField = "Address";
addressField.HeaderText = "地址";
addressField.ItemStyle.Wrap = false;
cityField.DataField = "City";
cityField.HeaderText = "城市";
cityField.ItemStyle.Wrap = false;
//将字段添加到GridView
myGrid.Columns.Add(editField); //编辑
myGrid.Columns.Add(deleteField);//删除
myGrid.Columns.Add(employeeidField);
myGrid.Columns.Add(lastnameField);
myGrid.Columns.Add(firstnameField);
myGrid.Columns.Add(titleField);
myGrid.Columns.Add(addressField);
myGrid.Columns.Add(cityField);
}
//设置 GridView 数据源绑定
public void GridBind()
{
//bll = new Employees.Employees_BLL();
myGrid.DataSource = bll.GetAllList();
myGrid.DataBind();
}
#endregion 方法
// GridView 编辑操作
protected void myGrid_RowEditing(object sender, GridViewEditEventArgs e)
{
//设置编辑行的索引
myGrid.EditIndex = e.NewEditIndex;
//设置更新与取消按钮之背景颜色
myGrid.Columns[0].ControlStyle.BackColor = Color.LightSteelBlue;
myGrid.Columns[1].ControlStyle.BackColor = Color.LightSteelBlue;
myGrid.ShowFooter = true;
//设置GridView在编辑模式时,TextBox字段宽度及背景颜色
//EmployeeID字段
myGrid.Columns[2].ControlStyle.Width = 80;
myGrid.Columns[2].ControlStyle.BackColor = Color.LightBlue;
myGrid.Columns[2].FooterText = "不可编辑";
myGrid.Columns[2].FooterStyle.BackColor = Color.Red;
//LastName字段
myGrid.Columns[3].ControlStyle.Width = 80;
myGrid.Columns[3].ControlStyle.BackColor = Color.LightBlue;
myGrid.Columns[3].FooterText = "不可编辑";
myGrid.Columns[3].FooterStyle.BackColor = Color.Red;
//FirstName字段
myGrid.Columns[4].ControlStyle.Width = 80;
myGrid.Columns[4].ControlStyle.BackColor = Color.LightBlue;
myGrid.Columns[4].FooterText = "可编辑";
myGrid.Columns[4].FooterStyle.BackColor = Color.Red;
//Title字段
myGrid.Columns[5].ControlStyle.Width = 100;
myGrid.Columns[5].ControlStyle.BackColor = Color.LightPink;
myGrid.Columns[5].FooterText = "可编辑";
myGrid.Columns[5].FooterStyle.BackColor = Color.Red;
//Address字段
myGrid.Columns[6].ControlStyle.Width = 120;
myGrid.Columns[6].ControlStyle.BackColor = Color.LightPink;
myGrid.Columns[6].FooterText = "可编辑";
myGrid.Columns[6].FooterStyle.BackColor = Color.Red;
//City字段
myGrid.Columns[7].ControlStyle.Width = 80;
myGrid.Columns[7].ControlStyle.BackColor = Color.LightGreen;
myGrid.Columns[7].FooterText = "可编辑";
myGrid.Columns[7].FooterStyle.BackColor = Color.Red;
GridBind();
}
// GridView 更新操作
protected void myGrid_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
if (e.NewValues != e.OldValues)
{
Employees.Employees_Model model = new Employees.Employees_Model();
model.EmployeeID = Convert.ToInt32(myGrid.Rows[e.RowIndex].Cells[2].Text.Trim());
//Employees_DAL 中的 where 条件为 EmployeeID and LastName, 所以 LastName 不能更改
model.LastName = myGrid.Rows[e.RowIndex].Cells[3].Text.ToString();
model.FirstName = ((TextBox)myGrid.Rows[e.RowIndex].Cells[4].Controls[0]).Text.ToString();
model.Title = ((TextBox)myGrid.Rows[e.RowIndex].Cells[5].Controls[0]).Text.ToString();
model.Address = ((TextBox)myGrid.Rows[e.RowIndex].Cells[6].Controls[0]).Text.ToString();
model.City = ((TextBox)myGrid.Rows[e.RowIndex].Cells[7].Controls[0]).Text.ToString();
//Employees.BLL.Employees_Model bll = new Employees.BLL.Employees_Model();
bll.Update(model);
//取消编辑时隐藏Footer
myGrid.ShowFooter = false;
//设置"编辑"和"删除"按钮还原为系统定义的颜色
myGrid.Columns[0].ControlStyle.BackColor = Color.LightPink;
myGrid.Columns[1].ControlStyle.BackColor = Color.LightPink;
myGrid.EditIndex = -1;
GridBind();
}
}
// GridView 取消事件
protected void myGrid_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
{
//取消编辑时隐藏Footer
myGrid.ShowFooter = false;
//设置"编辑"和"删除"按钮还原为系统定义的颜色
myGrid.Columns[0].ControlStyle.BackColor = Color.LightPink;
myGrid.Columns[1].ControlStyle.BackColor = Color.LightPink;
myGrid.EditIndex = -1; //取消编辑状态
GridBind();
}
// GridView 删除操作
protected void myGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
{
//设置更新与取消按钮之背景颜色
myGrid.Columns[0].ControlStyle.BackColor = Color.LightSteelBlue;
myGrid.Columns[1].ControlStyle.BackColor = Color.LightSteelBlue;
// 获取 Employees_DAL 中的 where 条件: EmployeeID and LastName
int strEmployeeID = Convert.ToInt32(myGrid.DataKeys[e.RowIndex].Values[0]);
string strLastName = myGrid.Rows[e.RowIndex].Cells[3].Text.ToString();
bll.Delete(strEmployeeID, strLastName); //删除
GridBind();
}
}
三、这是业务逻辑层,如果代码量不大,可以整合到aspx.cs类文件中。
Employees_BLL.cs
using System;
using System.Data;
namespace Employees
{
/**//// <summary>
/// 业务逻辑类Employees_BLL 的摘要说明。
/// </summary>
public class Employees_BLL
{
private readonly Employees.Employees_DAL dal=new Employees.Employees_DAL();
public Employees_BLL()
{}
成员方法#region 成员方法
/**//// <summary>
/// 是否存在该记录
/// </summary>
public bool Exists(int EmployeeID,string LastName)
{
return dal.Exists(EmployeeID,LastName);
}
/**//// <summary>
/// 增加一条数据
/// </summary>
public int Add(Employees.Employees_Model model)
{
return dal.Add(model);
}
/**//// <summary>
/// 更新一条数据
/// </summary>
public void Update(Employees.Employees_Model model)
{
dal.Update(model);
}
/**//// <summary>
/// 删除一条数据
/// </summary>
public void Delete(int EmployeeID,string LastName)
{
dal.Delete(EmployeeID,LastName);
}
/**//// <summary>
/// 得到一个对象实体
/// </summary>
public Employees.Employees_Model GetModel(int EmployeeID,string LastName)
{
return dal.GetModel(EmployeeID,LastName);
}
/**//// <summary>
/// 获得数据列表
/// </summary>
public DataSet GetList(string strWhere)
{
return dal.GetList(strWhere);
}
/**//// <summary>
/// 获得数据列表
/// </summary>
public DataSet GetAllList()
{
return GetList("");
}
/**//// <summary>
/// 获得数据列表
/// </summary>
//public DataSet GetList(int PageSize,int PageIndex,string strWhere)
//{
//return dal.GetList(PageSize,PageIndex,strWhere);
//}
#endregion 成员方法
}
}
四、数据访问层,这个没有什么好说的。
Employees_DAL.cs
using System;
using System.Data;
using System.Text;
using System.Data.SqlClient;
namespace Employees
{
/**//// <summary>
/// 数据访问类Employees_DAL
/// </summary>
public class Employees_DAL
{
public Employees_DAL()
{}
成员方法#region 成员方法
/**//// <summary>
/// 是否存在该记录
/// </summary>
public bool Exists(int EmployeeID,string LastName)
{
StringBuilder strSql=new StringBuilder();
strSql.Append("select count(1) from Employees");
strSql.Append(" where EmployeeID="+EmployeeID+" and LastName='"+LastName+"' ");
return DbHelperSQL.Exists(strSql.ToString());
}
/**//// <summary>
/// 增加一条数据
/// </summary>
public int Add(Employees.Employees_Model model)
{
StringBuilder strSql=new StringBuilder();
strSql.Append("insert into Employees(");
strSql.Append("LastName,FirstName,Title,Address,City");
strSql.Append(")");
strSql.Append(" values (");
strSql.Append("'"+model.LastName+"',");
strSql.Append("'"+model.FirstName+"',");
strSql.Append("'"+model.Title+"',");
strSql.Append("'"+model.Address+"',");
strSql.Append("'"+model.City+"'");
strSql.Append(")");
strSql.Append(";select @@IDENTITY");
object obj = DbHelperSQL.GetSingle(strSql.ToString());
if (obj == null)
{
return 1;
}
else
{
return Convert.ToInt32(obj);
}
}
/**//// <summary>
/// 更新一条数据
/// </summary>
public void Update(Employees.Employees_Model model)
{
StringBuilder strSql=new StringBuilder();
strSql.Append("update Employees set ");
strSql.Append("FirstName='"+model.FirstName+"',");
strSql.Append("Title='"+model.Title+"',");
strSql.Append("Address='"+model.Address+"',");
strSql.Append("City='"+model.City+"'");
strSql.Append(" where EmployeeID="+ model.EmployeeID+" and LastName='"+ model.LastName+"' ");
DbHelperSQL.ExecuteSql(strSql.ToString());
}
/**//// <summary>
/// 删除一条数据
/// </summary>
public void Delete(int EmployeeID,string LastName)
{
StringBuilder strSql=new StringBuilder();
strSql.Append("delete Employees ");
strSql.Append(" where EmployeeID="+EmployeeID+" and LastName='"+LastName+"' " );
DbHelperSQL.ExecuteSql(strSql.ToString());
}
/**//// <summary>
/// 得到一个对象实体
/// </summary>
public Employees.Employees_Model GetModel(int EmployeeID,string LastName)
{
StringBuilder strSql=new StringBuilder();
strSql.Append("select ");
strSql.Append(" EmployeeID,LastName,FirstName,Title,Address,City ");
strSql.Append(" from Employees ");
strSql.Append(" where EmployeeID="+EmployeeID+" and LastName='"+LastName+"' " );
Employees.Employees_Model model=new Employees.Employees_Model();
DataSet ds=DbHelperSQL.Query(strSql.ToString());
if(ds.Tables[0].Rows.Count>0)
{
if(ds.Tables[0].Rows[0]["EmployeeID"].ToString()!="")
{
model.EmployeeID=int.Parse(ds.Tables[0].Rows[0]["EmployeeID"].ToString());
}
model.LastName=ds.Tables[0].Rows[0]["LastName"].ToString();
model.FirstName=ds.Tables[0].Rows[0]["FirstName"].ToString();
model.Title=ds.Tables[0].Rows[0]["Title"].ToString();
model.Address=ds.Tables[0].Rows[0]["Address"].ToString();
model.City=ds.Tables[0].Rows[0]["City"].ToString();
return model;
}
else
{
return null;
}
}
/**//// <summary>
/// 获得数据列表
/// </summary>
public DataSet GetList(string strWhere)
{
StringBuilder strSql=new StringBuilder();
strSql.Append("select EmployeeID,LastName,FirstName,Title,Address,City ");
strSql.Append(" FROM Employees ");
if(strWhere.Trim()!="")
{
strSql.Append(" where "+strWhere);
}
return DbHelperSQL.Query(strSql.ToString());
}
#endregion 成员方法
}
}
五、数据实体类,关于这个我不得不提及请指点--关于数据访问层(2)这篇文章中作者的第一个问题,我个人觉得实体类的属性尽量保持跟数据库表的字段保持一致,如果项目实际需要,也可以在其中既包括外键ID也包含外键ID对应的名称。
Employees_Model.cs
using System;
namespace Employees
{
/**//// <summary>
/// 实体类Employees_Model
/// </summary>
public class Employees_Model
{
public Employees_Model()
{}
Model#region Model
private int _employeeid;
private string _lastname;
private string _firstname;
private string _title;
private string _address;
private string _city;
/**//// <summary>
/// EmployeeID
/// </summary>
public int EmployeeID
{
set{ _employeeid=value;}
get{return _employeeid;}
}
/**//// <summary>
/// LastName
/// </summary>
public string LastName
{
set{ _lastname=value;}
get{return _lastname;}
}
/**//// <summary>
/// FirstName
/// </summary>
public string FirstName
{
set{ _firstname=value;}
get{return _firstname;}
}
/**//// <summary>
/// Title
/// </summary>
public string Title
{
set{ _title=value;}
get{return _title;}
}
/**//// <summary>
/// Address
/// </summary>
public string Address
{
set{ _address=value;}
get{return _address;}
}
/**//// <summary>
/// City
/// </summary>
public string City
{
set{ _city=value;}
get{return _city;}
}
#endregion Model
}
}
六、扩充了相关功能的SqlHelper
DbHelperSQL.cs
Demo下载:Employees_Demo.rar
说明:由于SQL 2000中,默认Northwind数据库的Employees表的EmployeeID字段存在约束和主外键关系,所以删除记录时会报错。在这里,我简单的创建了一个myNorthwind数据库,并添加了一个Employees表(在App_Data文件夹中)。