实战剖析三层架构

实战剖析三层架构1 收藏
实战剖析三层架构1
      引言:本文不是从理论的角度来探讨三层架构,而是用一个示例来介绍如何建设一个三层架构的项目,并说明项目中各个文件所处的层次与作用。写本文的目的,不是为了说明自己的这个方法有多对,别人的肯定不对,而是希望给那些初学三层架构却不知从何入手的朋友提供一点帮助。因为网上的文章,大多是注重理论的介绍,而忽略了具体的实践应用,或者有示例但讲得不透彻。导致看了之后,理论上又学习了一遍,但还是不知道代码怎么写。所以想从这个方面入手写一下,让从来没做过三层架构的初学者也能照猫画虎,写出代码来。文章表述的是笔者个人对三层架构的认识,肯定有许多不足的地方,欢迎大家指正,小弟也会根据反馈来修改这篇文章。文中的代码是伪代码,仅用来阐明思路。
    正文:
    一提三层架构,大家都知道是表现层(UI),业务逻辑层(BLL)和数据访问层(DAL),而且每层如何细分也都有很多的方法。但具体代码怎么写,到底那些文件算在哪一层,却是模模糊糊的。下面用一个简单的例子来带领大家实战三层架构的项目,这个例子只有一个功能,就是用户的简单管理。
    首先建立一个空白解决方案,添加如下项目及文件
    1、添加ASP.NET Web Application项目,命名为UI,新建Web Form类型文件User.aspx(含User.aspx.cs)
    2、添加ClassLibrary项目,命名为BLL,新建Class类型文件UserBLL.cs
    3、添加ClassLibrary项目,命名为DAL,新建Class类型文件UserDAL.cs。添加SQLHelper引用。(这个是微软的数据访问类,也可以不用,直接编写所有的数据访问代码。我一般用自己写的数据访问类DataAccessHelper )。
    4、添加ClassLibrary项目,命名为Model,新建Class类型文件UserModel.cs
    5、添加ClassLibrary项目,命名为IDAL,新建Interface类型文件IUserDAL.cs
    6、添加ClassLibrary项目,命名为ClassFactory
    相信大家已经看出来了,这个和Petshop的示例没什么区别,而且更简单,因为在下也是通过Petshop学习三层架构的。但一些朋友对于这几个项目所处的层次,以及它们之间的关系,可能比较模糊,这里逐个说明一下:
    1、User.aspx和User.aspx.cs
    这两个文件(以及文件所属的项目,下面也是如此,不再重复强调了)都属于表现层部分。User.aspx比较好理解,因为它就是显示页面了。User.aspx.cs有些人觉得不应该算,而是要划到业务逻辑层中去。如果不做分层的话,那么让User.aspx.cs来处理业务逻辑,甚至操作数据库都没什么问题,但是做分层的话,这样就不应该了。在分层结构中,User.aspx.cs仅应该处理与显示有关的内容,其它部分都不应该涉及。
    举例:我们实现用列表方式显示用户的功能,那么提取信息的工作是由BLL来做的,UI(本例中是User.aspx.cs)调用BLL得到UserInfo后,通过代码绑定到User.aspx的数据控件上,就实现了列表的显示。在此过程中User.aspx.cs对UI没有起到什么作用,仅是用来传递数据,而且因为实际编码中大部分情况都是如此的实现,所以使有些人觉得User.aspx.cs不应该算UI,而应该并入BLL负责逻辑处理。继续往下看,这时提出了一个新需求,要求在每个用户的前面加一个图标,生动地表现出用户的性别,而且不满18岁的用儿童图标表示。这个需求的实现,就轮到User.aspx.cs来做了,这种情况下User.aspx.cs才算有了真正的用途。
    2、NewBLL.cs
    添加如下方法:
    public IList<UserInfo> GetUsers():返回所有的用户信息列表
    public UserInfo GetUser(int UserId):返回指定用户的详细信息
    public bool AddUser(UserInfo User):新增用户信息
    public bool ChangeUser(UserInfo User):更新用户信息
    public void RemoveUser(int UserId):移除用户信息
    此文件就属于业务逻辑层了,专门用来处理与业务逻辑有关的操作。可能有很多人觉得这一层唯一的用途,就是把表现层传过来的数据转发给数据层。这种情况确实很多,但这只能说明项目比较简单,或者项目本身与业务的关系结合的不紧密(比如当前比较流行的MIS),所以造成业务层无事可做,只起到了一个转发的作用。但这不代表业务层可有可无,随着项目的增大,或者业务关系比较多,业务层就会体现出它的作用来了。
    此处最可能造成错误的,就是把数据操作代码划在了业务逻辑层,而把数据库作为了数据访问层。
    举例:有些朋友感觉BLL层意义不大,只是将DAL的数据提上来就转发给了UI,而未作任何处理。看一下这个例子
    BLL层
    SelectUser(UserInfo userInfo)根据传入的username或email得到用户详细信息。
    IsExist(UserInfo userInfo)判断指定的username或email是否存在。
    然后DAL也相应提供方法共BLL调用
    SelectUser(UserInfo userInfo)
    IsExist(UserInfo userInfo)
    这样BLL确实只起到了一个传递的作用。
    但如果这样做:
    BLL.IsExist(Userinfo userinfo)
    {
          UerInfo user = DAL.SelectUser(User);
        return (userInfo.Id != null);
    }
    那么DAL就无需实现IsExist()方法了,BLL中也就有了逻辑处理的代码。
    3、UserModel.cs
    实体类,这个东西,大家可能觉得不好分层。包括我以前在内,是这样理解的:UIßàModelßàBLLßàModelßàDAL,如此则认为Model在各层之间起到了一个数据传输的桥梁作用。不过在这里,我们不是把事情想简单,而是想复杂了。
    Model是什么?它什么也不是!它在三层架构中是可有可无的。它其实就是面向对象编程中最基本的东西:类。一个桌子是一个类,一条新闻也是一个类,int、string、doublie等也是类,它仅仅是一个类而已。
    这样,Model在三层架构中的位置,和int,string等变量的地位就一样了,没有其它的目的,仅用于数据的存储而已,只不过它存储的是复杂的数据。所以如果你的项目中对象都非常简单,那么不用Model而直接传递多个参数也能做成三层架构。
    那为什么还要有Model呢,它的好处是什么呢。下面是思考一个问题时想到的,插在这里:   
    Model在各层参数传递时到底能起到做大的作用?
    在各层间传递参数时,可以这样:
    AddUser(userId,userName,userPassword,…,)
    也可以这样:
    AddUser(userInfo)
    这两种方法那个好呢。一目了然,肯定是第二种要好很多。
    什么时候用普通变量类型(int,string,guid,double)在各层之间传递参数,什么使用Model传递?下面几个方法:
    SelectUser(int UserId)
    SelectUserByName(string username)
    SelectUserByName(string username,string password)
    SelectUserByEmail(string email)
    SelectUserByEmail(string email,string password)
    可以概括为:
    SelectUser(userId)
    SelectUser(user)
    这里用user这个Model对象囊括了username,password,email这三个参数的四种组合模式。UserId其实也可以合并到user中,但项目中其它BLL都实现了带有id参数的接口,所以这里也保留这一项。
    传入了userInfo,那如何处理呢,这个就需要按照先后的顺序了,有具体代码决定。
    这里按这个顺序处理
    首先看是否同时具有username和password,然后看是否同时具有email和password,然后看是否有username,然后看是否有email。依次处理。
    这样,如果以后增加一个新内容,会员卡(number),则无需更改接口,只要在DAL的代码中增加对number的支持就行,然后前台增加会员卡一项内容的表现与处理即可。
    4、UserDAL.cs
    public IList<UserInfo> SelectUsers():返回所有的用户信息列表
    public UserInfo SelectUser(int UserId):返回指定用户的相信信息
    public bool InsertUser(UserInfo User):新增用户信息
    public bool UpdateUser(UserInfo User):更新用户信息
    public void DeleteUser(int UserId):移除用户信息
    很多人最闹不清的就是数据访问层,到底那部分才算数据访问层呢?有些认为数据库就是数据访问层,这是对定义没有搞清楚,DAL是数据访问层而不是数据存储层,因此数据库不可能是这一层的。也有的把SQLHelper(或其同类作用的组件)作为数据访问层,它又是一个可有可无的东西,SQLHelper的作用是减少重复性编码,提高编码效率,因此如果我习惯在乎效率或使用一个非数据库的数据源时,可以丢弃SQLHelper,一个可以随意弃置的部分,又怎么能成为三层架构中的一层呢。
    可以这样定义:与数据源操作有关的代码,就应该放在数据访问层中,属于数据访问层
    5、IUserDAL
    数据访问层接口,这又是一个可有可无的东西,因为Petshop中带了它和ClassFactory类工厂,所以有些项目不论需不需要支持多数据源,都把这两个东西做了进来,有的甚至不建ClassFactory而只建了IDAL,然后“IUserDAL iUserDal = new UserDAL();”,不知意义何在。这就完全是画虎不成反类犬了。
    许多人在这里有一个误解,那就是以为存在这样的关系:BLLßàIDALßàDAL,认为IDAL起到了BLL和DAL之间的桥梁作用,BLL是通过IDAL来调用DAL的。但实际是即使你如此编码:“IUserDAL iUserDal = ClassFacotry.CreateUserDAL();”,那么在执行“iUserDal.SelectUsers()”时,其实还是执行的UserDAL实例,而不是IUserDAL实例,所以IDAL在三层中的位置是与DAL平级的关系。
    通过上面的介绍,基本上将三层架构的层次结构说明了。其实,本人有一个判断三层架构是否标准的方法,那就是将三层中的任意一层完全替换,都不会对其它两层造成影响,这样的构造基本就符合三层标准了(虽然实现起来比较难^_^)。例如如果将项目从B/S改为C/S(或相反),那么除了UI以外,BLL与DAL都不用改动;或者将SQLServer改为Oracle,只需替换SQLServerDAL到OracleDAL,无需其它操作等等。本来想在文中加入一些具体的代码的,但感觉不是很必要,如果大家觉得需要的话,我再补充吧。
    总结:不要因为某个层对你来说没用,或者实现起来特别简单,就认为它没有必要,或者摒弃它,或者挪作它用。只要进行了分层,不管是几层,每一层都要有明确的目的和功能实现,而不要被实际过程所左右,造成同一类文件位于不同层的情况发生。也不要出现同一层实现了不同的功能的情况发生。


 

前段时间写了《实战剖析三层架构》,看有些朋友希望提供一下代码。但近期博客园上关于三层架构的文章很多,而且写得都很好,所以就不准备被再写了。不过这几天又有朋友留言鼓励,而且编程中发现一段比较合适的,所以还是决定写出来共享给大家。
    先简要介绍一下,这个模块是一个商品管理模块。程序中实现了商品的浏览、添加、修改等功能。

   

    Model项目,商品品牌实体类,BrandInfo.cs:

Code
using System;

namespace JKL.eShop.Model
{
    /**//// <summary>
    /// 品牌元数据
    /// </summary>
    [Serializable]
    public class BrandInfo
    {
        /**//***************************************************
         * 成员列表
         ***************************************************/

        //品牌Id
        private int _Id;
        //品牌名称
        private string _Name;
        //排序编号
        private int _Number;
        //是否为推荐品牌
        private bool _IsVouch;  

        /**//***************************************************
         * 属性列表
         ***************************************************/

        /**//// <summary>
        /// 品牌Id
        /// </summary>
        public int Id
        {
            get { return _Id; }
            set { _Id = value; }
        }

        /**//// <summary>
        /// 品牌名称
        /// </summary>
        public string Name
        {
            get { return _Name; }
            set { _Name = value; }
        }

        /**//// <summary>
        /// 排序编号
        /// </summary>
        public int OrderNumber
        {
            get { return _Number; }
            set { _Number = value; }
        }

        /**//// <summary>
        /// 是否推荐品牌
        /// </summary>
        public bool IsVouch
        {
            get { return _IsVouch; }
            set { _IsVouch = value; }
        }

        public string Vouch
        {
            get
            {
                if (_IsVouch)
                {
                    return "推荐品牌";
                }
                else
                {
                    return "普通品牌";
                }
            }
        }

        /**//***************************************************
         * 构造函数
         ***************************************************/

        public BrandInfo() { }

        public BrandInfo(int id, string name)
        {
            _Id = id;
            _Name = name;
        }

        public BrandInfo(int id, string name, int orderNumber, bool isVouch)
        {
            _Id = id;
            _Name = name;
            _Number = orderNumber;
            _IsVouch = isVouch;
        }
    }
}
    Model项目:GoodSortInfo.cs

Code
using System;
using System.Collections.Generic;
using System.Text;

namespace JKL.eShop.Model
{
    /**//// <summary>
    /// 分类实体类
    /// </summary>
    [Serializable]
    public class GoodSortInfo
    {
        /**//***************************************************
         * 成员列表
          ***************************************************/

        //分类Id
        private int _Id;
        //分类名称
         private string _Name;
        //级别
         private int _Layer;      
        //上级分类
         private GoodSortInfo _ParentSort;
        //下级分类数量
         private int _ChildNumber;
        //备注
         private string _Remark;

        /**//***************************************************
         * 属性列表
         ***************************************************/

        /**//// <summary>
        /// 分类Id     
        /// </summary>
        public int Id
        {
            get { return _Id; }
            set { _Id = value; }
        }

        /**//// <summary>
        /// 分类名称
        /// </summary>
        public string Name
        {
            get { return _Name; }
            set { _Name = value; }
        }

        /**//// <summary>
        /// 分类等级
        /// </summary>
        public int Layer
        {
            get { return _Layer; }
            set { _Layer = value; }
        }

        /**//// <summary>
        /// 上级分类
        /// </summary>
        public GoodSortInfo ParentSort
        {
            get { return _ParentSort; }
            set { _ParentSort = value; }
        }

        /**//// <summary>
        /// 上级分类Id
        /// </summary>
        public int ParentId
        {
            get { return _ParentSort.Id; }
        }

        /**//// <summary>
        /// 下级分类数量
        /// </summary>
        public int ChildSortCount
        {
            get { return _ChildNumber; }
            set { _ChildNumber = value; }
        }

        /**//// <summary>
        /// 备注
        /// </summary>
        public string Remark
        {
            get { return _Remark; }
            set { _Remark = value; }
        }

        /**//***************************************************
         * 构造函数
         ***************************************************/

        public GoodSortInfo() { }

        public GoodSortInfo(int id)
        {
            _Id = id;
        }

        public GoodSortInfo(int id, string name)
        {
            _Id = id;
            _Name = name;
        }

        public GoodSortInfo(int id, string name, int depth, int childNumber)
        {
            _Id = id;
            _Name = name;
            _Layer = depth;        
            _ChildNumber = childNumber;
        }
    }
}

 

    Model项目,GoodInfo.cs:

Code
using System;
using System.Collections;
using System.Collections.Generic;

namespace JKL.eShop.Model
{
    /**//// <summary>
    /// 商品元数据
    /// </summary>  
    [Serializable]
    public class GoodInfo
    {
        /**//***************************************************
         * 成员列表
         ***************************************************/

        //商品Id
        private int _Id;
        //商品名称
        private string _Name;
        //品牌
        private BrandInfo _BrandInfo;
        //分类
        private GoodSortInfo _SortInfo;
        //商品编码
        private string _Code;
        //条形码
        private string _Barcode;     
    
        //单位(个/盒/袋等)
        private string _Unit;
        //规格
        private string _Standard;
        //简介
        private string _SimpleIntroduction;
        //详细说明
        private string _DetailIntroduction;
        //图片路径
        private string _PicturePath;

        //发布日期
        private DateTime _PublicTime;
        //更新日期
        private DateTime _UpdateTime;
        //市场价
        private decimal _MarketPrice;
        //零售价
        private decimal _SellPrice;
        //赠送积分
        private decimal _Score;

        //浏览次数
        private int _ClickCount;
        //上一次浏览时间
        private DateTime _ClickTime;

        //商品种类
        private string _KindName;

        //库存数量
        private int _Stock;

        //是否显示
        private bool _Visable = false;

        关键字段枚举#region 关键字段枚举

        private static Hashtable _KindList;
        private static Hashtable _PopList;

        #endregion

        /**//***************************************************
         * 属性列表
         ***************************************************/

        public int Id
        {
            get { return _Id; }
            set { _Id = value; }
        }

        /**//// <summary>
        /// 商品编码
        /// </summary>
        public string Code
        {
            get { return _Code; }
            set { _Code = value; }
        }

        /**//// <summary>
        /// 条形码
        /// </summary>
        public string Barcode
        {
            get { return _Barcode; }
            set { _Barcode = value; }
        }

        /**//// <summary>
        /// 品牌
        /// </summary>
        public BrandInfo BrandInfo
        {
            get
            {
                if (_BrandInfo == null)
                {
                    _BrandInfo = new BrandInfo();
                }
                return _BrandInfo;
            }
            set { _BrandInfo = value; }
        }

        /**//// <summary>
        /// 品牌名称
        /// </summary>
        public string BrandName
        {
            get { return this.BrandInfo.Name; }
            set { this.BrandInfo.Name = value; }
        }

        /**//// <summary>
        /// 商品分类
        /// </summary>
        public GoodSortInfo Sort
        {
            get { return _SortInfo; }
            set { _SortInfo = value; }
        }
      
        /**//// <summary>
        /// 商品名称
        /// </summary>
        public string Name
        {
            get { return _Name; }
            set { _Name = value; }
        }

        /**//// <summary>
        /// 单位(个/盒/袋等)
        /// </summary>
        public string Unit
        {
            get { return _Unit; }
            set { _Unit = value; }
        }

        /**//// <summary>
        /// 规格
        /// </summary>
        public string Standard
        {
            get { return _Standard; }
            set { _Standard = value; }
        }

        /**//// <summary>
        /// 简介
        /// </summary>
        public string SimpleIntroduction
        {
            get { return _SimpleIntroduction; }
            set { _SimpleIntroduction = value; }
        }

        /**//// <summary>
        /// 详细说明
        /// </summary>
        public string DetailIntroduction
        {
            get { return _DetailIntroduction; }
            set { _DetailIntroduction = value; }
        }

        /**//// <summary>
        /// 图片路径
        /// </summary>
        public string PicturePath
        {
            get { return _PicturePath; }
            set { _PicturePath = value; }
        }

        /**//// <summary>
        /// 发布日期
        /// </summary>
        public DateTime PublicTime
        {
            get { return _PublicTime; }
            set { _PublicTime = value; }
        }

        /**//// <summary>
        /// 更新日期
        /// </summary>
        public DateTime UpdateTime
        {
            get { return _UpdateTime; }
            set { _UpdateTime = value; }
        }

        /**//// <summary>
        /// 市场价
        /// </summary>
        public decimal MarketPrice
        {
            get { return _MarketPrice; }
            set { _MarketPrice = value; }
        }

        /**//// <summary>
        /// 零售价
        /// </summary>
        public decimal SellPrice
        {
            get { return _SellPrice; }
            set { _SellPrice = value; }
        }

        /**//// <summary>
        /// 赠送积分
        /// </summary>
        public decimal Score
        {
            get { return _Score; }
            set { _Score = value; }
        }

        /**//// <summary>
        /// 浏览次数
        /// </summary>
        public int ClickCount
        {
            get { return _ClickCount; }
            set { _ClickCount = value; }
        }

        /**//// <summary>
        /// 上次浏览时间
        /// </summary>
        public DateTime ClickTime
        {
            get { return _ClickTime; }
            set { _ClickTime = value; }
        }

        public string KindName
        {
            get { return _KindName; }
            set { _KindName = value; }
        }

        /**//// <summary>
        /// 库存数量
        /// </summary>
        public int Stock
        {
            get { return _Stock; }
            set { _Stock = value; }
        }

        /**//// <summary>
        /// 是否显示
        /// </summary>
        public bool Visable
        {
            get { return _Visable; }
            set { _Visable = value; }
        }
    
        /**//***************************************************
         * 构造函数
         ***************************************************/

        public GoodInfo() { }

        public GoodInfo(int id, string code, string name, string picturePath, decimal marketPrice, decimal sellPrice, string unit, string standard, int stock)
        {
            _Id = id;
            _Code = code;
            _Name = name;
            _PicturePath = picturePath;
            _MarketPrice = marketPrice;
            _SellPrice = sellPrice;
            _Unit = unit;
            _Standard = standard;
            _Stock = stock;
        }

        public GoodInfo(int id, string code, string name, string picturePath, string brandName, string kindName, string standard, string simpleIntroduction, DateTime publicTime, decimal marketPrice, decimal sellPrice, string unit, decimal score, int clickNumber)
        {
            _Id = id;
            _Code = code;
            _Name = name;
            _PicturePath = picturePath;
            this.BrandName = brandName;
            _KindName = kindName;
            _Standard = standard;
            _SimpleIntroduction = simpleIntroduction;
            _PublicTime = publicTime;
            _MarketPrice = marketPrice ;
            _SellPrice = sellPrice;
            _Unit = unit;
            _Score = score;
            _ClickCount = clickNumber;
            _Stock = Stock;
        }

        public GoodInfo(int id, string code, string name, string picturePath, string brandName, string kindName, string standard, string simpleIntroduction, string detailIntroduction, decimal marketPrice, decimal sellPrice, string unit, decimal score, int clickNumber, DateTime publicTime, int stock)
        {
            _Id = id;
            _Code = code;
            _Name = name;
            _PicturePath = picturePath;
            this.BrandName = brandName;
            _KindName = kindName;
            _Standard = standard;
            _SimpleIntroduction = simpleIntroduction;
            _DetailIntroduction = detailIntroduction;
            _MarketPrice = marketPrice;
            _SellPrice = sellPrice;
            _Unit = unit;
            _Score = score;
            _ClickCount = clickNumber;
            _PublicTime = publicTime;
            _Stock = stock;
        }
    }
}
 BLL项目:GoodBLL.cs

BLL
using System;
using System.Collections.Generic;
using JKL.eShop.DAL;
using JKL.eShop.Model;

namespace JKL.eShop.BLL
{
    /**//// <summary>
    ///
    /// </summary>
    public class GoodBLL
    {
        /**//* **************************************************
         * 成员列表
         * *************************************************/

        private GoodDAL _GoodDal = null;

        /**//* **************************************************
         * 属性列表
         * *************************************************/

        private GoodDAL GoodDal
        {
            get
            {
                if (_GoodDal == null)
                {
                    _GoodDal = new GoodDAL();
                }
                return _GoodDal;
            }
        }

        /**//* **************************************************
         * 公共方法
         * *************************************************/

        /**//// <summary>
        /// 更新商品信息
        /// </summary>
        /// <param name="goodInfo"></param>
        /// <returns></returns>
        public bool ChangeGood(GoodInfo goodInfo)
        {
            return false;
        }

        /**//// <summary>
        /// 商品变价
        /// </summary>
        /// <param name="goodinfo"></param>
        /// <returns></returns>
        public bool ChangePrice(GoodInfo goodinfo)
        {
            return this.GoodDal.UpdateGoodsPrice(goodinfo);
        }

        /**//// <summary>
        /// 根据ID返回商品的详细信息
        /// </summary>
        /// <param name="goodId"></param>
        /// <returns></returns>
        public GoodInfo GetGood(int goodId)
        {
            GoodInfo good = this.GoodDal.SelectGood(goodId);
            good.ClickCount++;
            this.GoodDal.UpdateGood(good);
            return good;
        }

        返回商品信息列表#region 返回商品信息列表

        /**//// <summary>
        /// 返回所有商品的列表
        /// </summary>
        /// <returns></returns>
        public IList<GoodInfo> GetGoodsList()
        {
            return this.GoodDal.SelectGoods();
        }

        /**//// <summary>
        /// 返回指定分类的所属商品列表
        /// </summary>
        /// <param name="sortId">分类Id</param>
        /// <returns></returns>
        public IList<GoodInfo> GetGoodsListBySort(int sortId)
        {
            return this.GoodDal.SelectGoodsBySort(sortId);
        }

        /**//// <summary>
        /// 根据热门程度的类型返回商品列表
        /// </summary>
        /// <param name="sortId"></param>
        /// <returns></returns>
        public IList<GoodInfo> GetGoodsListByPop(string popKind)
        {
            return this.GoodDal.SelectGoodsListByPop(1000, popKind);
        }

        /**//// <summary>
        /// 返回指定品牌的所属商品列表
        /// </summary>
        /// <param name="brandName"></param>
        /// <returns></returns>
        public IList<GoodInfo> GetGoodsListByBrand(string brandName)
        {
            GoodDAL goodDal = new GoodDAL();
            return goodDal.SelectGoodsListByBrand(brandName);       
        }

        public IList<GoodInfo> GetGoodsBrief()
        {
            return this.GoodDal.SelectGoodsBrief();
        }

        /**//// <summary>
        ///
        /// </summary>
        /// <param name="topNumber"></param>
        /// <param name="kindName"></param>
        /// <returns></returns>
        public IList<GoodInfo> GetGoodsBriefByKind(int topNumber, string kindName)
        {
            return this.GoodDal.SelectGoodsBriefByKind(topNumber, kindName);
        }

        public IList<GoodInfo> GetGoodsListByKind(string kindName)
        {
            return this.GoodDal.SelectGoodsListByKind(kindName);
        }

        #endregion

        商品查询部分#region 商品查询部分

        public IList<GoodInfo> QueryGoodsList(string keyword)
        {
            GoodDAL goodDal = new GoodDAL();
            return goodDal.SelectGoodsListByKeyword(keyword);
        }

        #endregion
    }
}

    DAL项目:GoodDAL.cs

Code
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using KoalaStudio.Data.SqlServer;
using JKL.eShop.Model;

namespace JKL.eShop.DAL
{
    /**//// <summary>
    ///
    /// </summary>
    public class GoodDAL
    {
        /**//* **************************************************
         * 成员列表
         * **************************************************/       

        /**//* **************************************************
         * 属性列表
         * **************************************************/      

        /**//* **************************************************
         * 构造函数
         * **************************************************/

        public GoodDAL()        {                    }

        /**//* **************************************************
         * 私有和受保护方法
         * **************************************************/

        /**//// <summary>
        ///
        /// </summary>
        /// <param name="sqlText"></param>
        /// <returns></returns>
        private IList<GoodInfo> SelectGoodsBrief(string sqlText)
        {
            CmdCommand cmd = new CmdCommand();
            cmd.CommandText = sqlText;
            SqlDataReader dr = cmd.ExecuteReader();
            IList<GoodInfo> goods = new List<GoodInfo>();
            SortDAL sortDal = new SortDAL();
            if (dr.HasRows)
            {
                while (dr.Read())
                {
                    GoodInfo good = new GoodInfo();
                    good.Id = Convert.ToInt32(dr["Id"]); ;
                    good.Code = dr["dianneima"].ToString();
                    good.Name = dr["Name"].ToString();
                    string picturePath = dr["Picture"].ToString();
                    good.PicturePath = picturePath.Substring(0, picturePath.IndexOf("|||"));
                    good.MarketPrice = Convert.ToDecimal(dr["MarketPrice"]); ;
                    good.SellPrice = Convert.ToDecimal(dr["MemberPrice"]);
                    good.Unit = dr["DanWei"].ToString();
                    good.Standard = dr["GuiGe"].ToString();
                    goods.Add(good);
                }
                dr.Close();
                cmd.Disconnection();
            }
            return goods;
        }

        /**//// <summary>
        ///
        /// </summary>
        /// <param name="sqlText"></param>
        /// <returns></returns>
        private IList<GoodInfo> SelectGoodsList(string sqlText)
        {
            CmdCommand cmd = new CmdCommand();
            cmd.CommandText  = sqlText;
            SqlDataReader dr = cmd.ExecuteReader();
            IList<GoodInfo> goods = new List<GoodInfo>();
            SortDAL sortDal = new SortDAL();
            if (dr.HasRows)
            {
                while (dr.Read())
                {
                    GoodInfo good = new GoodInfo();
                    good.Id = Convert.ToInt32(dr["Id"]);
                    good.Name = dr["Name"].ToString();
                    good.BrandInfo.Name = dr["Brand"].ToString();                  
                    good.Code = dr["dianneima"].ToString();
                    string picturePath = dr["Picture"].ToString();
                    good.PicturePath = picturePath.Substring(0, picturePath.IndexOf("|||"));
                    good.KindName = dr["Kind"].ToString();
                    good.Standard = dr["GuiGe"].ToString();
                    good.SimpleIntroduction = dr["Introduce"].ToString();
                    good.PublicTime = Convert.ToDateTime(dr["JoinDate"]);
                    good.MarketPrice = Convert.ToDecimal(dr["MarketPrice"]); ;
                    good.SellPrice = Convert.ToDecimal(dr["MemberPrice"]);
                    good.Unit = dr["DanWei"].ToString();
                    good.Score = Convert.ToDecimal(dr["Score"]);
                    good.ClickCount = Convert.ToInt32(dr["TotalHits"]);
                    goods.Add(good);
                }
                dr.Close();
                cmd.Disconnection();
            }
            return goods;
        }

        /**//* **************************************************
         * 公共方法
         * **************************************************/

        /**//// <summary>
        /// 更新商品的价格和库存信息
        /// </summary>
        /// <returns></returns>
        public bool UpdateGoodsPrice(GoodInfo goodInfo)
        {
            CmdCommand cmd = new CmdCommand();
            cmd.CommandText = "UPDATE SW_Product SET MarketPrice = " + goodInfo.MarketPrice + ", MemberPrice = " + goodInfo.SellPrice + ", Stock = " + goodInfo.Stock + ", ModiDate = '" + DateTime.Now + "' WHERE Id = " + goodInfo.Id;
            cmd.ExecuteNonQuery();
            return true;
        }

        /**//// <summary>
        /// 更新商品信息
        /// 当前仅支持更新点击率一项
        /// </summary>
        /// <param name="good"></param>
        /// <returns></returns>
        public bool UpdateGood(GoodInfo good)
        {
            CmdCommand cmd = new CmdCommand();
            cmd.CommandText = "UPDATE eShop_Goods SET ClickCount = " + good.ClickCount + " WHERE GoodId = " + good.Id;          
            cmd.ExecuteNonQuery();
            return true;         
        }

        /**//// <summary>
        /// 返回所有商品列表
        /// </summary>
        /// <returns></returns>
        public IList<GoodInfo> SelectGoods()
        {
            string sqlText = "SELECT * FROM eShop_Goods";
            return this.SelectGoodsList(sqlText);
        }

        /**//// <summary>
        /// 返回指定分类的所属商品列表
        /// </summary>
        /// <param name="sortId"></param>
        /// <returns></returns>
        public IList<GoodInfo> SelectGoodsBySort(int sortId)
        {        
            string sqlText = "SELECT * FROM SW_Product WHERE  Hide = 0 AND ClassId IN (SELECT ClassId FROM SW_Class WHERE Parentstr LIKE '%" + sortId + "%' OR ClassId = " + sortId + ")";
            return this.SelectGoodsList(sqlText);
        }

        /**//// <summary>
        /// 返回指定排行类型的所属商品列表
        /// </summary>
        /// <param name="topNumber"></param>
        /// <param name="popName"></param>
        /// <returns></returns>
        public IList<GoodInfo> SelectGoodsByPop(int topNumber, string popName)
        {
            string sqlText = "SELECT TOP " + topNumber + " Id, dianneima, Name, Picture, Brand, GuiGe, CASE Kind WHEN 0 THEN '最新上架' WHEN 1 THEN '推荐商品' WHEN 2 THEN '特价商品' END AS Kind, Introduce, JoinDate, MarketPrice, MemberPrice, DanWei, Score, TotalHits, Stock FROM SW_Product WHERE Hide = 0 ORDER BY " + GoodInfo.PopList[popName].ToString() + " DESC";
            return this.SelectGoodsList(sqlText);
        }

        /**//// <summary>
        /// 返回指定品牌的所属商品列表
        /// </summary>
        /// <param name="brandName"></param>
        /// <returns></returns>
        public IList<GoodInfo> SelectGoodsByBrand(string brandName)
        {
            string sqlText = "SELECT * FROM SW_Product WHERE Hide = 0 AND Brand = '" + brandName + "'";
            return this.SelectGoodsList(sqlText);
        }

        /**//// <summary>
        /// 返回所有商品列表,按简要说明返回
        /// </summary>
        /// <returns></returns>
        public IList<GoodInfo> SelectGoodsBrief()
        {
            string sqlText = "SELECT Id, dianneima, Name, Picture, MarketPrice, MemberPrice, DanWei, GuiGe, Stock FROM SW_Product";
            return this.SelectGoodsBrief(sqlText);
        }

        /**//// <summary>
        /// 返回指定分区的所属商品列表,按简要说明返回
        /// </summary>
        /// <returns></returns>
        public IList<GoodInfo> SelectGoodsBriefByKind(int topNumber, string kindName)
        {
            string sqlText = "SELECT TOP " + topNumber + " Id, dianneima, Name, Picture, MarketPrice, MemberPrice, DanWei, GuiGe, Stock FROM SW_Product WHERE Hide = 0 AND Kind = " + GoodInfo.KindList[kindName].ToString() + " ORDER BY JoinDate DESC";
            return this.SelectGoodsBrief(sqlText);
        }

        /**//// <summary>
        /// 返回指定分区的所属商品列表
        /// </summary>
        /// <param name="kindName"></param>
        /// <returns></returns>
        public IList<GoodInfo> SelectGoodsListByKind(string kindName)
        {
            string sqlText = "SELECT Id, dianneima, Name, Picture, Brand, GuiGe, CASE Kind WHEN 0 THEN '最新上架' WHEN 1 THEN '推荐商品' WHEN 2 THEN '特价商品' END AS Kind, Introduce, JoinDate, MarketPrice, MemberPrice, DanWei, Score, TotalHits, Stock FROM SW_Product WHERE Hide = 0 AND (Kind = " + GoodInfo.KindList[kindName].ToString() + ") ORDER BY JoinDate DESC";
            return this.SelectGoodsList(sqlText);          
        }       

        /**//// <summary>
        /// 返回商品的详细信息
        /// </summary>
        /// <param name="goodId"></param>
        /// <returns></returns>
        public GoodInfo SelectGood(int goodId)
        {
            CmdCommand cmd = new CmdCommand();
            cmd.CommandText = "SELECT TOP 1 Id, dianneima, Name, Picture, Brand, GuiGe, CASE Kind WHEN 0 THEN '最新上架' WHEN 1 THEN '推荐商品' WHEN 2 THEN '特价商品' END AS Kind, Introduce, JoinDate, Detail, MarketPrice, MemberPrice, DanWei, Score, TotalHits, Stock FROM SW_Product WHERE Hide = 0 AND Id = " + goodId;
            SqlDataReader dr = cmd.ExecuteReader();
            GoodInfo good;
            if (dr.HasRows)
            {
                dr.Read();
                string picturePath = dr["Picture"].ToString();
                picturePath = picturePath.Substring(0, picturePath.IndexOf("|||"));
                good = new GoodInfo(Convert.ToInt32(dr["Id"]), dr["dianneima"].ToString(), dr["Name"].ToString(), picturePath, dr["Brand"].ToString(), dr["Kind"].ToString(), dr["GuiGe"].ToString(), dr["Introduce"].ToString(), dr["Detail"].ToString(), Convert.ToDecimal(dr["MarketPrice"]), Convert.ToDecimal(dr["MemberPrice"]), dr["DanWei"].ToString(), Convert.ToDecimal(dr["Score"]), Convert.ToInt32(dr["TotalHits"]), Convert.ToDateTime(dr["JoinDate"]), Convert.ToInt32(dr["Stock"]));
                dr.Close();
                cmd.Disconnection();
            }
            else
            {
                good = new GoodInfo();
            }
            return good;
        }

        /**//// <summary>
        /// 根据关键字返回商品列表
        /// </summary>
        /// <param name="queryString"></param>
        /// <returns></returns>
        public IList<GoodInfo> SelectGoodsListByKeyword(string keyword)
        {
            string sqlText = "SELECT Id, dianneima, Name, Picture, Brand, GuiGe, CASE Kind WHEN 0 THEN '最新上架' WHEN 1 THEN '推荐商品' WHEN 2 THEN '特价商品' END AS Kind, Introduce, JoinDate, MarketPrice, MemberPrice, DanWei, Score, TotalHits, Stock FROM SW_Product WHERE Hide = 0 AND Name like '%" + keyword + "%'";
            return this.SelectGoodsList(sqlText);
        }

        /**//// <summary>
        /// 新增商品
        /// </summary>
        /// <param name="good"></param>
        /// <returns></returns>
        public bool InsertGood(GoodInfo good)
        {
            CmdCommand cmd = new CmdCommand();
            cmd.CommandText = "INSERT INTO eShop_Goods (SortId, [Name], BrandId, Code, Barcode, Unit, [Standard], PicturePath, SimpleIntroduction, DetailIntroduction, MarketPrice, SellPrice, PublicTime, UpdateTime, ClickCount, ClickTime, Stock) VALUES (" + good.Sort.Id + ", '" + good.Name + "', " + good.BrandInfo.Id + ", '" + good.Code + "', '" + good.Barcode + "', '" + good.Unit + "', '" + good.Standard + "', '" + good.PicturePath + "', '" + good.SimpleIntroduction + "', '" + good.DetailIntroduction + "', " + good.MarketPrice + ", " + good.SellPrice + ", '" + good.PublicTime + "', '" + good.UpdateTime + "', " + good.ClickCount + ", '" + good.ClickTime + "', " + good.Stock + ")";
            cmd.ExecuteNonQuery();
            return true;
        }       
    }
}

在本系列的第一篇中,笨熊已经介绍过BLL层的功用,也说明了为什么许多人觉得这一层可有可无。但看近期博客园的文章,还有不少朋友对BLL的存在仍有疑问。正好今天碰到的这个问题颇具代表性,写出来让这些朋友对BLL在业务系统中的功用有个具体的感觉。
    先简要介绍一下要做的功能,在程序中,用户输入商品的商品编码,系统从数据库中提取显示此商品的详细信息(图1)。如果一个商品有多个供应商,则弹出提示框供用户选择某一供应商(图2)。

    图1


    图2

功能上来说很简单,先定义一个实体类:

Code
    public class StockTakingCardInfo
    {
        private string _GoodBarcode;
        private string _GoodBarcodeType;
        private string _GoodCode;
        private string _GoodName;
        private string _Standard;
        private string _Unit;
        private decimal _SellPrice;
        private string _CargoGroup;
        private string _CargoGroupName;
        private string _Distribution;
        private string _DistributionName;
        private string _ProducingArea;

        public string GoodBarcode
        {
            get { return _GoodBarcode; }
            set { _GoodBarcode = value; }
        }

        public string GoodBarcodeType
        {
            get { return _GoodBarcodeType; }
            set { _GoodBarcodeType = value; }
        }

        public string GoodCode
        {
            get { return _GoodCode; }
            set { _GoodCode = value; }
        }

        public string GoodName
        {
            get { return _GoodName; }
            set { _GoodName = value; }
        }

        public string Standard
        {
            get { return _Standard; }
            set { _Standard = value; }
        }

        public string Unit
        {
            get { return _Unit; }
            set { _Unit = value; }
        }

        public decimal SellPrice
        {
            get { return _SellPrice; }
            set { _SellPrice = value; }
        }

        public string CargoGroup
        {
            get { return _CargoGroup; }
            set { _CargoGroup = value; }
        }

        public string CargoGroupName
        {
            get { return _CargoGroupName; }
            set { _CargoGroupName = value; }
        }

        public string Distribution
        {
            get { return _Distribution; }
            set { _Distribution = value; }
        }

        public string DistributionName
        {
            get { return _DistributionName; }
            set { _DistributionName = value; }
        }

        public string ProducingArea
        {
            get { return _ProducingArea; }
            set { _ProducingArea = value; }
        }
    }
  然后数据层负责提取数据:      逻辑层没什么用,直接调用DAL的方法得到数据并返回。

Code
public IList<StockTakingCardInfo> GetStockTakingCards(string code)   
{
     /***** 从数据库中提取 *****/
     string goodCode;

     CmdCommand cmd = new CmdCommand();
     cmd.CommandText = "SELECT DPTM FROM commod_xstm1 WHERE xstm = '" + code + "'";
     OleDbDataReader drXstm = cmd.ExecuteReader();
     if (drXstm.HasRows)
     {   //输入的是销售条码
          drXstm.Read();
         goodCode = drXstm["DPTM"].ToString();
         drXstm.Close();
     }
     else
     {   //输入的是货号
          goodCode = code;
     }
     cmd.Disconnection();

     cmd.CommandText = "";
     OleDbDataReader dr = cmd.ExecuteReader();

     IList<StockTakingCardInfo> stockTakingCards = new List<StockTakingCardInfo>();
     if (dr.HasRows)
     {   //
         while (dr.Read())
         {
             StockTakingCardInfo stockTakingCard = new StockTakingCardInfo();
             stockTakingCard.GoodBarcode = dr["销售条码"].ToString();
             stockTakingCard.GoodBarcodeType = dr["销售条码类型"].ToString();
             stockTakingCard.GoodCode = dr["商品编码"].ToString();
             stockTakingCard.GoodName = dr["商品名称"].ToString();
             stockTakingCard.Standard = dr["规格"].ToString();
             stockTakingCard.Unit = dr["单位"].ToString();
             stockTakingCard.SellPrice = Convert.ToDecimal(dr["零售价"]);
             stockTakingCard.CargoGroup = dr["柜组"].ToString(); ;
             stockTakingCard.CargoGroupName = dr["柜组名称"].ToString();
             stockTakingCard.Distribution = dr["供货商"].ToString();
             stockTakingCard.DistributionName = dr["供货商名称"].ToString();
             stockTakingCard.ProducingArea = dr["产地"].ToString();
             stockTakingCards.Add(stockTakingCard);
          }
          dr.Close();
          cmd.Disconnection();
    }
    return stockTakingardings
}

    表现层(这里是WinForm窗体)调用逻辑层方法得到数据,并判断数据条目,如果是一条,则直接显示;如果是多条,则弹出提示框让用户选择,选好后再显示(代码略)。

    编写好后运行,代码没有问题,但是在批量测试时发现了几种特殊情况。

    1、一些商品没有提供条码(如蔬菜、水果等散装商品),因此需要根据商品编号生成条码。

    2、一些商品除了本身的条码外,在商品的包装箱上还有条码(比如啤酒、露露、牛奶等,瓶上的条码和箱上的条码是不一样的。因此买一箱时只需扫描包装箱上的条码即可,而不用拆箱扫描里面的商品)。

    3、一些商品只有外包装箱上有条码,商品本身却没有条码,但在销售时还需要拆开来按单个商品销售,这时也要根据商品编码生成条形码。

    考虑这三种情况,那么从数据库中查询时,返回的结果有以下几种(按条码、条码类型、编码、名称、供应商显示,其它列略。条码类型列,A表示条码为商品本身条码,B表示条码为包装箱条码)。

    (1)一般情况(结果返回1 - n条数据)

    692590758024,A,186202,舒尔但莫代尔女单衣,唐山XX有限公司

    (2)商品本身没有条码(结果返回1 - n条数据)

    NULL,NULL,80604,散大米,唐山XX有限公司

    NULL,NULL,80604,散大米,天津市XX加工厂

    (3)商品本身有条码,包装箱还有条码(结果返回n条数据)

    690180818886,A,104992,露露,河北XX股份有限公司

    690180818882,B,104991,露露,河北XX股份有限公司

    690180818886,A,104991,露露,锦江XX有限公司

    690180818882,B,104991,露露,锦江XX有限公司

    (4)商品本身无条码,包装箱有条码(结果返回1 - n条数据)

    692590751824,B,101974,双汇王中王,唐山XX有限公司

    692590751824,B,101974,双汇王中王,河北XX有限公司

    这样汇总整理一下,对于重数据库中查询返回的结果,要做如下处理:首先判断条码是否为空,如果空则生成条码。确认条码不为空后,判断条码类型中是否全是B。如果有A有B,则进行筛选,保留类型为A的条目。如果全是B,则将结果中的条码全部用自动生成条码替换。

    确定了处理过程以后,决定需要将此过程放在何处。首先考虑的是数据层,如果能直接提取出所需结果那是最好的,但是有几点原因决定了不能放在数据层:

    (1)数据层的这个方法提供的数据具有通用性,逻辑层中不光一个方法对其进行调用,更改了返回结果会造成逻辑层的其它方法无法使用此结果。

    (2)这个方法的查询语句非常简单,如果增加了此过程,则SQL语句的编写难度会加大,而且很难用1条语句处理这几种情况,必须实现的话,则此方法的SQL查询语句需要改为3句,还要提前执行一条SQL判断语句来决定使用那个SQL查询语句。成倍增加了编写难度与执行时间。

    所以数据层不是进行处理的好地方。

    再看看表示层,如果放在了表示层,则需要在主窗口(图1)进行判断,对数据进行处理,这样将逻辑处理代码与显示代码混合在一起,不但乱,而且加大了编码量,因为每个逻辑分值里面都要编写相应的显示代码。所以放在表示层也不是个好主意(呵呵,一开始感觉简单,真的想不建逻辑层直接合并在表示层中着)。

    因此,最适合放置此处理过程的地方,就是业务逻辑层了。而且此处的工作就是业务逻辑的处理,即使没有上述问题,也应该放与此层。

Code
public IList<StockTakingCardInfo> GetStockTakingCards(string code)
{     
      IList<StockTakingCardInfo> stockTakingCards = DAL.GetStockTakingCards()

      /***** 业务逻辑整理(还未编写空白条码处理部分) *****/
      //判断是否有A码
       bool hasA = false;
      foreach (StockTakingCardInfo myStockTakingCard in stockTakingCards)
      {
          if (myStockTakingCard.GoodBarcodeType == "A")
          {
                hasA = true;
                break;
          }
      }

      //根据是否有A吗处理
       IList<StockTakingCardInfo> reStockTakingCards = new List<StockTakingCardInfo>();
      if (hasA)
      {   //
          foreach (StockTakingCardInfo reStockTakingCard in stockTakingCards)
          {
                if (reStockTakingCard.GoodBarcodeType == "A")
                {
                    reStockTakingCards.Add(reStockTakingCard);
                }
          }
       }
       else
       {   //没有A码
             //将所有的销售条码重新生成
             foreach (StockTakingCardInfo reStockTaking in stockTakingCards)
            {
                if (reStockTaking.GoodCode.Length == 5)
                {
                    reStockTaking.GoodBarcode = "2000000" + reStockTaking.GoodCode;
                }
                else if (reStockTaking.GoodCode.Length == 6)
                {
                    reStockTaking.GoodBarcode = "200000" + reStockTaking.GoodCode;
                }
                reStockTakingCards.Add(reStockTaking);
            }
        }

        /***** 返回处理后结果 ******/
        return reStockTakingCards;
}


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/hanhang2008/archive/2009/02/24/3933002.aspx


 

原文地址:https://www.cnblogs.com/tangself/p/1612893.html