解析大型.NET ERP系统 界面与逻辑分离
2015-08-07 08:04
363 查看
WindowsForms程序实现界面与逻辑分离的关键是数据绑定技术(DataBinding),这与微软推出的ASP.NETMVC的原理相同,分离业务代码与界面层,提高系统的可维护性。
ERP系统选择LLBLGenORM框架作为数据访问技术基础,数据源为实体类型。主要绑定以下几种类型:
IEntity数据库表的实体映射,每个属性。绑定时,控件的类型与实体的属性类型严格匹配与验证。
上面的代码例子定义了二个属性:数值类型的销售金额,字符串类型的客户编号。在做数据绑定设计时,需要分别选择NumbericEditor和TextEditor。
控件Control简单的数据绑定,比如TextEditor,NumbericEditor,CheckedEdidtor只绑定一个属性,复杂的数据定比如Grid绑定一个对象的多个属性,根据实体的属性自动设定绑定控件的属性。
数据源控件BindingSource负责将控件的属性和对象的属性关联起来。对象的属性值会被自动传递个控件的属性,而控件的属性值更改后也会直接传回对象的属性(双向绑定)。
简单绑定绑定下面的代码的含义是将客户编号绑定到txtCustomerNo控件的Text属性。
复杂数据绑定下面的代码将客户集合(EntityCollection)绑定到网格控件。
下拉选择框(Combox)的数据绑定,下面是绑定到枚举类型的例子:
代码中将枚举的值反射出来,创建为ValueList绑定到下拉框中。
1自动赋值逻辑。在采购订单输入窗体中,输入供应商编号,自动带入供应商名称,供应商付款货币和付款条款的值到当前采购订单单据中。
2计算逻辑。输入单价和数量后,自动计算出物料金额金额,再输入折扣率后,计算出折扣金额和应付款金额。
3数据验证。出仓时,库存余额不能是负数。付款金额必须大于等于订单金额时,订单才能完成付款。
4业务关联。送货单要依据销售订单的订单数量为依据来送货,采购验货的结果中,合格和不合格数量要等于采购订单要验货的数量。
界面中读取数据的方法LoadData,读取数据后绑定到数据源控件BindingSource控件中。
以上是界面层中数据绑定的两个核心方法,读取数据并将数据绑定到界面控件中。注意到方法是通过参数实体EntityBase2操作的,所以它是通用的方法实现,被框架调用实现界面与逻辑分离。
数据绑定DataBinding
数据绑定技术的主要内容:数据源(DataSource),控件(Control),绑定方式(Binding)。通过绑定控件,将数据源中的属性绑定到界面控件中,并可以实现双向的数据绑定。当界面控件中的值发生改变时,可以通过数据绑定控件,更新控件绑定的对象,当通过代码直接改变对象值后,数据绑定控件可以将值更新到它所绑定的界面控件中。ERP系统选择LLBLGenORM框架作为数据访问技术基础,数据源为实体类型。主要绑定以下几种类型:
IEntity数据库表的实体映射,每个属性。绑定时,控件的类型与实体的属性类型严格匹配与验证。
///<summary>TheTotLdiscAmtpropertyoftheEntityPurchaseOrder<br/> ///Mappedontablefield:"PUORDH"."TOT_LDISC_AMT"<br/> ///Tablefieldtypecharacteristics(type,precision,scale,length):Decimal,16,2,0<br/> ///Tablefieldbehaviorcharacteristics(isnullable,isPK,isidentity):true,false,false<br/><br/> ///ReadOnly:<br/></summary> ///<remarks>Mappedontablefield:"PUORDH"."TOT_LDISC_AMT"<br/> ///Tablefieldtypecharacteristics(type,precision,scale,length):Decimal,16,2,0<br/> ///Tablefieldbehaviorcharacteristics(isnullable,isPK,isidentity):true,false,false</remarks> publicvirtualSystem.DecimalSalesAmount { get{return(System.Decimal)GetValue((int)PurchaseOrderFieldIndex.TotLdiscAmt,true);} set{SetValue((int)PurchaseOrderFieldIndex.TotLdiscAmt,value);} } ///<summary>TheTotAtaxAmtpropertyoftheEntityPurchaseOrder<br/> ///Mappedontablefield:"PUORDH"."TOT_ATAX_AMT"<br/> ///Tablefieldtypecharacteristics(type,precision,scale,length):Decimal,16,2,0<br/> ///Tablefieldbehaviorcharacteristics(isnullable,isPK,isidentity):true,false,false<br/><br/> ///ReadOnly:<br/></summary> ///<remarks>Mappedontablefield:"PUORDH"."TOT_ATAX_AMT"<br/> ///Tablefieldtypecharacteristics(type,precision,scale,length):Decimal,16,2,0<br/> ///Tablefieldbehaviorcharacteristics(isnullable,isPK,isidentity):true,false,false</remarks> publicvirtualSystem.StringCustomerNo { get{return(System.Decimal)GetValue((int)PurchaseOrderFieldIndex.TotAtaxAmt,true);} set{SetValue((int)PurchaseOrderFieldIndex.TotAtaxAmt,value);} }
上面的代码例子定义了二个属性:数值类型的销售金额,字符串类型的客户编号。在做数据绑定设计时,需要分别选择NumbericEditor和TextEditor。
控件Control简单的数据绑定,比如TextEditor,NumbericEditor,CheckedEdidtor只绑定一个属性,复杂的数据定比如Grid绑定一个对象的多个属性,根据实体的属性自动设定绑定控件的属性。
数据源控件BindingSource负责将控件的属性和对象的属性关联起来。对象的属性值会被自动传递个控件的属性,而控件的属性值更改后也会直接传回对象的属性(双向绑定)。
简单绑定绑定下面的代码的含义是将客户编号绑定到txtCustomerNo控件的Text属性。
CustomerEntitycustomer=...... txtCustomerNo.DataBindings.Add("Text",customer,"CustomerNo");
复杂数据绑定下面的代码将客户集合(EntityCollection)绑定到网格控件。
EntityCollection<CustomerEntity>customers=......
customerBindingSource.DataSource=customers;
gridCustomer.SetDataBinding(customerBindingSource,"Customer",true,true);
下拉选择框(Combox)的数据绑定,下面是绑定到枚举类型的例子:
comboDepartment.InitializeValueFromEnum<CustomerType>();
代码中将枚举的值反射出来,创建为ValueList绑定到下拉框中。
业务逻辑BusinessLogic
从ERP的技术层面考虑,ERP包含以下四种逻辑:1自动赋值逻辑。在采购订单输入窗体中,输入供应商编号,自动带入供应商名称,供应商付款货币和付款条款的值到当前采购订单单据中。
//PurchaseOrderEntity.cs
privatevoidOnChangeVendorNo(stringoriginalValue)
{
if(string.CompareOrdinal(this.VendorNo,originalValue)==0)
return;
IVendorManagervendorMan=ClientProxyFactory.CreateProxyInstance<IVendorManager>();
VendorEntityvendor=vendorMan.GetValidVendor(Shared.CurrentUserSessionId,this.VendorNo);
this.VendorName=vendor.VendorName.;
this.PayTerms=vendor.PayTerms;
this.PayCurrency=vendor.PayCurrency;
}
2计算逻辑。输入单价和数量后,自动计算出物料金额金额,再输入折扣率后,计算出折扣金额和应付款金额。
//PurchaseOrderEntity.cs
privatevoidOnChangeQty(decimal?originalValue)
{
if(!originalValue.HasValue||this.Qty!=originalValue.Value)
{
this.Amount=this.Qty*this.UnitPrice;
}
}
3数据验证。出仓时,库存余额不能是负数。付款金额必须大于等于订单金额时,订单才能完成付款。
//Shipment.cs
publicboolValidateQtyShiped(ShipmentEntityshipment,decimalvalue)
{
IInventoryBalanceManagerbalanceManager=CreateProxyInstance<IInventoryBalanceManager>();
decimalqtyRemaining=balanceManager.GetItemBalance(shipment.ItemNo);
if(qtyRemaining<0|qtyRemaining<value)
thrownewFieldValidationException("Negativeitembalancedetected");
returntrue;
}
4业务关联。送货单要依据销售订单的订单数量为依据来送货,采购验货的结果中,合格和不合格数量要等于采购订单要验货的数量。
//PurchaseInspectionEntity.cs
privatevoidOnChangeGrnNo(stringoriginalValue)
{
if(Shared.StringCompare(this.GrnNo,originalValue)==0)
return;
IPrefetchPath2prefetchPath=newPrefetchPath2(EntityType.GrnEntity);
prefetchPath.Add(GrnEntity.PrefetchPathGrnOrderDetails);
IGrnManagergrnManager=CreateProxyInstance<IGrnManager>();
GrnEntitygrn=grnManager.GetGrn(Shared.CurrentUserSessionId,this.GrnNo,prefetchPath);
//待检验数量=订单数量-已检验数量
this.Qty=grn.QtyOrder-grn.QtyInspected;
}
界面设计InterfaceDesign
几乎所有的界面绑定业务实体到控件中的方法都一致,参考下面的方法:protectedoverridevoidBindControls(EntityBase2entity)
{
base.BindControls(entity);
this.purchaseOrderBindingSource.DataSource=entity;
}
界面中读取数据的方法LoadData,读取数据后绑定到数据源控件BindingSource控件中。
//PurchaseOrderEntry.cs
protectedoverrideEntityBase2LoadData(Dictionary<string,string>refNo)
{
stringorderNo;
if(refNo.TryGetValue("OrderNo",outorderNo))
{
PurchaseOrderEntityresult=_purchaseOrderManager.GetPurchaseOrder(orderNo);
this._purchaseOrder=result;
}
return_purchaseOrder;
}
以上是界面层中数据绑定的两个核心方法,读取数据并将数据绑定到界面控件中。注意到方法是通过参数实体EntityBase2操作的,所以它是通用的方法实现,被框架调用实现界面与逻辑分离。
相关文章推荐
- iOS开发 - 19.手势解锁
- MySQL数据库引擎详解
- Android应用UI架构
- SQL Server窗口函数:ROWS与RANGE
- EF6 CodeFirst+Repository+Ninject+MVC4+EasyUI实践(九)
- 【前端学习笔记】原生Javascript中的ajax
- Read Large Files in Python
- .NET技术大系概览
- 为人篇
- Quartz2D - 08.图片擦除效果
- 黑马程序员——多态
- 通过测试想到的一些问题
- 程序优化的5个方向
- python基础学习笔记<内建模块与第三方模块>
- Java - 继承
- 理解Buffer
- 构造函数为什么不能是虚函数?
- 问题点0807------框架设计中的一些调整
- Nim语言的模块化编程
- Coins in a Line II