分享一个进销存项目(多层架构)

    花了点时间完成了一个进销存的项目,个人觉得对逻辑分析,架构思想都有一定的体会,故拿出来和大家分享下,首先介绍下项目的功能吧,主要功能有采购管理,销售管理,库存管理,人员信息管理,系统管理等,不过写这篇博客的主要目的并不是讲解如何实现项目功能,而是分享下该项目的架构思想、实现步骤和碰到的一些问题,分享下心得体会罢了......

 

    数据库设计

 分享一个进销存项目(多层架构)_第1张图片

 

   下面进入主题,首先贴上项目的主界面,由于对界面要求不是很高,所以界面相对来说比较简单,甚至可是说是简陋了,,呵呵,不过重点不在界面上,勇敢的直接上图:

分享一个进销存项目(多层架构)_第2张图片 

架构:

首先说说项目架构吧,,贴上项目模块图:

分享一个进销存项目(多层架构)_第3张图片

从图中可知,项目总共分10个模块,采用三层架构+WCF+反射+抽象工厂来搭建,其中还加入了单例模式缓存机制。下面分别说说每个模块的作用:

Insigma.PSI.UI:WinForm界面层

Insigma.PSI.Model:模型层,数据实体   
Insigma.PSI.IBLLService:业务逻辑接口层,是定义逻辑层的契约层
Insigma.PSI.BLLService: 业务逻辑实现层,是对接口的实现
Insigma.PSI.IDAL:数据访问接口层,是定义访问层的契约层
Insigma.PSI.SqlServerDAL:数据访问实现层,实现访问接口层,主要是针对SQL数据库
Insigma.PSI.OracleDAL:数据访问实现层,实现访问接口层,主要是针对Oracle数据库  
Insigma.PSI.Utility:工具层,主要存放了Oracle数据库,SQL数据库的数据库操作类

Insigma.PSI.DBFactory:抽象工厂层,主要为考虑可以使项目可以适用Oracle数据库和SQL数据库,就搭建了一个抽象工厂层,分别在Oracle数据库层和SQL数据库层建立一个 工 厂来实现该抽象工厂

C:\..\WcfWebRelease\:web层,主要用来发布WCF服务


项目: 
 简单的分析完了框架的每个模块的作用,然后讲讲项目,由于篇幅问题,无法讲解项目的所有功能,所以在这里就拿出一个模块来讲解下,我选择了库存模块

分享一个进销存项目(多层架构)_第4张图片 

从图中可知,库存模块主要实现四个模块的功能,即 库存列表、出库/入库、库存明细、库存警报

首先看看库存列表,上图:

分享一个进销存项目(多层架构)_第5张图片 

库存列表的功能比较简单,只是实现了仓库的增、删、改、查,由于功能比较简单,也就不多做讲解了....

 

再贴上出库/入库模块图:

分享一个进销存项目(多层架构)_第6张图片
 

这块应该算是库存模块中的核心模块了,因为该块的业务逻辑比较多,比较繁琐,大致讲讲业务逻辑吧,大致的逻辑为:出库单/出库单-->填写订单-->出库/入库-->修改库存信息,按照这个顺序来完成入库出库,顺序不能颠倒,同时还要实现订单的删除,修改,在修改库存信息时由于表和表之间有很多的外键关系,所以要同时删除多张表中含有删除信息的内容,实现联级删除,由于这里考虑到要保证结果的一致性,所以用事务来实现。再看看界面,为了保证用户按照这个逻辑顺序操作,首先我添加了一对单选框(出库单,入库单),当单选框都没有选中时,保存订单按钮、出库、入库按钮都是不可用状态,当用户选择了订单类型之后,保存订单按钮转为可用状态,需要用户填写订单信息,填写好对应信息之后保存订单,保存成功之后列表框中出现刚生产的订单,同时订单号是自动来生成的,入库单是“S_I”开头,出库单是"S_O"开头,后面五位数字为随机生成,这样生成的订单号易于区别,也避免了订单号的重复,然后根据用户选择的订单类型,对应的按钮转为可用(出库单-->出库按钮) ,这样就保证了用户始终是按照这个顺序来操作,同时订单列表框下有删除订单按钮,点击可以删除选中按钮。

 

下面那出库为例,看看如何实现入库:

分享一个进销存项目(多层架构)_第7张图片

点击【入库】按钮,弹出商品选择窗口,窗口中显示了订单号和仓库编号,同时还有一个【选择】按钮,通过该按钮选择商品

分享一个进销存项目(多层架构)_第8张图片 

点击【选择】按钮,弹出商品列表窗口,由于我这里商品记录比较少,只有两条,我们就选第二条吧,点击【确定】按钮

分享一个进销存项目(多层架构)_第9张图片

这时的"入库信息"窗口中就填写上了刚才选择商品的信息,然后只要在填写上入库数量,点击【确定】就完成了入库操作了,如图:

分享一个进销存项目(多层架构)_第10张图片

 出库的思路和入库一样,也是先选择出库单,然后填写出库单信息并保存订单,然后再给订单上添加商品,过程也是通过选择实现,由于类同,就不赘述了...

 

 然后是库存明细模块,上图:

 分享一个进销存项目(多层架构)_第11张图片

该模块主要实现的功能是对存放在各个仓库中的商品进行清点,点击商品的行头就显示出该商品的信息:编号、数量、盘点日期、存放仓库;填写好盘点的数量之后单击【保存】按钮,实现保存盘点数据,这个模块也没什么难点,唯一复杂点的就是因为同样的货品可以存放到不同的仓库中,所以点击时要搜索出该商品所在的所有仓库编号,并在“存放仓库”列表中显示。

 

最后是库存警报模块,上图:

分享一个进销存项目(多层架构)_第12张图片 

 该模块主要来设置库存上限和库存下限,实现当库存中存放的商品的数量超过库存上限或者低于库存下限时,弹出警告框提醒。

 这样,我们就简单的把项目的功能叙述了下,当然还有一些细节的功能,这里就不再罗列了.....

 

代码:

由于代码端模块较多,我们就按照程序执行调用的层次关系来罗列.这样相对来说看起来思路比较清晰......Model层只是定义了需要用到的实体而已,不再赘述了.....

1>.Insigma.PSI.IBLLService层

 分享一个进销存项目(多层架构)_第13张图片

 可以看到有很多的功能类,无法全部讲解,所以就挑选一块,后面的模块层中也着重讲解挑选的这一块,我挑选了库存管理模块,即IStoreManageBLLService,贴上该类的代码:

ExpandedBlockStart.gif IStoreManageBLLService
 1  using Insigma.PSI.Model;
 2  using System.ServiceModel;
 3 
 4  namespace Insigma.PSI.IBLLService
 5 {
 6     [ServiceContract]
 7      public  interface IStoreManageBLLService
 8     {
 9         [OperationContract]
10          // 获取所有仓库
11          List GetStores( string sqlWhere);
12 
13         [OperationContract]
14          // 获取所有仓库编号
15          List< string> GetAllStoreCode();
16 
17         [OperationContract]
18          // 添加仓库
19           bool AddStore(StoreHouse store);
20 
21         [OperationContract]
22          // 修改仓库
23           bool UpdateStore(StoreHouse store);
24 
25         [OperationContract]
26          // 删除仓库
27           bool DeleteStore( string code);
28 
29         [OperationContract]
30          // 修改库存明细
31           bool UpdateStoreDetails(GoodsDetailsModel goodDetail);
32 
33         [OperationContract]
34          // 获取商品明细
35          List GetStoreDetails( string sqlWhere);
36 
37         [OperationContract]
38          // 修改商品数量
39           bool UpdateGoodNum(StoreDetailsModel storeDetail);
40     }
41 }

由于我们要用到WCF来发布服务,所以这里的每个方法都添加了WCF特性,主要功能是获取仓库、获取仓库编号、对仓库的增、删、改、查;由于出库入库对商品的数量有影响,又由于不存在同一张表格中,所以我们专门定义了一个方法来实现修改商品数量;然后出库入库对应一张库存明细表,执行操作之后要修改该表,记录信息,同时也可以获取该表信息,定义的方法就那么多了.....

 

2>Insigma.PSI.BLLService

 分享一个进销存项目(多层架构)_第14张图片

这层是对上面提及的接口层的实现,但除了实现之外,我还在这里加了一个反射机制,在这层按照原来三层的思路是要调用DAL层来实现对数据库的操作,但因为考虑到要适用于不同的数据库,所以我们不知道要调用哪个数据库层的DAL来操作,这就是这个反射存在的意义了,我们可以通过反射来读取配置文件中定义的数据库信息,从而创建对应数据库类的实例来操作....先贴上StoreManageBLLService的代码,之后再接着讲解,看到了代码思路会清晰点:

ExpandedBlockStart.gif StoreManageBLLService
  1  using Insigma.PSI.Model;
  2  using Insigma.PSI.IBLLService;
  3  using Insigma.PSI.IDAL;
  4  using Insigma.PSI.DBFactory;
  5 
  6  namespace Insigma.PSI.BLL
  7 {
  8      public  class StoreManageBLLService:IStoreManageBLLService
  9     {
 10          // 通过反射获取对应的抽象产品
 11           private IStoreManageDAL Ismd = Refection.GetFactory().StoreManageProduct;
 12 
 13          ///  
 14           ///  获取StoresHost表数据
 15           ///  

 16           ///  
 17           ///  
 18           public List GetStores( string sqlWhere)
 19         {
 20              return Ismd.GetStores(sqlWhere);
 21         }
 22 
 23          ///  
 24           ///  添加仓库
 25           ///  

 26           ///  
 27           ///  
 28           public  bool AddStore(StoreHouse store)
 29         {
 30              int result = Ismd.AddStore(store);
 31              if (result >  0)
 32             {
 33                  return  true;
 34             }
 35              else
 36             {
 37                  return  false;
 38             }
 39         }
 40 
 41          ///  
 42           ///  修改仓库
 43           ///  

 44           ///  
 45           ///  
 46           public  bool UpdateStore(StoreHouse store)
 47         {
 48              int result = Ismd.UpdateStore(store);
 49 
 50              if (result >  0)
 51             {
 52                  return  true;
 53             }
 54              else
 55             {
 56                  return  false;
 57             }
 58         }
 59 
 60          ///  
 61           ///  删除仓库
 62           ///  

 63           ///  
 64           ///  
 65           public  bool DeleteStore( string code)
 66         {
 67              int result = Ismd.DeleteStore(code);
 68 
 69              if (result >  0)
 70             {
 71                  return  true;
 72             }
 73              else
 74             {
 75                  return  false;
 76             }
 77         }
 78 
 79          ///  
 80           ///  获取所有仓库编号
 81           ///  

 82           ///  
 83           public List< string> GetAllStoreCode()
 84         {
 85              return Ismd.GetAllStoreCode();
 86         }
 87 
 88          ///  
 89           ///  修改商品明细的数量信息
 90           ///  

 91           ///  
 92           ///  
 93           public  bool UpdateStoreDetails(GoodsDetailsModel goodDetail)
 94         {
 95              int result = Ismd.UpdateStoreDetails(goodDetail);
 96 
 97              if (result >  0)
 98             {
 99                  return  true;
100             }
101              else
102             {
103                  return  false;
104             }
105         }
106 
107          ///  
108           ///  获取所有库存明细
109           ///  

110           ///  
111           ///  
112           public List GetStoreDetails( string sqlWhere)
113         {
114              return Ismd.GetStoreDetails(sqlWhere);
115         }
116 
117          ///  
118           ///  修改库存明细的盘点数量
119           ///  

120           ///  
121           ///  
122           public  bool UpdateGoodNum(StoreDetailsModel storeDetail)
123         {
124              int result = Ismd.UpdateGoodNum(storeDetail);
125 
126              if (result >  0)
127             {
128                  return  true;
129             }
130              else
131             {
132                  return  false;
133             }
134         }
135     }
136 } 

从代码中可以看到我们是通过反射来获取DAL的实例的,然后再调用该实例的方法,那么反射里是怎么实现的呢?贴上代码:

ExpandedBlockStart.gif 反射
 1  using System.Configuration;
 2  using System.Reflection;
 3  using Insigma.PSI.DBFactory;
 4 
 5  namespace Insigma.PSI.BLL
 6 {
 7      public  class Refection
 8     {
 9          // 定义一个抽象工厂类的对象实例 用于保存作用
10           private  static AbstractFactory DataAccess =  null;
11 
12          // 获取配置文件中定义的数据库信息
13           private  static  string path = ConfigurationManager.AppSettings[ " DllName "];
14          private  static  string className = ConfigurationManager.AppSettings[ " ClassName "];
15 
16          // 单例模式-----因为每次加载该程序集都会去调用相同的DAL 
17           //              如果已经加载过了 就不要再次加载 提高性能
18           static Refection()
19         { 
20              // 反射----因为不确定要获取什么类型的DAL 不知道是Oracle还是SQL等等 所以这里添加一个反射机制
21               string _classname = path +  " . " + className;
22            DataAccess = (AbstractFactory)Assembly.Load(path).CreateInstance(_classname);
23         }
24 
25          // 获取对应数据库的具体工厂
26           public  static AbstractFactory GetFactory()
27         {
28              return DataAccess;
29         }
30     }
31 }

从代码可知,我们获取了配置文件中定义的程序集以及数据库类的信息,通过加载该程序集来获取该数据库类的实例,同时,添加了单例模式,因为考虑到每次加载该类库的时候都要来获取一次数据库类的实例,但是该数据库类只要配置文件定义不变他就不会变,所以不用每次都去创建实例 ,只需要创建一次就可以了,提高性能,所以就是单例模式的作用了,这里首先定义了一个AbstractFactory类型(后面会讲到)变量来接受创建好的实例,该变量也是static类型的,保证唯一性,我这里是将构造函数改为static的方式,然后在静态构造函数中实例化数据库类,并赋值给定义好的变量,因为静态构造函数只会执行一次,之后就不会执行了,这就保证了只实例一次,然后定义一个GetFactory()方法来返回数据库实例,这样就达到了"单例"的效果了,当然单例模式还有一种思路是将构造函数设为private,也同样定义一个静态变量和静态方法来创建实例,同样保证了"单例"。

然后看看配置文件中定义的数据库信息:

ExpandedBlockStart.gif web.config
1 
2   
3   
4     
5     " DLLName " value= " Insigma.PSI.OracleDAL "/>
6     
7     " ClassName " value= " OracleFactory "/>
8   

可以看到配置文件中定义了程序集的名称,数据库类的名称,但其实这个数据库类就是工厂类的名称。反射中提到了AbstractFactory类型,这是抽象工厂模块中的内容,那么为什么要有这个抽象工厂呢??存在的意义是什么呢?我们就来看看抽象工厂,我的思路:定义一个抽象工厂,里面是一些抽象产品,对应于IDAL中定义的类型的产品,然后在每个数据库类库中定义一个具体工厂,该具体工厂继承抽象工厂,重写抽象产品,返回具体产品,即对IDAL接口类的实现类(DAL)的对应实例,那你会想到了,如没有工厂我们照样可以实现用反射类获取实例的呀,但你想想这时候获取到得实例是什么?它就是我们工厂类中的单个产品而已,当我们的产品有很多的时候怎么办呢,一个DAL类库中的每个类都是一个产品,那难道我们把每个产品都在配置文件中去定义,然后用到的时候通过反射去获取么??显然不可能,所以我们加了抽象工厂模式了,在DAL类库中有一个具体的工厂类,我们所有的产品都由该工厂来创建,那我们获取的是时候只需要获取工厂实例,然后通过该实例来创建我们需要的产品就可以了......

 

3>Insigma.PSI.IDAL层

分享一个进销存项目(多层架构)_第15张图片 

同样的拿IStoreManageDAL类讲解,上代码:

ExpandedBlockStart.gif IStoreManageDAL
 1  using Insigma.PSI.Model;
 2 
 3  namespace Insigma.PSI.IDAL
 4 {
 5      public  interface IStoreManageDAL
 6     {
 7         List GetStores( string sqlWhere);
 8         List< string> GetAllStoreCode();
 9          int AddStore(StoreHouse store);
10          int UpdateStore(StoreHouse store);
11          int DeleteStore( string code);
12          int UpdateStoreDetails(GoodsDetailsModel goodDetail);
13         List GetStoreDetails( string sqlWhere);
14          int UpdateGoodNum(StoreDetailsModel storeDetail);
15     }
16 }

这也是一个接口类,接口的作用就是规范行为,定义一种契约,继承该接口的类都必须实现契约,所以这里主要来约束DAL实现层的行为


4>.Insigma.PSI.DBFactory

 

抽象工厂模块中就一个抽象工厂类,就直接贴上类代码:

ExpandedBlockStart.gif AbstractFactory
 1  using Insigma.PSI.IDAL;
 2 
 3  namespace Insigma.PSI.DBFactory
 4 {
 5      // 定义一个抽象工厂 便于适用于不同的数据库  每个数据库都可以建立一个具体工厂 继承该工厂 
 6       public  abstract  class AbstractFactory
 7     {
 8         // 抽象产品类---对应于IDAL层
 9          public  abstract ISysUserDAL SysUserProduct {  get; }
10         public  abstract IStoreManageDAL StoreManageProduct {  get;}
11         public  abstract IDepartmentDAL DepartmentProduct {  get; }
12         public  abstract IHandleGeneralsDAL HandleGeneralsProduct {  get; }
13         public  abstract IGoodsDAL GoodsProduct {  get; }
14         public  abstract IHandle_GoodDetailsDAL Handle_GoodDetailsProduct {  get; }
15         public  abstract IStaffsDAL StaffsProduct {  get; }
16         public  abstract ICompanyDAL CompanyProduct {  get; }
17     }
18 }

在抽象类中为每个在IDAL中定义的类型都对应的建立了一个属性(也可以定义成方法),因为加了抽象工厂之后,IDAL中的每个类都是一个抽象产品类,是产品类的基类,而DAL中的每个类都是该基类的派生类,都实现了基类中定义的方法,最终返回的具体产品的类型都为抽象产品类型(子类赋值给基类)。

 

5>.Insigma.PSI.OracleDAL

分享一个进销存项目(多层架构)_第16张图片  
有一个工厂类,用于创建具体产品实例,贴上工厂类代码:
ExpandedBlockStart.gif Oracle工厂
  1  using Insigma.PSI.DBFactory;
  2  using System.Web;
  3  using Insigma.PSI.IDAL;
  4 
  5  namespace Insigma.PSI.OracleDAL
  6 {
  7      // 具体工厂:Oracle
  8       public  class OracleFactory:AbstractFactory
  9     {
 10          // 定义DAL类型变量,用于保存
 11           private SysUserDAL sud =  null;
 12          private DepartmentDAL dp =  null;
 13          private StoreManageDAL sm =  null;
 14          private HandleGeneralsDAL hg =  null;
 15          private GoodsDAL gd =  null;
 16          private Handle_GoodDetailsDAL h_g =  null;
 17          private StaffsDAL sd =  null;
 18          private CompanyDAL cd =  null;
 19 
 20 
 21          // 具体产品:SysUserDAL
 22           public  override ISysUserDAL SysUserProduct
 23         {
 24              get 
 25             {
 26                  // 首先搜索缓存 看看是否有对应的产品
 27                   if (HttpRuntime.Cache.Get( " sysuserdal ") ==  null)
 28                 {
 29                     sud =  new SysUserDAL();
 30                      // 添加缓存机制-----将sud添加到缓存 因为每个用户调用都会去实例化同一个对象 加入缓存机制 
 31                       //                  如果要获取相同类型的对象 则可以直接从缓存中获取 提高性能
 32                      HttpRuntime.Cache.Insert( " sysuserdal ", sud, null,DateTime.Now.AddMinutes( 5),TimeSpan.Zero);
 33                 }
 34                 
 35                  // 返回存储在缓存中的值
 36                   return (SysUserDAL)HttpRuntime.Cache.Get( " sysuserdal ");
 37             }
 38         }
 39 
 40          // 具体产品:DepartmentDAL
 41           public  override IDepartmentDAL DepartmentProduct
 42         {
 43              get
 44             {
 45                  // 首先搜索缓存 看看是否有对应的产品
 46                   if (HttpRuntime.Cache.Get( " departmentdal ") ==  null)
 47                 {
 48                     dp =  new DepartmentDAL();
 49                      // 添加缓存机制-----将sud添加到缓存 因为每个用户调用都会去实例化同一个对象 加入缓存机制 
 50                       //                  如果要获取相同类型的对象 则可以直接从缓存中获取 提高性能
 51                      HttpRuntime.Cache.Insert( " departmentdal ", dp,  null, DateTime.Now.AddMinutes( 5), TimeSpan.Zero);
 52                 }
 53 
 54                  // 返回存储在缓存中的值
 55                   return (DepartmentDAL)HttpRuntime.Cache.Get( " departmentdal ");
 56 
 57             }
 58         }
 59 
 60          // 具体产品:StoreManageDAL
 61           public  override IStoreManageDAL StoreManageProduct
 62         {
 63              get 
 64             {
 65                  // 首先搜索缓存 看看是否有对应的产品
 66                   if (HttpRuntime.Cache.Get( " storemanagedal ") ==  null)
 67                 {
 68                     sm =  new StoreManageDAL();
 69                      // 添加缓存机制-----将sud添加到缓存 因为每个用户调用都会去实例化同一个对象 加入缓存机制 
 70                       //                  如果要获取相同类型的对象 则可以直接从缓存中获取 提高性能
 71                      HttpRuntime.Cache.Insert( " storemanagedal ", sm,  null, DateTime.Now.AddMinutes( 5), TimeSpan.Zero);
 72                 }
 73 
 74                  // 返回存储在缓存中的值
 75                   return (StoreManageDAL)HttpRuntime.Cache.Get( " storemanagedal ");
 76             }
 77         }
 78 
 79          // 具体产品:HandleGeneralsDAL
 80           public  override IHandleGeneralsDAL HandleGeneralsProduct
 81         {
 82              get 
 83             {
 84                  if(HttpRuntime.Cache.Get( " handlegenerals ")== null)
 85                 {
 86                     hg =  new HandleGeneralsDAL();
 87                     HttpRuntime.Cache.Insert( " handlegenerals ", hg);
 88                 }
 89                  return (HandleGeneralsDAL)HttpRuntime.Cache.Get( " handlegenerals ");
 90             }
 91         }
 92 
 93          // 具体产品:GoodsDAL 
 94           public  override IGoodsDAL GoodsProduct
 95         {
 96              get 
 97             {
 98                  if (HttpRuntime.Cache.Get( " good ") ==  null)
 99                 {
100                     gd =  new GoodsDAL();
101                     HttpRuntime.Cache.Insert( " good ", gd);
102                 }
103                  return (GoodsDAL)HttpRuntime.Cache.Get( " good ");
104             }
105         }
106 
107          // 具体产品:Handle_GoodDetailsDAL
108           public  override IHandle_GoodDetailsDAL Handle_GoodDetailsProduct
109         {
110              get
111             {
112                  if (HttpRuntime.Cache.Get( " hand_good ") ==  null)
113                 {
114                     h_g =  new Handle_GoodDetailsDAL();
115                     HttpRuntime.Cache.Insert( " hand_good ", h_g);
116                 }
117                  return (Handle_GoodDetailsDAL)HttpRuntime.Cache.Get( " hand_good ");
118             }
119         }
120 
121          // 具体产品:StaffsDAL
122           public  override IStaffsDAL StaffsProduct
123         {
124              get 
125             {
126                  if (HttpRuntime.Cache.Get( " staff ") ==  null)
127                 {
128                     sd =  new StaffsDAL();
129                     HttpRuntime.Cache.Insert( " staff ", sd);
130                 }
131                  return (StaffsDAL)HttpRuntime.Cache.Get( " staff ");
132             }
133         }
134 
135          // 具体产品:CompanyDAL
136           public  override ICompanyDAL CompanyProduct
137         {
138              get
139             {
140                  if (HttpRuntime.Cache.Get( " company ") ==  null)
141                 {
142                     cd =  new CompanyDAL();
143                     HttpRuntime.Cache.Insert( " company ", cd);
144                 }
145                  return (CompanyDAL)HttpRuntime.Cache.Get( " company ");
146             }
147         }
148 
149     }
150 }

在创建具体产品实例的时候我们还用到了缓存机制,每次使用产品功能的时候都回去实例化一个具体的产品返回,所以多次使用同一产品的时候就做了很多无谓的实例化,所以我加了一层缓存,用来将产品的实例缓存起来,下次再需要该产品时直接从缓存中读取就可以了,提高了性能。

我这里用的是Runtime.Cache.Get()来获取存,Runtime.Cache.Insert()来添加缓存。

当然还有一种方法是用HttpContext.Current.Cache.Get()来取,HttpContext.Current.Cache.Insert()来添加缓存。

至于这两者的区别,要涉及到asp.net生命周期和其他方面的一些内容了,这里就不解析了.....

 

SqlDAL中的内容和Oracle类似,也是建立了一个工厂创建具体产品,同样继承并实现了IDAL中定义的方法,这里也不罗列了......

 

到这里抽象工厂的角色都出现了,那么来总结下:

抽象工厂:AbstractFactory类

抽象产品:IDAL类库中每个类

具体工厂:1.OracleFactory  2.SqlFactory

具体产品:DAL层中定义的每个类

 

6>.WCF

分享一个进销存项目(多层架构)_第17张图片

我们仍然拿StoreManageBLL.svc来讲解,贴上代码:

ExpandedBlockStart.gif StoreManageBLLService
<%@ ServiceHost Language= " C# " Debug= " true " Service= " Insigma.PSI.BLL.StoreManageBLLService " %>

 定义了程序集的名称和服务类名,然后在web.config中配置服信息,上代码:

ExpandedBlockStart.gif web.config

    " true " targetFramework= " 4.0 " />
  

  
    
      
        
          
          " true "/>
          
          " true "/>
        

      

    

    " true " />
  

  
    " true "/>
  

 

7>.Insigma.PSI.UI

 

分享一个进销存项目(多层架构)_第18张图片
 

 

服务配置好了,那我们客户端怎么调用呢??所以我们需要在客户端的app.config配置文件中添加终结点信息,上代码:

ExpandedBlockStart.gif app.config
 1 
 2     
 3         
 4             " http://localhost:33864/WcfWebRelease/SysUserBLL.svc "
 5                 binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.ISysUserBLLSerivce "
 6                 name= " SysUserService " />
 7             " http://localhost:33864/WcfWebRelease/DepartmentBLL.svc "
 8                 binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IDepartmentBLLService "
 9                 name= " DepartmentBLLService " />
10             " http://localhost:33864/WcfWebRelease/StoreManageBLL.svc "
11                 binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IStoreManageBLLService "
12                 name= " StoreManageBLLService " />
13           " http://localhost:33864/WcfWebRelease/HandleGeneralsBLL.svc "
14                binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IHandleGeneralsBLLService "
15                name= " HandleGeneralsBLLService " />
16           " http://localhost:33864/WcfWebRelease/GoodsBLL.svc "
17               binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IGoodsBLLService "
18               name= " GoodsBLLService " />
19           " http://localhost:33864/WcfWebRelease/Handle_GoodDetails.svc "
20              binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IHandle_GoodDetailsBLLService "
21              name= " Handle_GoodDetailsBLLService " />
22           " http://localhost:33864/WcfWebRelease/StaffsBLL.svc "
23              binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IStaffsBLLService "
24              name= " StaffsBLLService " />
25           " http://localhost:33864/WcfWebRelease/CompanyBLL.svc "
26              binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.ICompanyBLLService "
27              name= " CompanyBLLService " />
28         

29     
  

同时,我在客户端专门定义了一个类用来创建wcf代理,返回BLL实例,上代码:

ExpandedBlockStart.gif 代理类
  1  namespace Insigma.PSI.UI
  2 {
  3      public  class GetServiceFromWcf
  4     {
  5 
  6          private  static ISysUserBLLSerivce _SysUserBLLSerivceProxy;
  7          private  static IDepartmentBLLService _DepartmentBLLService;
  8          private  static IStoreManageBLLService _StoreManageBLLService;
  9          private  static IHandleGeneralsBLLService _HandleGeneralsBLLService;
 10          private  static IGoodsBLLService _GoodsBLLService;
 11          private  static IHandle_GoodDetailsBLLService _Handle_GoodDetailsBLLService;
 12          private  static IStaffsBLLService _StaffsBLLService;
 13          private  static ICompanyBLLService _CompanyBLLService;
 14 
 15 
 16          // 获取SysUser代理
 17           public  static ISysUserBLLSerivce GetSysUserBLLSerivceProxy()
 18         {
 19              if (_SysUserBLLSerivceProxy ==  null)
 20             {
 21                 ChannelFactory channelFactory =  new ChannelFactory( " SysUserService ");
 22                 _SysUserBLLSerivceProxy = channelFactory.CreateChannel();
 23             }
 24              return _SysUserBLLSerivceProxy;
 25         }
 26 
 27          // 获取Department代理
 28           public  static IDepartmentBLLService GetDepartmentBLLServiceProxy()
 29         {
 30              if (_DepartmentBLLService ==  null)
 31             {
 32                 ChannelFactory channelFactory =  new ChannelFactory( " DepartmentBLLService ");
 33                 _DepartmentBLLService = channelFactory.CreateChannel();
 34             }
 35              return _DepartmentBLLService;
 36         }
 37 
 38          // 获取StoreHost代理
 39           public  static IStoreManageBLLService GetStoreManageBLLServiceProxy()
 40         {
 41              if (_StoreManageBLLService ==  null)
 42             {
 43                 ChannelFactory channelFactory =  new ChannelFactory( " StoreManageBLLService ");
 44                 _StoreManageBLLService = channelFactory.CreateChannel();
 45             }
 46              return _StoreManageBLLService;
 47         }
 48 
 49          // 获取HandleGenerals代理
 50           public  static IHandleGeneralsBLLService GetHandleGeneralsBLLServiceProxy()
 51         {
 52              if (_HandleGeneralsBLLService ==  null)
 53             {
 54                 ChannelFactory channelFactory =  new ChannelFactory( " HandleGeneralsBLLService ");
 55                 _HandleGeneralsBLLService = channelFactory.CreateChannel();
 56             }
 57              return _HandleGeneralsBLLService;
 58         }
 59 
 60          // 获取Goods代理
 61           public  static IGoodsBLLService GetGoodsBLLService()
 62         {
 63              if (_GoodsBLLService ==  null)
 64             {
 65                 ChannelFactory channelFactory =  new ChannelFactory( " GoodsBLLService ");
 66                 _GoodsBLLService = channelFactory.CreateChannel();
 67             }
 68              return _GoodsBLLService;
 69         }
 70 
 71          // 获取Handle_GoodDetails代理
 72           public  static IHandle_GoodDetailsBLLService GetHandle_GoodDetails()
 73         {
 74              if (_Handle_GoodDetailsBLLService ==  null)
 75             {
 76                 ChannelFactory channelFactory =  new ChannelFactory( " Handle_GoodDetailsBLLService ");
 77                 _Handle_GoodDetailsBLLService = channelFactory.CreateChannel();
 78             }
 79              return _Handle_GoodDetailsBLLService;
 80         }
 81 
 82          // 获取Staffs代理
 83           public  static IStaffsBLLService GetStaffsBLLService()
 84         {
 85              if (_StaffsBLLService ==  null)
 86             {
 87                 ChannelFactory channelFactory =  new ChannelFactory( " StaffsBLLService ");
 88                 _StaffsBLLService = channelFactory.CreateChannel();
 89             }
 90              return _StaffsBLLService;
 91         }
 92 
 93          /// 获取Company代理
 94           public  static ICompanyBLLService GetCompanyBLLService()
 95         {
 96              if (_CompanyBLLService ==  null)
 97             {
 98                 ChannelFactory channelFactory =  new ChannelFactory( " CompanyBLLService ");
 99                 _CompanyBLLService = channelFactory.CreateChannel();
100             }
101              return _CompanyBLLService;
102         }
103     }
104 }

 

Utility没什么好讲的,就是两个数据库操作类,一个Oracle类,一个SQL类,这里就不贴图贴代码了....

 

疑问解答

架构、项目、代码端都讲解完成了....接下来和大家分享项目中碰到的一个绑定的问题....

首先贴上要绑定的Model层信息:

ExpandedBlockStart.gif 外键对象
 1  namespace Insigma.PSI.Model
 2 {
 3     [DataContract]
 4      public  class Handles_GoodDetailsModel
 5     {
 6          // 外键对象
 7          [DataMember]
 8          public HandelGeneralsModel Handles;
 9 
10         [DataMember]
11          public GoodsDetailsModel GoodsDetails;
12     }
13 }

讲讲要求吧,就是希望当页面载入是显示订单表_商品详细表的综合信息,由于是不同的表,所以我在Model层建立了一个单独的Model类,里面是一对外键对象,因为存在主外键的关系,所以我就考虑到了用外键对象来存放数据,然后通过外键建立两张表的视图,然后获取视图数据,OK,获取完全没有问题,获取到了就是页面端的绑定了,按照原来的思路,上代码:

ExpandedBlockStart.gif 错误绑定
1              // 【错误】绑定
2              List hand_goodList = GetServiceFromWcf.GetHandle_GoodDetails().GetHandle_GoodDetails(sqlWhere);
3             dgv_DetailShow.DataSource = hand_goodList;

 结果就是,始终绑不上,经过深思熟虑之后,才觉得应该是外界对象的问题,所以收集资料,终于发现错误点确实在外键对象上,外键对象无法直接直接绑定上去,至少DataGridView无法直接绑定,web中的Eval()、Bind()可以绑定外键对象(经测试)........好了,问题找到了就要开始解决了,最后终于发现了一种手动绑定的方法,上代码:

ExpandedBlockStart.gif 正确绑定
 1              // 【正确】手工绑定
 2              List hand_goodList = GetServiceFromWcf.GetHandle_GoodDetails().GetHandle_GoodDetails(sqlWhere);
 3              // 由于存在外键对象  无法直接绑定 需要手工绑定
 4              dgv_DetailShow.Rows.Clear();
 5              foreach (Handles_GoodDetailsModel model  in hand_goodList)
 6             {
 7                  // 新增一行,该值会随着循环绑定自增
 8                   int i = dgv_DetailShow.Rows.Add();
 9 
10                  // 给对应行绑定数据
11                  dgv_DetailShow.Rows[i].Cells[ " HandCode "].Value = model.Handles.HandCode;
12                 dgv_DetailShow.Rows[i].Cells[ " ContCode "].Value = model.Handles.ContCode;
13                 dgv_DetailShow.Rows[i].Cells[ " HandDate "].Value = model.Handles.HandDate;
14                 dgv_DetailShow.Rows[i].Cells[ " StaffCode "].Value = model.Handles.StaffCode;
15                 dgv_DetailShow.Rows[i].Cells[ " StoreDate "].Value = model.Handles.StoreDate;
16 
17                 dgv_DetailShow.Rows[i].Cells[ " DetailCode "].Value = model.GoodsDetails.DetailCode;
18                 dgv_DetailShow.Rows[i].Cells[ " GooCode "].Value = model.GoodsDetails.GooCode;
19                 dgv_DetailShow.Rows[i].Cells[ " GooNum "].Value = model.GoodsDetails.GooNum;
20                 dgv_DetailShow.Rows[i].Cells[ " GooPrice "].Value = model.GoodsDetails.GooPrice;
21                 dgv_DetailShow.Rows[i].Cells[ " StoreCodeIn "].Value = model.GoodsDetails.StoreCodeIn;
22                 dgv_DetailShow.Rows[i].Cells[ " StoreCodeOut "].Value = model.GoodsDetails.StoreCodeOut;
23             }
24         }

 

 至此,,本篇博客就算告一个段落了,欢迎大家评点指导。。。。。。。

转载于:https://www.cnblogs.com/holyknight-zld/archive/2012/08/31/psidemo.html

你可能感兴趣的:(分享一个进销存项目(多层架构))