一般在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可以理解为一个黑箱子.处理完返回实际发送给了多少人.
posted on 2007-01-25 15:03  冷火  阅读(370)  评论(1编辑  收藏  举报