DynamicObject动态数据包分析原创
金蝶云社区-eris
eris
36人赞赏了该文章 8,333次浏览 未经作者许可,禁止转载编辑于2022年03月06日 17:54:09
summary-icon摘要由AI智能服务提供

本文介绍了动态实体类型(DynamicObjectType)、动态数据包(DynamicObject)及动态属性(DynamicProperty)的概念和用法。动态实体类型用于抽象化地表示数据库中的数据模型,动态数据包是其实例化形式,存储具体数据,以树形结构展示。动态属性则定义数据字段的类型和行为。文章还详细说明了如何注册、赋值、遍历动态数据包,并讨论了数据包中值的类型判断、错误处理等问题。此外,还提供了如何创建自定义动态实体类型的示例代码,以及动态属性赋值和遍历数据包的实现方式。

一. DynamicObjectType动态实体类型(实体类型),DynamicObject动态数据包(数据包),DynamicProperty动态属性说明:

1.动态实体类型可以看成是Model(数据实体),把从数据库中取到的数据转换成代码中具体的实体类来存载,只不过使用model是具体的数据类,而动态类型是抽象统一的数据类,是Model之上的抽象,由动态属性构成的。

2. 动态数据包就是动态实体类型的实例化,存载具体的数据,以树形结构显示,每项为key和value。

3. key 为字符串类型。

4. value是object类型,但在赋值的时候会根据动态属性中的类型对其验证。

5. 动态属性可以看成某个字段,或某个列名之类的,动态属性来源于单据字段。

6.  单据只有一份元数据,只有一个实体类型,多份数据包;一个字段只有一个动态属性,多个值(不同单据值不一样);

7.  动态数据包都时通过字典或数组存储

.数据包结构

1、树形字典结构

2、每一项都时key/value,键值对

3、简单项的value不能分解,一般未数字或字符串

4、复杂项的value又是个数据包

5、集合项的value是个数据包集合

6、表单数据包最外层是单据头的字段,单据体,子单据头的集合

image.png

三. 基础资料在数据包中的结构

1、会在数据包中注册个简单内码项和复杂项

2、简单内码项的key为属性名+“_"+Id, 复杂项为属性名

3、复杂项中为引用字段组成的数据包

4、复杂项里面的基础资料只会包含Id, msterId, Number, Name等字段

四.如何得到数据包:

1. 手工创建数据包
1)先创建动态类型,假设创建名称为dynTypeName的动态实体类型

DynamicObjectType objType= new DynamicObjectType("dynTypeName");

2)注册属性,可以注册简单属性,复杂属性,集合属性

//注册简单属性
 DynamicProperty property1 = objType.RegisterSimpleProperty("proName1", typeof(int), 0);
//注册复杂属性 DynamicObjectType secondObjType = new DynamicObjectType("SecongdDynType");
objType.RegisterComplexProperty("SecongdDynType", secondObjType, false);//注册复杂项
//注册集合属性 DynamicObjectCollection objColl = new DynamicObjectCollection(secondObjType); 
objType.RegisterCollectionProperty("", secondObjType, objColl.GetType());

2、通过sql脚本得到数据包

var dynObjs = DBUtils.ExecuteDynamicObject(this.Context, sql)

3、插件中的数据包

1)整张单据的,表单插件:this.Model.DataObject

2)操作插件,传入的,一般在相应的事件中:e.DataEntity

五、预制好自定义的已知实体类型:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kingdee.BOS.Orm.Metadata.DataEntity;
using Kingdee.BOS.Orm.DataEntity;
namespace Kingdee.Test
{
    /// <summary>
    /// 自定义已知的动态实体模型
    /// </summary>
    [Serializable]
    public class CustomType : DynamicObjectType
    {
        //表名
        private const string DEFAULTTABLENAME = "t_customTableName";
        private DynamicProperty _cIdProperty;
        /// <summary>
        /// 主键
        /// </summary>
        public DynamicProperty CIdProperty
        {
            get { return _cIdProperty; }
        }
        private DynamicProperty _createTimeProperty;
        /// <summary>
        /// 创建时间
        /// </summary>
        public DynamicProperty CreateTimeProperty
        {
            get { return _createTimeProperty; }
        }
        /// <summary>
        /// 构造函数,不设置物理表格名,直接采用默认表格
        /// </summary>
        public CustomType()
            : base("CustomName", attributes: new DataEntityTypeAttribute() { Alias = tableName })
        {
            this.CreateProperties();
        }
        private void CreateProperties()
        {
            //主键
            _cIdProperty = this.RegisterSimpleProperty("FId",
                                                       typeof(string),
                                                       attributes: new SimplePropertyAttribute(true)
                                                       { DbType = System.Data.DbType.AnsiString });
            // CreateTime -> FCreateTime
            _createTimeProperty = this.RegisterSimpleProperty(
                "CreateTime", typeof(DateTime),
                attributes: new SimplePropertyAttribute() { Alias = "FCreateTime" });
        }
    }
}


六.数据包赋值
1. 有3种方式直接给数据包赋值,其实这3种方式都是通过找到实体类型中的动态属性,
     然后通过动态属性给其赋值,所以他们的本质都是一样的。intProperty.SetValue(dynData, 123);
2. 动态属性又是怎样给数据包赋值的?动态属性中有个属性Ordinal,其实Ordinal就是数据包中的数组值得索引。
3 .为什么不直接赋值而要通过动态属性?这是因为动态属性可以对值进行控制,可以设置默认值,类型验证,改变事件等。
    dynData[0] = 111; //1.索引
    dynData["proName1"] = 222;//2.动态属性名
    dynData[property1] = 333;//动态属性
    property1.SetValue(dynData, 123); //动态属性给其赋值


七. 有必要去判断数据包是否包含key,或某值是什么类型?
1. 数据包中的key都是来源元数据中的字段名,所以单据中有的字段名,数据包一定是有的,除一些特殊字段,比如代理字段等,所以没有必要去判断。
2. value的类型有必要去判断?也是没有必要的,在构建数据包时,类型都是根据字段固定好的。

八.动态属性GetValue和GetFastValue的区别

1)GetValue会根据数据包,去找对应的真实属性,如果动态属性持有的动态类型和数据包持有的动态类型相同,则返回此动态属性,否则得找此数据包对应的动态类型下面的动态属性,找到对应的动态属性之后再去取值

2)GetFastValue不用找动态属性,把调用此方法的动态属性作为取数据的动态属性

九、如何遍历数据包

1)通过动态类型下的动态属性集合遍历数据包,

2)假设已知的动态数据包未dynObj,代码:


foreache(var proItem in dynObj.DynamicObjectType.Properties)
{
      var value1= proItem.GetValue(dynObj)
}


十.常见错误
1.实体类型{0}中不存在名为{1}的属性;
答:这是因为实体类型中的动态属性没有名称为{0}的动态属性   比如:dynData["不存在的属性名"],
 很可能是元数据跟代码版本不一致,或者打补丁后二开的字段丢失。
2. 寻找实体上{0}对应的属性描述符失败,实体不存在此属性![EntityType:{0} Propeyties:{1}];
   答:参考1
3.索引超出范围。必须为非负值并小于集合大小。
  答:这往往是因为创建了动态数据包后又去修改实体类型。其实我们看到的数据包是会动态改变大小的,在数据包中索引没有超出,
   这里的超出往往是触发了动态属性改变事件,记录值有改过时而发生的。
4. 输入的字符串格式不正确。
    答:给数据包赋值跟动态属性的类型不一致。