DynamicObject 类
命名空间:
程序集:
System.Linq.Expressions.dll
提供用于在运行时指定动态行为的基类。 必须继承此类;不能直接对其进行实例化。DynamicObject是K3Cloud的基础数据格式。金蝶的表单数据、执行SQL后的数据集都用DynamicObject来表示。
实体数据包主要是DynamicObject对象,可以称为动态数据对象,我们可以简单的把DynamicObject理解为
Dictionary<string,object>
。
DynamicObject的集合用DynamicObjectCollection来表示,可以简单的理解为List<Dictionary<string,object>>
DynamicObjectCollection里面有多个或者1个DynamicObject 。是DynamicObject集合,相当于是DynamicObject 的 数组。
DynamicObject相当于一个有层次结构的数据字典。需要"一层一层剥开"去进行操作。一个单据的数据包就是一个DynamicObject,单据的数据包里面有很多类型的数据,其实字段是绑定到实体里面的其实DynamicObject里面有很多层,是通过键值对的方式构成的,而每个值是有不同的数据类型的,当然也会存在DynamicObject对象类型的值,所以就形成了DynamicObject多层展开的数据结构。字典的key对应的就是字段的实体属性标识。读取/设置值的方法也跟字典一致。
例:
object a=dyo["Id"];//获取值
dyo["Id"]=123;//设置值
DynamicObject的值可以是基本类型(int、DateTime、string等),也可以是复杂类型(DynamicObject、DynamicObjectCollection)。
如果DynamicObject是SQL语句的执行结果,那么只会有基本类型,所以本文只考虑表单的DynamicObject,不考虑SQL的结果集
基本类型
int/long:控件为整数类型,对应的DynamicObject值的数据类型为long
string:控件为文本类型,对应的DynamicObject值的数据类型为string
bool:控件为复选框,对应的DynamicObject值的数据类型为bool,KSQL没有bit类型,实际在数据库存储是char(1)类型
DateTime:控件为时间类型,对应的DynamicObject值的数据类型为DateTime
decimal:控件为小数、数量、单价、金额等类型,对应的DynamicObject值的数据类型为decimal
实际获取,我们一般用Convert.ToXXX方法进行转换,或者显示声明。
例:
long id=Convert.ToInt64(dyo["Id"]);
long id2=(long)dyo["Id"];
复杂类型
表单控件为基础资料类,值为:DynamicObject。里面的字段为该基础资料的基本字段(内码/代码/名称)+本表单引用该基础资料的字段。
分录数据包,值为:DynamicObjectCollection
为了便于开发,DynamicObject存在一些默认键,用于方便开发引用
Id:表单数据主键名。
示例:object id=dyo["Id"];//获取主键
XXX_Id:XXX为基础资料实体,它是一个DynamicObject类型,但表单同时存在一个键为XXX_Id,它的值为基础资料ID。
示例:如物料实体名为MaterialId,则var midt=dyo["MaterialId_Id"];//获取物料内码
XXX_Text:只存在于批号字段,值为批号文本。
示例:如批号实体名为Lot,则string lot=dyo["Lot_Text"];//获取批号文本
Seq:分录序号实体名,固定为此名称,仅存在于分录。
示例:object id=dyo["Seq"];//获取行序号
通过sql脚本得到数据包
var dynObjs = DBUtils.ExecuteDynamicObject(this.Context, sql)
插件中的数据包
整张单据的,表单插件:this.Model.DataObject
操作插件,传入的,一般在相应的事件中:e.DataEntity
数据包赋值
有3种方式直接给数据包赋值,其实这3种方式都是通过找到实体类型中的动态属性,然后通过动态属性给其赋值,所以他们的本质都是一样的。intProperty.SetValue(dynData, 123);
动态属性又是怎样给数据包赋值的?动态属性中有个属性Ordinal,其实Ordinal就是数据包中的数组值得索引。
为什么不直接赋值而要通过动态属性?这是因为动态属性可以对值进行控制,可以设置默认值,类型验证,改变事件等。
dynData[0] = 111; //1.索引
dynData[“proName1”] = 222;//2.动态属性名
dynData[property1] = 333;//动态属性
property1.SetValue(dynData, 123); //动态属性给其赋值
动态属性GetValue和GetFastValue的区别
GetValue会根据数据包,去找对应的真实属性,如果动态属性持有的动态类型和数据包持有的动态类型相同,则返回此动态属性,否则得找此数据包对应的动态类型下面的动态属性,找到对应的动态属性之后再去取值GetFastValue不用找动态属性,把调用此方法的动态属性作为取数据的动态属性
动态属性SetValue和SetItemValueByNumber
SetValue更新字段值会走表单服务:会触发datachange事件和实体服务规则
例:
void SetValue(Field field, DynamicObject activeRow, object value);
void SetValue(string key, object value, int row);
SetItemValueByNumber根据基础资料的代码,设置基础资料完整信息
例:
void SetItemValueByNumber(string key, string number, int row);
如何遍历数据包
通过动态类型下的动态属性集合遍历数据包,
假设已知的动态数据包未dynObj,代码:
foreache(var proItem in dynObj.DynamicObjectType.Properties)
{
var value1= proItem.GetValue(dynObj)
}
案例分析 - 准备工作
我们新建个单据,添加以下控件:
类型 | 标识 | 实体名 | 备注 |
---|---|---|---|
整数 | FLong | Long | |
小数 | FNum | Num123 | |
日期 | FDate | Date | |
文本 | FText | Text | |
基础资料 | FItemID | ItemID | 类型:物料、引用属性:启用批号管理 |
复选框 | FOk | FOk | |
单据体 | FEntity | Entity | |
文本 | FValue | Value | 位于单据体 |
添加1个按钮用于读取当前表单数据包
案例分析 - 代码
public class Test1BillPlugin: AbstractBillPlugIn {
void button1() {//读取界面表单数据
DynamicObject dyo = this.Model.DataObject;
getField(dyo);
}
void getField(DynamicObject dyo) {
var keys = dyo.DynamicObjectType.Properties;//枚举键
Debug.WriteLine("当前DynamicObject键为:"+keys);
object vLong = dyo["Long"];//整数
object vNum = dyo["Num123"];//小数
object vDate = dyo["Date"];//日期
object vText = dyo["Text"];//文本
object vItemID = dyo["ItemID"];//基础资料(物料)
object vItemID_Id= dyo["ItemID_Id"];//基础资料隐藏键(物料内码)
object vOK = dyo["FOK"];//复选框
DynamicObjectCollection vEntity = dyo["Entity"] as DynamicObjectCollection;//单据体
//循环单据体
foreach (DynamicObject row in vEntity) {
var v = row["Value"];//单据体-文本字段值
Debug.WriteLine(v);
}
}
public override void ButtonClick(ButtonClickEventArgs e) {
base.ButtonClick(e);
if (e.Key.EqualsIgnoreCase("FButton1")) {
button1();
}
}
}
案例分析 - 调试结果
调试上面的插件。我们在界面上输入值,然后点击按钮1,查看数据包结构。
单据数据包DynamicObject,相当于一个有层次结构的数据字典:
第一层包含全部的单据头字段以及单据体行集合;
单据体数据行集合、基础资料字段,则需要通过第二层的DynamicObject来展示。
基本特征:
包含了全部单据头字段值
包含了单据体行集合对象
字段通过Key + Value,形成一个键值对,占据DynamicObject的一个节点
字段在数据包中的Key,使用的是字段的属性名
基础资料字段的值,也是一个DynamicObject对象,其中嵌套包含了各个引用属性的值
图二:单据体行集合,DynamicObjectCollection类型
图三:单据体行,又是一个DynamicObject
分录数据包类型为DynamicObjectCollection
物料(基础资料)数据包类型为DynamicObject
PS:dyo.DynamicObjectType.Properties可枚举当前DynamicObject键,结果如图:
操作示例:
方法一:直接操作DynamicObject**
// 假设billObj是单据的数据包
DynamicObject billObj = this.Model.DataObject;
// 读取单据内码
long billId = Convert.ToInt64(billObj[0]);
// 普通文本字段(读取 + 设置)
string fldBillNoValue = Convert.ToString(billObj["BillNo"]);
billObj["BillNo"] = fldBillNoValue ;
// 日期字段(读取 + 设置)
DateTime fldDateValue = Convert.ToDateTime(billObj["F_JD_Date"]);
billObj["F_JD_Date"] = fldDateValue;
// 基础资料字段(读取 + 设置)
DynamicObject fldSupplierValue = billObj["F_JD_Supplier"] as DynamicObject;
billObj["F_JD_Supplier"] = fldSupplierValue ;
if (fldSupplierValue != null)
{ billObj["F_JD_Supplier_Id"] = Convert.ToInt64(fldSupplierValue[0]);
long supplierId = Convert.ToInt64(fldSupplierValue[0]);
string supplierNumber = fldSupplierValue["Number"].ToString();
string supplierName = fldSupplierValue["Name"].ToString();
}
// 单据体(单据体行集合属性本身只读,可以通过单据体集合提供的方法,添加行、删除行)
DynamicObjectCollection entityRows = billObj["FEntity"] as DynamicObjectCollection;
foreach (var entityRow in entity1Rows)
{
// 内码
long entityId = Convert.ToInt64(entityRow[0]);
// 数量 (读取 + 设置)
decimal fldQtyValue = Convert.ToDecimal(entityRow["F_JD_Qty"]);
entityRow["F_JD_Qty"] = fldQtyValue;
}
**方法二:通过字段的元数据,操作DynamicObject(推荐)**
// 假设billObj是单据的数据包
DynamicObject billObj = this.Model.DataObject;
// 首先获取各种元素的元数据
Field fldBillNo = this.View.BillBusinessInfo.GetField("FBillNo");
Field fldDate = this.View.BillBusinessInfo.GetField("F_JD_Date");
BaseDataField fldSupplier = this.View.BillBusinessInfo.GetField("F_JD_Supplier") as BaseDataField;
BaseDataField fldMaterial = this.View.BillBusinessInfo.GetField("F_JD_FMaterialId") as BaseDataField;
Field fldQty = this.View.BillBusinessInfo.GetField("F_JD_Qty");
Entity entity = this.ListView.BillBusinessInfo.GetEntity("FEntity");
// 读取单据内码
long billId = Convert.ToInt64(billObj[0]);
//单据编号
string billNo = Convert.ToDateTime(fldBillNo.DynamicProperty.GetValue(billObj));
fldBillNo.DynamicProperty.SetValue(billObj, billNo));
// 日期
DateTime fldDateValue = Convert.ToDateTime(fldDate.DynamicProperty.GetValue(billObj));
fldDate.DynamicProperty.SetValue(billObj, fldDateValue)
// 供应商:基础资料字段
DynamicObject fldSupplierValue = fldSupplier.DynamicProperty.GetValue(billObj) as DynamicObject;
// 设置供应商基础字段值
DynamicObject[] supplierObjs = Kingdee.BOS.ServiceHelper.BusinessDataServiceHelper.LoadFromCache(
this.Context,
new object[] { fldSupplierValue[0] },
fldSupplier.RefFormDynamicObjectType);
fldSupplier.RefIDDynamicProperty.SetValue(billObj, supplierObjs[0][0]);
fldSupplier.DynamicProperty.SetValue(billObj, supplierObjs[0]);
// 基础资料属性值
if (fldSupplierValue != null)
{
long supplierId = Convert.ToInt64(fldSupplierValue[0]);
string supplierNumber = fldSupplier.GetRefPropertyValue(fldSupplierValue, "FNumber").ToString();
string supplierName = fldSupplier.GetRefPropertyValue(fldSupplierValue, "FName").ToString();
}
// 单据体的字段
DynamicObjectCollection entityRows = entity.DynamicProperty.GetValue(billObj) as DynamicObjectCollection;
foreach (var entityRow in entityRows)
{
// 内码
long entityId = Convert.ToInt64(entity1Row[0]);
// 物料:基础资料字段
DynamicObject fldMaterialValue = fldMaterial.DynamicProperty.GetValue(entityRow) as DynamicObject;
if (fldMaterialValue != null)
{
long materialId = Convert.ToInt64(fldMaterialValue[0]);
string materialNumber = fldMaterial.GetRefPropertyValue(fldMaterialValue, "FNumber").ToString();
string materialName = fldMaterial.GetRefPropertyValue(fldMaterialValue, "FName").ToString();
}
// 数量
decimal fldQtyValue = Convert.ToDecimal(fldQty.DynamicProperty.GetValue(entityRow));
fldQty.DynamicProperty.SetValue(entityRow, fldQtyValue);
}
// 给单据体添加新行
DynamicObject newRow = new DynamicObject(entity.DynamicObjectType);
entityRows.Add(newRow);
ue, “FName”).ToString();
}
// 数量
decimal fldQtyValue = Convert.ToDecimal(fldQty.DynamicProperty.GetValue(entityRow));
fldQty.DynamicProperty.SetValue(entityRow, fldQtyValue);
}
// 给单据体添加新行
DynamicObject newRow = new DynamicObject(entity.DynamicObjectType);
entityRows.Add(newRow);