数据层的逻辑

一般在N层设计中,数据层绝不应该牵扯到逻辑.理由很多,但最重要的一点,应该是有利于维护.如果面向的数据库是无存储过程的,那么这个法则应该被遵守.比如ACCESS,MYSQL.

如果使用MS SQLServer等其它有储存过程的数据库,我个人认为大不必如此.有时候一个好的存储过程能带来更良好的性能合可读性.

经典的架构就和牛顿的经典力学一样,并不是放之四海而皆为准的,不用毛主席的话说,那就是没有银弹.架构,OOP,这些前辈的经验都是面对一个问题的——客户的需求要变。如果客户的需求不变动,那要OOP,要设计模式,要架构有什么用?面向过程完全能写出更加高效,更加大众化的程序来。

我认为带逻辑的存储过程完全是AOP的实例,虽然存储过程不具备OOP的特征。数据层用来提供数据,至于怎么提供数据我认为没有必要过于关心。虽然一个带逻辑的存储过程可能会进行很多操作,以至于耦合度太高。但可以认为它是一个方法,甚至可以理解为一个封装的组件。

比如,在项目实施中实现站内短信息系统。当然,我这个例子可能不够好。
站内短消息要实现站内短信息的发送,收取,群发,黑名单过滤功能,涉及到批量更新数据及过滤。
建立3个表:
1,Messages
(MessagesID,UserFromID,Subject,[Content],SendToCount,IsKilledBySender)
分别是 ID,用户ID,主题,内容,发送数量,是否被发件人删除   //我在这里做了适当简化

2. MessagesToUsers
(MessageID,UserID,IsReaded)
分贝是ID,用户ID,是否已读

3. BlackList
(ListID,UserOwner,UserTarget)
分别是 ID,屏蔽的用户,被屏蔽的用户

要发送一次群发,如果用ADO.NET处理,需要以下步骤

1.      
连接数据库;
2.       查询获得收件人的ID;    //用户发送是选或填写用户名列表,需要遍历数据库对应ID

3.       插入消息;      //把消息写入Messages

4.       以收件人数开始循环;   //把收件人和信息得关系写入MessagesToUsers,是1对多关系

5.       循环中读取黑名单查看该发件人是否被收件人屏蔽;

6.       如果屏蔽则返回循环

7.       如果没屏蔽, 插入消息与收件人关系

这样就实现了.看上去复杂,却是最简单,最容易想到的办法.

而这个算法无疑使效率变得非常低.

而可以使用以下储存过程代替

 1 set ANSI_NULLS ON
 2 set QUOTED_IDENTIFIER ON
 3 GO
 4 -- =============================================
 5 -- Author:        <Author,,Name>
 6 -- Create date: <Create Date,,>
 7 -- Description:    <Description,,>
 8 -- =============================================
 9 ALTER PROCEDURE [dbo].[SendMessages]
10 (
11 @subject nvarchar(200),        --主题
12 @content text,                --内容
13 @sendto nvarchar(2000),        --收件人列表,以,分割的字符
14 @from int,                    --发件人
15 @type bit                    --消息类型(0为用户消息,1为系统提醒)        
16 )
17 AS
18 BEGIN
19     SET NOCOUNT ON;
20     --插入消息
21     insert into InMessages (UserFromID,Subject,[Content],SendTime,IsKilledBySender,ReadCount,SendToCount,MessageType)
22     values (@from,@subject,@content,getDate(),0,0,0,@type);
23     --取得插入消息ID
24     declare @messageid int
25     set @messageid = @@IDENTITY
26     --构造虚拟表(在查询用户ID的基础上,添加自定字段)
27     declare @sql nvarchar(400)
28     set @sql = 'select MessageID=' + cast(@@IDENTITY as varchar+ ',UserID,IsReaded=0 from Users where DisplayName in ( ' + @sendto + ') and (select count(UserTarget) from MsgBlackList where UserOwner = UserID and UserTarget = ' + cast(@from as varchar+ ') = 0'
29     insert into InMessagesToUsers EXEC(@sql)
30     
31     --更新该邮件发送人数
32     declare @sendcount int
33     set @sendcount = (select count(mu.MessageID) from InMessagesToUsers as mu where mu.MessageID = @messageid)
34     update InMessages set SendToCount = @sendcount where MessageID = @messageid
35     
36     --返回邮件发送人数,表字段名:SendCount
37     select SendCount = @sendcount
38 END
39 
40 

传入参数里有得字段被我省略了(SQL Server 2005).

发送人发送消息被记录到InMessages

收件人由字符串组成,所以查询采用select in

收件人于信件的关系被记录在表InMessagesToUsers

黑名单表MsgBlackList


而调用也非常简单

 1       /// <summary>
 2         /// 执行存储过程SendMessages
 3         /// </summary>
 4         /// <returns></returns>
 5         public override string ProcessAction()
 6         {
 7             string subject = GetQueryValue("Subject");
 8             string content = GetQueryValue("Content");
 9             string sendto = GetQueryValue("SendTo");
10             sendto = "'" + sendto + "'";
11             sendto = sendto.Replace(",""','");   //sendto是有,连接的收件人,这里处理了才能被select in 使用
12 
13             SUser s = LoginHelper.CrrentUser();
14             int from = s.UserID;
15             
16             if (from != 0)
17             {
18                 bool MessageType = false;
19                 SqlParameter[] parms = {
20                     new SqlParameter("@subject",SqlDbType.NVarChar,200),
21                     new SqlParameter("@content",SqlDbType.Text),
22                     new SqlParameter("@sendto",SqlDbType.NVarChar,4000),
23                     new SqlParameter("@from",SqlDbType.Int),
24                     new SqlParameter("@type",SqlDbType.Bit)
25                 };
26                 parms[0].Value = subject;
27                 parms[1].Value = content;
28                 parms[2].Value = sendto;
29                 parms[3].Value = from;
30                 parms[4].Value = MessageType;
31                 
32                 
33                 DataTable dt = DBHelper.ExecuteTable
34                     (
35                     CommandType.StoredProcedure,
36                     "SendMessages",
37                     parms
38                     );
39 
40                 //sendcount应为INT类型,这里用string用于返回
41                 string sendcount = dt.Rows[0]["SendCount"].ToString();
42 
43                 return "2|消息共发送给了" + sendcount + "人|SendMessages.aspx";
44 
45             }
46             else
47             {
48                 return "2|请登陆后再发|Login.aspx";
49             }
50         }
51     }

无论效率和可读性都比常规办法要好很多.
存储过程SendMessages可以理解为一个黑箱子.处理完返回实际发送给了多少人.

http://www.cnblogs.com/birdshover/
2007年1月25日
posted @ 2007-01-25 00:11 Birdshover 阅读(1611) 评论(12)  编辑 收藏 所属分类: 数据

  回复  引用  查看    
#1楼 2007-01-25 00:34 | 马哥      
我从来就没认真遵守过所谓的架构、标准。
凡能达到目的,基本上不择手段:D
  回复  引用  查看    
#2楼 2007-01-25 09:00 | cnlamar      
不从遵守规范的角度来说,我依然不喜欢存储过程。

不在乎说他里面是否有足够多的注释,不在乎命名是否规范,只因为如果数据库中有数千甚至上万的存储过程的时候,很难进行管理,他甚至不可以分类,只能杂乱的将所有存储过程放在一起,维护性,不单单是指代码本身,还包括对代码的管理,后者在系统足够大的时候更显得突出。
  回复  引用  查看    
#3楼 2007-01-25 09:13 | Daniel Phang      
我也不太赞成将业务逻辑写到存储过程,尽管它效率有可能会高一点。但某些时候为了实现某种业务逻辑,可能会在存储过程中使用游标,在数据量很大的时候,性能也会有影响
  回复  引用  查看    
#4楼 2007-01-25 09:52 | 喝酒的猫      
存储过程中好像没有使用事务吧!
  回复  引用  查看    
#5楼 [楼主]2007-01-25 10:00 | BirdsHover      
加个事务也是很简单的,不过我认为在这个场合不需要事务.即便实效了也不是什么大事情.发件人还是能知道发给了多少人的
  回复  引用    
#6楼 2007-01-25 11:15 | ganett_redaaple [未注册用户]
为什么存储过程中没有事物啊?
还是不理解
存储过程中是有事物的~

  回复  引用    
#7楼 2007-01-25 14:10 | yzx110 [未注册用户]
同意作者的思路。

@cnlamar
没有这么极端吧,使用存储过程并不意味着任何事情都要用存储过程来做,合适的就好了,简单的insert/select/update的就免了,并且能在代码中很高效简介实现的复杂逻辑也不用存储过程。
如果真能做到成千上万的存储过程,那这个系统的复杂度已经难以想象了,用不用存储过程都不是重点了。

根据这个原则,再加上我的实际经验,一个不怎么复杂的系统会写0个或者<=5个存储过程。

@Daniel Phang
这个就是实现者的问题,如果在存储过程中用游标,代码也一样可以用,难不成些存储过程的人都喜欢用游标?

总之,我不提倡大量用存储过程,但是合适的地方用存储过程会让你的系统更8简洁、高效

  回复  引用  查看    
#8楼 2007-01-26 00:25 | shenfx      
我比较同意@cnlamar 的观点,也认为在大型系统中不宜使用包含有复杂逻辑的存储过程,确实难于管理,不过又感觉如果存储过程不包含逻辑那还剩什么啦?基本的CURD?最多多表级联一下,那这样存储过程还有使用的必要么?
  回复  引用  查看    
#9楼 [楼主]2007-01-27 19:02 | BirdsHover      
我并不是鼓吹大家用存储过程,我认为当设计不能满足效率的时候,如果储存过程能达到目的,那就大可以用。

何况我认为可以把存储过程看作是特殊的数据层内容。用了存储过程是否侵入业务层主要看设计的功力。就算是入侵了业务层,如果这方面的业务有把握90%不会变动的话,为什么不能用呢?
  回复  引用    
#10楼 2007-04-20 09:25 | 菜鸟而已 [未注册用户]
在这里看了这么多高手的讨论,我也有点冲动想说一说自己的看法了。
我觉得楼主的说法的确很对
但是存储过程看起来也有管理上的麻烦
不过
我个人觉得(只是一个菜鸟的个人看法,说得不对不要打我哦)
存储过程最好的地方应该是它的事务功能吧?
当有要联动的数据表操作
它的确为整个程序提供了数据的统一性
特别是在插入的时候,我特有这个感觉
所以我对存储过程的用法也是,简单的数据表操作,绝对不用。
一般复杂的数据表操作,能免则免
超复杂的数据表操作(就类似楼主的这种),能用就用

标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      


相关链接: