日期:2014-05-18  浏览次数:20479 次

.net三层架构UI层是否不应该出现SQL代码.
经常看到一些大虾谈论三层架构,说UI层不能出现SQL代码,有的甚至说业务逻辑层都不能出现SQL代码。。
 我不理解他们是怎么做的,避开ORM及LINQ等工具,用户的查询条件、排序是动态多变的,如果不在UI传递SQL。那不是要针对
 用户所能涉及到的查询及排序在业务逻辑层里都要写个方法? 如果是那样的话我会疯掉的。
 我自己做过一些三层的系统。复杂多条件的查询都是直接在UI层拼接Sql然后传到业务层的。
 当然拼接Sql如果检查或过滤不当可能导致存在Sql存入的安全隐患。

 希望听听大家的意见及看法, 或解决方案。

 这个是我用三层开发的第一个网站。 谢谢指点: http://www.08px.com/

------解决方案--------------------
我在帖子:一个简单功能的实现 中写了一个demo。对于你的问题来说,这个程序采取什么技术并不太重要,关键是你要仔细看看我写这个程序时是怎样组织数据源的。在开发界面时,我使用了一个“假”的BLL类代码,数据是内存中的对象集合。这时候,并不影响别人开发另一个基于数据库的BLL,甚至不同种类的数据库分别开发。这时候我们很清楚业务逻辑,但是使用什么底层数据库机制并不确定,甚至连是否是使用关系数据库也不确定。只要数据对象的字段一致,将别的BLL切换到界面程序上来只要几秒钟修改一下ObjectDataSource中的一个字符串属性就行了。

回到你的问题,具体怎样开发、怎样才“省事”,很肯能有你完全没有想到的那种工程方法。当你不是从工程经验出发,可能并不能顺利理解大系统是怎样设计和开发出来的。当软件系统变大变复杂,对于什么才是“省事”就有了不同的观点。
------解决方案--------------------
对于数据源对象的查询方法(SelectMothod)来说,也可以参数查询等等,可以看Scott Mitchell的例子:

http://blog.csdn.net/wanghr74/archive/2007/04/02/1549163.aspx
http://www.cnblogs.com/Reeezak/archive/2007/08/18/861234.html
以及他的教程中许多地方都是参数化的。表现部分使用参数化查询来调用BLL以及以下层,并不一定要直接写出SQL,也并不确定此时一定是关系数据库(何况不同关系数据库的SQL还有很多种语法差别)。
------解决方案--------------------
界面--业务逻辑--数据,这是广义的三层结构。如果是OO设计,查询应当在数据层实现,而其它层之间则通过传递BUSINESS OBJECT来通讯来实现更好的封装。原因是当操作涉及到Transaction或分布式Transaction,需要在方法级实现失败全部 rollback(不仅仅是一个查询语句内部的失败的rollback),你不封装Business Layer,就无法实现分布式Transaction。

你的设计不是源于真正的OO思维。就拿分页控件和数据绑定的设计来说,分页控件应当是个USER CONTROL(有Link Button和style及一些Server Control的组合),比较好的封装应当是用户点击一个页码,完成显示(比如当前页码变成非链接)后,它通过delegate产生一个事件来通知页面文件,并且将当前页码传给页面。而你的代码正好相反,要通过Business Object取得结果集后再告知分页控件。试想,有100个页面,每个页面都需要你来考虑当前页码是几,然后再告知分页控件,或是由分页控件通过 Event Args告诉你当前页码是几,哪个一效率高?

而页码(PageIndex)和PageSize,则应当是SQL查询的标准参数。也就是说,当分页完成后,将当前页码传给查询,再返回数据集。


这个代码:
string where = string.Empty;
if (!string.IsNullOrEmpty(key))
{
switch (type)
{
case 1:
where = pre ? "UserName = '" + key + "'" : "UserName LIKE '%" + key + "%'";
break;
default:
return;
}
}

可以在SQL里写成: Select * from Table where (@UseWildcard = 0 and UserName = @InUserName) or (@useWildCard = 1 and UserName like '%' + @InUserName + '%')


下面是分页加排序的一个SQL: 

SQL code

create procedure TestPagination
(
    @pageNumber          int,
    @rowEachPage          int,
    @minCurrentPageRowId  int,
    @maxCurrentPageRowId  int,
    @sortColumn          nvarchar(150),
    @sortOrder            bit
)
as
begin

select @minCurrentPageRowId = (@pageNumber * @rowEachPage)+1,
      @maxCurrentPageRowId = (@pageNumber + 1) * @rowEachPage

if @sortOrder = 0 --升序
  select * from
  (
    select row_number() over (order by case @sortColumn when 'id' then id end
                                                        when 'name' then name end ) as RowId, * from TestTable
  ) as t
  where RowId between @minCurrentPageRowId and @maxCurrentPageRowId

else  --降序
  select * from
  (
    select row_number() over (order by case @sortColumn when 'id' then id end desc
                                                        when 'name' then name end desc ) as RowId, * from TestTable
  ) as t
  where RowId between @minCurrentPageRowId and @maxCurrentPageRowId

end