我从02年开始了编程的工作,开始接触一些简单的网站,下半年写了个小的自助建站程序(asp和asp.net),比较简陋没有使用。03年开始正式做网站(asp)。03年下半年开始改版三好在线(​​www.sanhaoonline.com​​ asp.net ),03年底、04年初才知道了三层架构的相关东东,一开始是很不了解的,所以呢根本就没有理睬,依旧按照我自己的思路写网站。


        后来有点痫暇时间才开始看了看三层方面的东东,不过很遗憾至今为止我依然没有看懂petshop和duwish的代码(也许是我没有花费太多的时间去看吧)。三层给我的总体感觉就是两个字 —— 麻烦;四个字 —— 浪费代码。感觉自己的东东很简洁。因为一直都是我一个人写网站,没有人帮我分担其它层的编写;而我又很懒,一个人写三个层的代码 —— 太累。


        随着时间的推移,逐渐验证了我自己的想法。不能说是绝对的好,但是也能够达到方便、简洁、可扩展、稳定性好的程度了。一直都想写点东西出来,但是由于种种原因阻挠,一直没有实现愿望。


        近两天三层又被提起来了,看来我也该写点东东了。


        在继续说明之前,先缩小一下范围(程序员的思路都是很广的,很容易联想到其它的方面):


        目前我想讨论的只是asp.net,而且只是网站,再缩小一点就是那种很简单的网站。(其他的情况暂且不考虑,当然以后会加以说明的!请不要心急。当然您不要认为我根本没有考虑到复杂的情况。)


下面简单说一下我的思路。其实很简单了,就是两个字 —— 通用!


1、 数据访问层是通用的。(和三层里的数据访问层的概念是有区别的,请不要较真。)

2、 实体类也是通用的。

3、 常用的功能(比如分页、联动下拉列表等)都写成了控件,自然也就是通用的了。


先说一下数据访问层。


        我的印象:三层架构里的数据访问层并不是通用的,其实我现在也没用完全弄明白三层架构里数据访问层到底要写些什么东东,感觉是在重复的写着


SqlConnection cn = new SqlConnection(…);

SqlCommand cm = new SqlCommand

SqlDataAdapter da = new SqlDataAdapter(cm);

da.Fill(…);


这些代码。不知道我的理解是不是正确。


        可能你会说:你不知道SqlHelp吗?

        我知道SqlHelp,但是知道的时候已经太晚了。在我知道他之前,我已经在用我自己写的“数据访问层”了(那时的还没有现在的这么强大,所以加了引号)。而且我感觉自己写的要比 SqlHelp 好用得多。当然我也参考了一些 SqlHelp 和其它的类似的程序,以便吸取精华,来不断地完善自己的数据访问层。


我的数据访问层的功能:


        简单的说就是对ado.net的进一步的封装 —— 简化功能的封装。ado.net是通用的,所以我的数据访问层自然也就是通用的了。



        想想我们需要的是什么?执行sql语句(比如添加、修改、删除)、返回记录集(存放在DataSet等对对象里面)。我的数据访问层也就是围绕这两个功能而展开。


直接使用 ado.net 的话要写好几行代码才能得到一个DataSet 。(就像我上面写的那个例子)。


而使用我的数据访问层的话就不那么多的代码了,基本上一行就可以了。


需要传入的参数:sql语句、存储过程的名称(存储过程的参数)。


根据实际情况判断具体传入什么参数,以及使用那些函数。(这些都是调用数据访问层的地方的事情。)


        返回类型:返回 void 、DataSet 、DataTable、DataRow、string[]、string,最近又增加了返回结构数组的功能,结构数组也就是“实体类”。


简单的说我的数据访问层就是这些接口。


下面举例说明我的数据访问层的使用方法:


比如我想在网站的首页里调用最后添加的五条资讯,然后绑定到Repeater控件上。


我可以这样写


JYK.DataAccessLayer dal = new JYK.DataAccessLayer();          //实例化数据访问层

Rpt_News.DataSource = dal.RunSqlDataTable (“select top 5 字段 from 表名 where 条件 排序等”);        //获得记录集

Rpt_News.DataBind();


dal. Dispose() ;//释放资源。


        短短几行就实现了功能,除了前台Repeater 里面需要再写点代码之外,其他的地方就不用再写任何的代码了。我感觉我的方法是相当的简介,你的感觉呢?


(当然首页里面不会只显示资讯就完事了,其他的也是类似的写法。也可以用

DataSet ds = dal.RunSqlDataSet(sql语句)

或者

DataSet ds = dal.RunStoreDataSet(存储过程名称)


返回多个记录集。然后用 ds.Table[0]、ds.Table[1]…来绑定控件。)




数据访问层先说到这里,下面说说实体类


        一开始我是不用实体类的,因为感觉需要一个表对应一个实体类,如果是这样的话就太麻烦了。取代的是直接使用DataTable ,和数据访问层搭配用起来也是很简单的。


        后来发现 <%# DataBinder.Eval(Container, "DataItem.txt")%> 的绑定方法实在是效率太低了,后弃之不用,改成了 <%#((DataRowView)Container.DataItem)["Url"]%>。但是总感觉不爽。突然想起来以前有位高人提及自定义数据源绑定控件的方法。记忆已经很是模糊了,隐约感觉是使用了结构之类的东东。经过一段时间的摸索和实验,确定了自己的一种“实体类” —— 更准确的说就是结构(struct)数组。


//插叙

        让我们先来看看网页里的“元素”。想想上面那个显示资讯的例子,在页面里面(UI层)我们需要获取什么呢?链接网址、资讯标题、发表时间、人气、资讯图片的网址、资讯的简要介绍。(还有其它的吗?)


        根据我的编写网站的经验,这些已经足够了。也就是说结构(struct)里面定义这些属性就足够一般的页面(首页和列表页面)使用了。当然在实际中我又加了一个ID的属性。

//插叙结束


        通用的实体类,也就是说不管是资讯还是文件下载还是其它的什么,都用具有这些属性的结构数组来保存记录集。再在数据访问层里面增加一个函数来返回结构数组(就像返回DataTable 那样的函数)。前台调用也是很方便,不仅可以绑定到控件,而且可以直接使用 for 循环来显示数据(仿佛回到了asp的时代J)。因为for更加的灵活,是控件所无法比拟的!而且可以很轻松的应对美工给的不好循环的页面。


绑定控件的写法

<%# ((BaseTitle)Container.DataItem).URL%>

<%# ((BaseTitle)Container.DataItem).Title%>

<%# ((BaseTitle)Container.DataItem).ImagePath%>

<%# ((BaseTitle)Container.DataItem).Hits%>

……


for的写法。(后台定义 myData,并使用我的数据访问层填充数据)

<%= myData[i].URL%>

<%= myData[i]..Title%>

<%= myData[i]..ImagePath%>

<%= myData[i]..Hits%>


相同的写法,不用考虑具体的字段名称,那里使用复制到哪里就可以了,是不是很方便。





下面是最后一个部分了。


说了首页之后,下面就是列表页面了。主要就是分页和查询。


        也许分页对你来说不是太容易(包括查询功能)(当然可能对于您也是非常容易的事情),但是对于我来说却是相当的容易 —— 因为我有自己的分页控件。



private void Page_Load(object sender, System.EventArgs e)

...{

       // 在此处放置用户代码以初始化页面

       Response.Cache.SetNoStore();


       Page1.PubShowDataObject = DL; //给一个显示数据的控件


       if (!Page.IsPostBack)

       ...{

              SetPageInfo();

       }

}


设置分页控件#region 设置分页控件

private void SetPageInfo()

...{

       Page1.SqlTableNames = "TableName";        //表名

       Page1.SqlColumns = "* ";                      //显示的字段

       Page1.SqlOrderColumn = "ID";                     //排序字段

       Page1.SqlOrderColumnKind = "int";           //排序字段的类型

       Page1.IsOrderDesc = true;              //倒序显示


       Page1.SqlPageSize = 10;        //一页的记录数

       Page1.SqlQuery = "" ;                    //查询条件


       Page1.CreateQuery();                     //生成查询语句

       Page1.BindFirstPage();                  //绑定第一页


}

#endregion



        我只要写上面这几行代码即可实现分页显示数据的功能(提取数据的部分,显示数据当然要另行处理了)。查询也是很简单的事情。组合SQL语句(where 后面的),然后赋值给 Page1.SqlQuery 属性,再执行

Page1.CreateQuery();                    //生成查询语句

       Page1.BindFirstPage();                  //绑定第一页


就可以了。点击下一页等的处理事件已经包含在控件里面了,没有特殊情况就不用自己再加事件了。


这都是控件带来的好处,也就是通用。在任何项目里面(包括后台管理)都可以很方便的使用!



下面总结一下:


UI层 : aspx页面


逻辑层,分为两种情况:

在项目里面只出现一次的函数,直接写在aspx.cs页面;

在项目里面会多次出现的函数,写在.cs文件里面。(比如登录、验证用户登录信息等)

大量使用自定义控件来简化编码。


数据访问层:调用dll文件,无需重复编写代码。


当然这么看起来的话好像变成了一层的代码,因为代码都写在了 aspx和aspx.cs里面了。

我采用的是分层的思路,而不是分层的形式!