zoukankan      html  css  js  c++  java
  • 数据层的逻辑

    一般在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日
  • 相关阅读:
    ucore lab4 内核线程管理 学习笔记
    谈谈博客三迁的经历
    ucore lab3 虚拟内存管理 学习笔记
    ucore lab2 物理内存管理 学习笔记
    ucore lab1 操作系统启动过程 学习笔记
    【VMware】在移动硬盘或U盘中安装便携linux系统
    借助ADB冻结与卸载Android系统应用(免ROOT)
    Windows下查找各类游戏存档路径
    QMetaObject::connectSlotsByName: No matching signal for XXX 原理探究
    将VScode添加至右键菜单
  • 原文地址:https://www.cnblogs.com/birdshover/p/629732.html
Copyright © 2011-2022 走看看