DynamicObject详解

DynamicObject 类

命名空间:

System.Dynamic

程序集:

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)
}
案例分析 - 准备工作

我们新建个单据,添加以下控件:

类型标识实体名备注
整数FLongLong
小数FNumNum123
日期FDateDate
文本FTextText
基础资料FItemIDItemID类型:物料、引用属性:启用批号管理
复选框FOkFOk
单据体FEntityEntity
文本FValueValue位于单据体

添加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();
            }
        }
    }
案例分析 - 调试结果

img
调试上面的插件。我们在界面上输入值,然后点击按钮1,查看数据包结构。

单据数据包DynamicObject,相当于一个有层次结构的数据字典
第一层包含全部的单据头字段以及单据体行集合;
单据体数据行集合、基础资料字段,则需要通过第二层的DynamicObject来展示。

基本特征:
包含了全部单据头字段值
包含了单据体行集合对象
字段通过Key + Value,形成一个键值对,占据DynamicObject的一个节点
字段在数据包中的Key,使用的是字段的属性名
基础资料字段的值,也是一个DynamicObject对象,其中嵌套包含了各个引用属性的值

各字段值及数据类型截图
img

图二:单据体行集合,DynamicObjectCollection类型

img

图三:单据体行,又是一个DynamicObject

img

分录数据包类型为DynamicObjectCollection
物料(基础资料)数据包类型为DynamicObject

PS:dyo.DynamicObjectType.Properties可枚举当前DynamicObject键,结果如图:
img

操作示例:
方法一:直接操作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);


  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值