本例完全代码下载:
StrongTypeDataBinding.rar
六,关于web开发中的数据绑定
在web开发中,数据绑定是所有程序员都会遇到,并且经常处理的问题。下面就这个小问题和大家探讨一下关于强类型支持在这个问题中的应用。拙劣不当之处,敬请各路高手斧正。
先look一眼这个demo截图:
先简单说一下,在我们的数据库有这样一个Programmer表,它有两个字段Name和WebSite,类型都是字符串。我们要处理的问题即是,把这个表从数据库中读出来并在页面上显示。简单吧,让我们开始。
现在我们是一个新手,对.Net数据绑定一无所知,于是我到msdn2搜索了一下,找到这么一个例子:
SqlDataSource
<%@ page language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html >
<head>
<title>Repeater.DataSourceID Property Example</title>
</head>

<body>
<form id="Form1" runat="server">

<h3>Repeater.DataSourceID Property Example</h3>

<asp:repeater id="Repeater1"
datasourceid="SqlDataSource1"
runat="server">

<headertemplate>
<table border="1">
<tr>
<td><b>Product ID</b></td>
<td><b>Product Name</b></td>
</tr>
</headertemplate>

<itemtemplate>
<tr>
<td> <%# Eval("ProductID") %> </td>
<td> <%# Eval("ProductName") %> </td>
</tr>
</itemtemplate>

<footertemplate>
</table>
</footertemplate>
</asp:repeater>

<asp:sqldatasource id="SqlDataSource1"
connectionstring="<%$ ConnectionStrings:NorthWindConnection%>"
selectcommand="SELECT ProductID, ProductName FROM [Products] Where ProductID <= 10"
runat="server">
</asp:sqldatasource>

</form>
</body>
</html>
这个例子用Repeater的DataSourceID绑定SqlDataSource,简单好用,不需要写什么cs代码,把这个例子修改一下,就可以解决我们的问题。
这是第一种方法。这种方法或许只在学校课堂里存在,.Net自学者怕也不屑于学它。
第二种方法。随便在google里搜索一下,发现在页面中直接绑定数据源这种方法很幼稚,在cs中绑定数据被推荐。于是我们想了想,写了如下代码用于解决问题:
页面代码:
<
asp:Repeater
ID
="ProgrammerRepeater_2"
runat
="server"
>
<
ItemTemplate
>
<
p
>

Name:
<%
# DataBinder.Eval(Container.DataItem,"Name")
%>
<
br
/>

WebSite:
<
a
href
="<%# DataBinder.Eval(Container.DataItem,"
WebSite
")%
>
">
<%
# DataBinder.Eval(Container.DataItem,"
WebSiteName
")
%>
</
a
></
p
>
</
ItemTemplate
>
<
SeparatorTemplate
>
<
p
>
--------------------------------------------
</
p
>
</
SeparatorTemplate
>
</
asp:Repeater
>
cs代码:
public
partial
class
StrongTypeDataBinding : System.Web.UI.Page

{
private DataTable _programmerTbl;
private static object syncObj = new object();

protected void Page_Load(object sender, EventArgs e)

{
ProgrammerRepeater_2.DataSource = ProgrammerTable;
ProgrammerRepeater_2.DataBind();
}

public DataTable ProgrammerTable

{
get

{
if (null == _programmerTbl)

{
lock (syncObj)

{
if (null == _programmerTbl)

{

_programmerTbl = new DataTable();
_programmerTbl.Columns.Add("Name", typeof(string));
_programmerTbl.Columns.Add("WebSite", typeof(string));

_programmerTbl.Rows.Add("sban", "http://www.sban.com.cn/");
_programmerTbl.Rows.Add("8th pawnshop",
"http://sban.cnblogs.com/");
}
}

}

return _programmerTbl;
}
}
}
为了重点说明数据绑定,从数据库读取数据改为由代码生成数据。
这便是第二种方法,编译一下,通过,没有问题。相信98%的读者都没有发现,这个程序其实是不能正常运行的。原因在呢?
看这句:
<%
# DataBinder.Eval(Container.DataItem,
"
WebSiteName
"
)
%>
WebSiteName
这个字段其实是不存在的,但是编译器无法替我检测到这错误。这种错误,此时只能依赖于程序员的细心和严谨。但是经验告诉我们,这份依赖是不可取的。我们需要强类型支持来避免这种错误。
下面我们看第三种解决方法:
页面代码:
<
asp:Repeater
ID
="ProgrammerRepeater_1"
runat
="server"
>
<
ItemTemplate
>
<
p
>
Name:
<%
# ((Sban.Lab.Programmer)Container.DataItem).Name
%>
<
br
/>
WebSite:
<
a
href
="<%# ((Sban.Lab.Programmer)Container.DataItem).WebSite%>"
>
<%
# ((Sban.Lab.Programmer)Container.DataItem).WebSite
%>
</
a
></
p
>
</
ItemTemplate
>
<
SeparatorTemplate
>
<
p
>
--------------------------------------------
</
p
>
</
SeparatorTemplate
>
</
asp:Repeater
>
cs代码:
public
partial
class
StrongTypeDataBinding : System.Web.UI.Page
{
private
IList
<
Programmer
>
_programmerList;
private
static
object
syncObj
=
new
object
();
protected
void
Page_Load(
object
sender, EventArgs e)
{
ProgrammerRepeater_1.DataSource
=
ProgrammerList;
ProgrammerRepeater_1.DataBind();
}
public
IList
<
Programmer
>
ProgrammerList
{
get
{
if
(
null
==
_programmerList)
{
lock
(syncObj)
{
if
(
null
==
_programmerList)
{
_programmerList
=
new
List
<
Programmer
>
();
_programmerList.Add(
new
Programmer { Name
=
"
sban
"
,
WebSite
=
"
http://www.sban.com.cn/
"
});
_programmerList.Add(
new
Programmer { Name
=
"
8th pawnshop
"
,
WebSite
=
"
http://sban.cnblogs.com/
"
});
}
}
}
return
_programmerList;
}
}
}
public
class
Programmer

{
private string _name;

public string Name

{
get

{
return _name;
}
set

{
_name = value;
}
}

private string _webSite;

public string WebSite

{
get

{
return _webSite;
}
set

{
_webSite = value;
}
}

}
在数据绑定中虽然有显式类型转化,但它可以给我们提供强类型支持,是可以接受的。并且Ilist代替Table,也有更灵活的编程体验。
推荐用第三种方法绑定数据。
本例完全代码下载:
StrongTypeDataBinding.rar
sban首发
博客园2008年1月3日1:06:43
+++++
以下是我在.Net开发中总结的一些小的开发技巧,园子里的朋友大多数都是.Net程序员,相信对此都很熟悉。如果您觉得太浅显了,请不要嘲笑;如果觉得哪些地方说的欠妥,敬请斧正;如果您觉得还有什么地方没有说到位,请补充,谢谢。
这些技巧再浅显平常不过,初学者很容易忽略。相信越是越老道的程序员,越重视这些稀疏平常的技巧。
一,关于变量的命名和属性
static readonly与const的变量,作用是一样的,无论访问修饰符是不是public,还是其它(private、protected、internal),变量名称一般为大写,中间以下划线。
public
static
readonly
int
MAX_HEIGHT;
public
const
int
MIN_HEIGHT
=
10
;
有些程序员对大写不敏感,上例中,
MAX_HEIGHT用Max_Height代替也未尝不可,甚至MaxHeight也可以。在.Net类库中,int.MaxValue与int.MinValue便是这样定义的。
const常量更确切的说是编译时常量,因为它在运行时是不存在的,在编译中所有变量引用将被实际值替掉。而static readonly则不然,它在运行时也是存在的。从原理上讲,论效率const优于static readonly。但是在一个比较在的项目中,在dll局部升级时,如果改变了某个const变量的值,而未升级的dll如果也有这个const变量的话,显而易见这时候问题将是产生。如果因此而升级全部dll,反而不值。所以在大型、多变应用中,建议使用static readonly代替const。其微乎其微的效率的减损对比升级布置可能出现的问题还是可以接受的。
除了以上两种静态只读和常量变量之外,其它变量命名均以下划线开始,访问修改符为private(不建议命名为internal、protected,更不建议命名为public):
private
static
int
_maxHeight;
private
int
_minHeight;
如果其命名不前置下划线,易与参数变量混淆。
对于下面这种定义:
private
int
_minHeight = default(int);
public
int
MinHeight
{
set
{
_minHeight
=
value;
}
get
{
return
_minHeight;
}
}
初学者可能觉得有点画蛇添足,不如直接命名为:
public
int
MinHeight;
这样岂不简单,干吗还要用getter和setter封装起来,额外的函数调用也使效率有损。
有时候在开发项目时,开始时我们要画的可能只是一条蛇,但是项目后期需求变了,改画一条龙了。所以在项目初期画蛇的时候添上一对足还是很有远见的。
getter与setter(属性存取器)可以像方法一样封装逻辑并且像变量一样使用,建议所有非静态只读和常量,定义为private,然后给其添加相应属性存取器,用于赋值与读取。在其它方法体内(包涵类外与类内),不建议直接读写变量。即使它目前可以被直接读写,我们也要通过调用属性存取器也调用。这一点有点麻烦,但很重要,很高老手有时也会犯错误。如下所示:
private
int
_minHeight
=
int
.MinValue;
public
int
MinHeight//或者是protected、internal,甚至是private
{
set
{
_minHeight
=
value;
//即使这里目前没有其它处理逻辑
}
get
{
return
_minHeight;
}
}
public
void
Method1(
int
minHeight)
{
this
.MinHeight
=
minHeight;//在这里不要使用this._minHeight直接读写
//
}
即使变量的访问是受保护的或者或者是私有的,也要使用属性存取器。
原则是:对于变量的读取,要用属性存取器封装,无论其访问修饰符如何,即使其属性存取器内除了存取目前没有任何其它逻辑。
二,关于命名空间和目录划分
从命名空间的命名,目录的划分与命名可以看出一个程序员是否有经验,是否很有经验。一个编程老手绝不允许架构混乱。
.Net开发中,一般目录名与命名空间名称是对应的。关于命名空间如何划分,目录如何分类,这个问题看似简单,实际上却比较复杂,虽然它不像动植物学有一套完整的分类学。
在.Net B/S架构中,一般分为如下三个主要的命名空间:
[公司名
/
作者名].[项目名].Business
[公司名
/
作者名].[项目名].Data
[公司名
/
作者名].[项目名].Web
这三部分可以在一个project中,也可以分置三处。
目录分类与空间命名之难在于:分类因素是二维的,而分类却只是一维的。解释一下:分类是一维的,指一个词语只能代表一个分类名称的含义,无论同时表达两个含义;分类因素是二维的,指分类可以横向类别分类,也可以按纵向属性分类。
假设我正在开发一个电子商务图书网站[湛蓝书店
www.ZLBook.cn],这个商务按照常规,它有用户中心,帮助中心,支付中心,商品中心等。我的这个项目分为三个project,如下:
Sban.ZLBook.Business
Sban.ZLBook.Data
Sban.ZLBook.Web
在Sban.ZLBook.Web工程中,我下设UserCenter、HelpCenter、PayCenter、ProductCenter等目录,这样的分类便是按类别横向分类。
而在这些分类中,肯定都用到了图片,还有一些css样式文件,这些文件我放在哪里?我把它们放在Web工程的Images目录下(如果不另辟图片服务器的话)。如果文件太多,不好管理,其子目录又可以分为UserCenter、HelpCenter、PayCenter、ProductCenter等。如此,Images的目录的划分便是按纵向属性分类。
关于具体如何命名,没有什么通用的方法,要看具体项目。做的项目多了,架构才能见水平。命名空间与目录建议大写。
不知道应该如何架构的时候,不妨翻一翻官方的类库。
btw:flex工程中,包名(pakeage)与目录小写,而类名大写。
三,关于泛型集合,能用则用
用Array,ArrayList,Dictionary等存储对象集合,面临的不只是拆装箱性能损耗的问题。从系统架构角度讲,所有对象对象都应该是强类型的。为了解决这个问题,从.Net2开始,便有了泛型。看如下代码:
public
class
Mobile
{
private
ArrayList friends
=
new
ArrayList();//这里用ArrayList便不足取
public
void
Add(IFriend f)
{
friends.Add(f)
}
private
void
SayBless()
{
for
(
int
i
=
0
; i
<
friends.Count; i
++
)
{
IFriend friend
=
(IFriend )friends[i];//这里拆装时,必须知道其元素的类型是IFriend
friend.Say();
}
}
}
这一条小技巧的建议便是:使用泛型集合避免显式类型转换。如果您的代码中有显式转换,或者有as操作,可能需要重新考虑一下架构。as操作符用起来看似优雅,但若用于类型转换不用也罢。
四,用接口代替类用于参数
接口是诚实的,能做什么不能做什么一目了然,从来没有什么欺瞒。不像类,可能拥有其接口没有定义的方法或属性,而编程时则有效要避免用到这些方法和属性。在定义方法时,对于我们需要的对象参数,我们需要的只是它这个对象的功能或作用的说明,而接口洽洽就可以提供这些了。使用接口代替类用于参数,凡是实现这个接口的类都可以用作参数实例,显而易见接口拥有更大的灵活性。
对于方法的返回值,如果要求返回的对象具有某个功能,而这个功能是在接口中声明的,则只需返回接口即可。
原则是:参数的传入与传出要尽可能提高其抽象性、扩大其涵盖范围。
sban首先于博客园2007年12月25日18:49:31