花了点时间完成了一个进销存的项目,个人觉得对逻辑分析,架构思想都有一定的体会,故拿出来和大家分享下,首先介绍下项目的功能吧,主要功能有采购管理,销售管理,库存管理,人员信息管理,系统管理等,不过写这篇博客的主要目的并不是讲解如何实现项目功能,而是分享下该项目的架构思想、实现步骤和碰到的一些问题,分享下心得体会罢了......
数据库设计
下面进入主题,首先贴上项目的主界面,由于对界面要求不是很高,所以界面相对来说比较简单,甚至可是说是简陋了,,呵呵,不过重点不在界面上,勇敢的直接上图:
架构:
首先说说项目架构吧,,贴上项目模块图:
从图中可知,项目总共分10个模块,采用三层架构+WCF+反射+抽象工厂来搭建,其中还加入了单例模式,缓存机制。下面分别说说每个模块的作用:
Insigma.PSI.UI:WinForm界面层
Insigma.PSI.DBFactory:抽象工厂层,主要为考虑可以使项目可以适用Oracle数据库和SQL数据库,就搭建了一个抽象工厂层,分别在Oracle数据库层和SQL数据库层建立一个 工 厂来实现该抽象工厂
C:\..\WcfWebRelease\:web层,主要用来发布WCF服务
首先看看库存列表,上图:
库存列表的功能比较简单,只是实现了仓库的增、删、改、查,由于功能比较简单,也就不多做讲解了....
再贴上出库/入库模块图:
这块应该算是库存模块中的核心模块了,因为该块的业务逻辑比较多,比较繁琐,大致讲讲业务逻辑吧,大致的逻辑为:出库单/出库单-->填写订单-->出库/入库-->修改库存信息,按照这个顺序来完成入库出库,顺序不能颠倒,同时还要实现订单的删除,修改,在修改库存信息时由于表和表之间有很多的外键关系,所以要同时删除多张表中含有删除信息的内容,实现联级删除,由于这里考虑到要保证结果的一致性,所以用事务来实现。再看看界面,为了保证用户按照这个逻辑顺序操作,首先我添加了一对单选框(出库单,入库单),当单选框都没有选中时,保存订单按钮、出库、入库按钮都是不可用状态,当用户选择了订单类型之后,保存订单按钮转为可用状态,需要用户填写订单信息,填写好对应信息之后保存订单,保存成功之后列表框中出现刚生产的订单,同时订单号是自动来生成的,入库单是“S_I”开头,出库单是"S_O"开头,后面五位数字为随机生成,这样生成的订单号易于区别,也避免了订单号的重复,然后根据用户选择的订单类型,对应的按钮转为可用(出库单-->出库按钮) ,这样就保证了用户始终是按照这个顺序来操作,同时订单列表框下有删除订单按钮,点击可以删除选中按钮。
下面那出库为例,看看如何实现入库:
点击【入库】按钮,弹出商品选择窗口,窗口中显示了订单号和仓库编号,同时还有一个【选择】按钮,通过该按钮选择商品
点击【选择】按钮,弹出商品列表窗口,由于我这里商品记录比较少,只有两条,我们就选第二条吧,点击【确定】按钮
这时的"入库信息"窗口中就填写上了刚才选择商品的信息,然后只要在填写上入库数量,点击【确定】就完成了入库操作了,如图:
出库的思路和入库一样,也是先选择出库单,然后填写出库单信息并保存订单,然后再给订单上添加商品,过程也是通过选择实现,由于类同,就不赘述了...
然后是库存明细模块,上图:
该模块主要实现的功能是对存放在各个仓库中的商品进行清点,点击商品的行头就显示出该商品的信息:编号、数量、盘点日期、存放仓库;填写好盘点的数量之后单击【保存】按钮,实现保存盘点数据,这个模块也没什么难点,唯一复杂点的就是因为同样的货品可以存放到不同的仓库中,所以点击时要搜索出该商品所在的所有仓库编号,并在“存放仓库”列表中显示。
最后是库存警报模块,上图:
该模块主要来设置库存上限和库存下限,实现当库存中存放的商品的数量超过库存上限或者低于库存下限时,弹出警告框提醒。
这样,我们就简单的把项目的功能叙述了下,当然还有一些细节的功能,这里就不再罗列了.....
代码:
由于代码端模块较多,我们就按照程序执行调用的层次关系来罗列.这样相对来说看起来思路比较清晰......Model层只是定义了需要用到的实体而已,不再赘述了.....
1>.Insigma.PSI.IBLLService层
可以看到有很多的功能类,无法全部讲解,所以就挑选一块,后面的模块层中也着重讲解挑选的这一块,我挑选了库存管理模块,即IStoreManageBLLService,贴上该类的代码:
2 using System.ServiceModel;
3
4 namespace Insigma.PSI.IBLLService
5 {
6 [ServiceContract]
7 public interface IStoreManageBLLService
8 {
9 [OperationContract]
10 // 获取所有仓库
11 List
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
36
37 [OperationContract]
38 // 修改商品数量
39 bool UpdateGoodNum(StoreDetailsModel storeDetail);
40 }
41 }
由于我们要用到WCF来发布服务,所以这里的每个方法都添加了WCF特性,主要功能是获取仓库、获取仓库编号、对仓库的增、删、改、查;由于出库入库对商品的数量有影响,又由于不存在同一张表格中,所以我们专门定义了一个方法来实现修改商品数量;然后出库入库对应一张库存明细表,执行操作之后要修改该表,记录信息,同时也可以获取该表信息,定义的方法就那么多了.....
2>Insigma.PSI.BLLService层
这层是对上面提及的接口层的实现,但除了实现之外,我还在这里加了一个反射机制,在这层按照原来三层的思路是要调用DAL层来实现对数据库的操作,但因为考虑到要适用于不同的数据库,所以我们不知道要调用哪个数据库层的DAL来操作,这就是这个反射存在的意义了,我们可以通过反射来读取配置文件中定义的数据库信息,从而创建对应数据库类的实例来操作....先贴上StoreManageBLLService的代码,之后再接着讲解,看到了代码思路会清晰点:
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
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
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的实例的,然后再调用该实例的方法,那么反射里是怎么实现的呢?贴上代码:
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,也同样定义一个静态变量和静态方法来创建实例,同样保证了"单例"。
然后看看配置文件中定义的数据库信息:
2
3
4
5
6
7
8
可以看到配置文件中定义了程序集的名称,数据库类的名称,但其实这个数据库类就是工厂类的名称。反射中提到了AbstractFactory类型,这是抽象工厂模块中的内容,那么为什么要有这个抽象工厂呢??存在的意义是什么呢?我们就来看看抽象工厂,我的思路:定义一个抽象工厂,里面是一些抽象产品,对应于IDAL中定义的类型的产品,然后在每个数据库类库中定义一个具体工厂,该具体工厂继承抽象工厂,重写抽象产品,返回具体产品,即对IDAL接口类的实现类(DAL)的对应实例,那你会想到了,如没有工厂我们照样可以实现用反射类获取实例的呀,但你想想这时候获取到得实例是什么?它就是我们工厂类中的单个产品而已,当我们的产品有很多的时候怎么办呢,一个DAL类库中的每个类都是一个产品,那难道我们把每个产品都在配置文件中去定义,然后用到的时候通过反射去获取么??显然不可能,所以我们加了抽象工厂模式了,在DAL类库中有一个具体的工厂类,我们所有的产品都由该工厂来创建,那我们获取的是时候只需要获取工厂实例,然后通过该实例来创建我们需要的产品就可以了......
3>Insigma.PSI.IDAL层
同样的拿IStoreManageDAL类讲解,上代码:
2
3 namespace Insigma.PSI.IDAL
4 {
5 public interface IStoreManageDAL
6 {
7 List
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
14 int UpdateGoodNum(StoreDetailsModel storeDetail);
15 }
16 }
这也是一个接口类,接口的作用就是规范行为,定义一种契约,继承该接口的类都必须实现契约,所以这里主要来约束DAL实现层的行为
4>.Insigma.PSI.DBFactory
抽象工厂模块中就一个抽象工厂类,就直接贴上类代码:
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
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层中定义的每个类
我们仍然拿StoreManageBLL.svc来讲解,贴上代码:
定义了程序集的名称和服务类名,然后在web.config中配置服信息,上代码:
7>.Insigma.PSI.UI
服务配置好了,那我们客户端怎么调用呢??所以我们需要在客户端的app.config配置文件中添加终结点信息,上代码:
2
3
4
5 binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.ISysUserBLLSerivce "
6 name= " SysUserService " />
7
8 binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IDepartmentBLLService "
9 name= " DepartmentBLLService " />
10
11 binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IStoreManageBLLService "
12 name= " StoreManageBLLService " />
13
14 binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IHandleGeneralsBLLService "
15 name= " HandleGeneralsBLLService " />
16
17 binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IGoodsBLLService "
18 name= " GoodsBLLService " />
19
20 binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IHandle_GoodDetailsBLLService "
21 name= " Handle_GoodDetailsBLLService " />
22
23 binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.IStaffsBLLService "
24 name= " StaffsBLLService " />
25
26 binding= " basicHttpBinding " bindingConfiguration= "" contract= " Insigma.PSI.IBLLService.ICompanyBLLService "
27 name= " CompanyBLLService " />
28
29
同时,我在客户端专门定义了一个类用来创建wcf代理,返回BLL实例,上代码:
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
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
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
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
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
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
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
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
99 _CompanyBLLService = channelFactory.CreateChannel();
100 }
101 return _CompanyBLLService;
102 }
103 }
104 }
Utility层没什么好讲的,就是两个数据库操作类,一个Oracle类,一个SQL类,这里就不贴图贴代码了....
疑问解答
架构、项目、代码端都讲解完成了....接下来和大家分享项目中碰到的一个绑定的问题....
首先贴上要绑定的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,获取完全没有问题,获取到了就是页面端的绑定了,按照原来的思路,上代码:
2 List
3 dgv_DetailShow.DataSource = hand_goodList;
结果就是,始终绑不上,经过深思熟虑之后,才觉得应该是外界对象的问题,所以收集资料,终于发现错误点确实在外键对象上,外键对象无法直接直接绑定上去,至少DataGridView无法直接绑定,web中的Eval()、Bind()可以绑定外键对象(经测试)........好了,问题找到了就要开始解决了,最后终于发现了一种手动绑定的方法,上代码:
2 List
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 }
至此,,本篇博客就算告一个段落了,欢迎大家评点指导。。。。。。。