本文介绍了动态实体类型(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、表单数据包最外层是单据头的字段,单据体,子单据头的集合
三. 基础资料在数据包中的结构
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. 输入的字符串格式不正确。
答:给数据包赋值跟动态属性的类型不一致。
推荐阅读