zoukankan      html  css  js  c++  java
  • YbSoftwareFactory 代码生成插件【十五】:Show 一下最新的动态属性扩展功能与键值生成器功能

        YbSoftwareFactory 各种插件的基础类库中又新增了两个方便易用的功能:动态属性扩展与键值生成器,本章将分别介绍这两个非常方便的组件。

    一、动态属性扩展 

        在实际的开发过程中,你肯定会遇到数据库字段不够用的情况,临时增加一个字段有时是很麻烦的一件事。例如需要修改 SQL 语句、视图、存储过程等等,即使你使用的是 ORM 组件,也需要增加和配置映射,每次修改完成后还需反复进行测试,非常的不方便,如果软件已经为客户部署好了的话,嘿嘿,不用说,肯定更让你头疼;而客户临时要添加新的字段的情况却是非常普遍的。另外,有些对象其实不适合放到一张主表中,即使这是 1:1 的关系,因为直接添加到一张表可能会存在一定的性能问题,例如图片、文件等信息,某些时候查询 N 多记录返回大量信息通常不是合理和明智的做法,在字段数量很多的情况下,对于某些不重要的字段信息保存到其他表中通常是可以提升查询性能的。

        本章介绍的动态属性扩展功能主要就是解决此类问题,可以灵活、方便的扩展属性。

        注:动态属性扩展组件主要面向正在开发中的审批流组件而设计的,其目的是为终端用户提供灵活、方便、易用的属性自定义的功能。动态属性扩展组件已集成到数据字典组件、组织机构管理组件中。 

        本组件具有如下显著特点:

    1. 自动完成动态属性值的加载和保存,通过键/值对的方式实现动态扩展属性的数据库保存和加载,非常的方便。如果你想玩得更高级点,可以直接从界面绑定一个动态属性,然后保存到数据库并能重新加载并绑定到界面上,这一过程无需你像某些软件类似的对所谓的元数据进行管理和配置,非常灵活。
    2. 能自动完成属性类型的转换,因为字段的属性值是通过键值对的方式保存到指定的数据库表中,因此需要把数据库中保存的文本型的属性值自动转换成指定的类型(如日期、整数、二进制信息)等。本文介绍的动态属性扩展功能可完成此类型的转换。
    3. 支持对象的序列化,这对于使用 WCF、Web Service、Web API 等类似的技术进行远程数据交互是很有必要的。 

        至于具体的实现原理,毫无疑问是利用了 .NET 4.0 的 Dynamic 特性,如下是核心基类的实现代码:

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 using System.Dynamic;
      5 using System.Reflection;
      6 
      7 namespace Yb.Data.Provider
      8 {
      9     [Serializable]
     10     public class ExtensionObject: DynamicObject, IDynamicMetaObjectProvider
     11     {
     12         object _instance;
     13 
     14         Type _instanceType;
     15         PropertyInfo[] _cacheInstancePropertyInfos;
     16         IEnumerable<PropertyInfo> _instancePropertyInfos
     17         {
     18             get
     19             {
     20                 if (_cacheInstancePropertyInfos == null && _instance != null)                
     21                     _cacheInstancePropertyInfos = _instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly);
     22                 return _cacheInstancePropertyInfos;                
     23             }
     24         }
     25 
     26         public ExtensionObject() 
     27         {
     28             Initialize(this);            
     29         }
     30 
     31         /// <remarks>
     32         /// You can pass in null here if you don't want to 
     33         /// check native properties and only check the Dictionary!
     34         /// </remarks>
     35         /// <param name="instance"></param>
     36         public ExtensionObject(object instance)
     37         {
     38             Initialize(instance);
     39         }
     40 
     41 
     42         protected virtual void Initialize(object instance)
     43         {
     44             _instance = instance;
     45             if (instance != null)
     46                 _instanceType = instance.GetType();           
     47         }
     48 
     49        /// <param name="binder"></param>
     50        /// <param name="result"></param>
     51        /// <returns></returns>
     52         public override bool TryGetMember(GetMemberBinder binder, out object result)
     53         {
     54             result = null;
     55 
     56             // first check the Properties collection for member
     57             if (Properties.Keys.Contains(binder.Name))
     58             {
     59                 result = Properties[binder.Name];
     60                 return true;
     61             }
     62 
     63 
     64             // Next check for Public properties via Reflection
     65             if (_instance != null)
     66             {
     67                 try
     68                 {
     69                     return GetProperty(_instance, binder.Name, out result);                    
     70                 }
     71                 catch (Exception)
     72                 { }
     73             }
     74 
     75             // failed to retrieve a property
     76             return false;
     77         }
     78 
     79         /// <param name="binder"></param>
     80         /// <param name="value"></param>
     81         /// <returns></returns>
     82         public override bool TrySetMember(SetMemberBinder binder, object value)
     83         {
     84 
     85             // first check to see if there's a native property to set
     86             if (_instance != null)
     87             {
     88                 try
     89                 {
     90                     bool result = SetProperty(_instance, binder.Name, value);
     91                     if (result)
     92                         return true;
     93                 }
     94                 catch { }
     95             }
     96             
     97             // no match - set or add to dictionary
     98             Properties[binder.Name] = value;
     99             return true;
    100         }
    101 
    102         /// <param name="binder"></param>
    103         /// <param name="args"></param>
    104         /// <param name="result"></param>
    105         /// <returns></returns>
    106         public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    107         {
    108             if (_instance != null)
    109             {
    110                 try
    111                 {
    112                     // check instance passed in for methods to invoke
    113                     if (InvokeMethod(_instance, binder.Name, args, out result))
    114                         return true;                    
    115                 }
    116                 catch (Exception)
    117                 { }
    118             }
    119 
    120             result = null;
    121             return false;
    122         }
    123         
    124         /// <param name="instance"></param>
    125         /// <param name="name"></param>
    126         /// <param name="result"></param>
    127         /// <returns></returns>
    128         protected bool GetProperty(object instance, string name, out object result)
    129         {
    130             if (instance == null)
    131                 instance = this;
    132 
    133             var miArray = _instanceType.GetMember(name, BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.Instance);
    134             if (miArray != null && miArray.Length > 0)
    135             {
    136                 var mi = miArray[0];
    137                 if (mi.MemberType == MemberTypes.Property)
    138                 {
    139                     result = ((PropertyInfo)mi).GetValue(instance,null);
    140                     return true;
    141                 }
    142             }
    143 
    144             result = null;
    145             return false;                
    146         }
    147 
    148         /// <param name="instance"></param>
    149         /// <param name="name"></param>
    150         /// <param name="value"></param>
    151         /// <returns></returns>
    152         protected bool SetProperty(object instance, string name, object value)
    153         {
    154             if (instance == null)
    155                 instance = this;
    156 
    157             var miArray = _instanceType.GetMember(name, BindingFlags.Public | BindingFlags.SetProperty | BindingFlags.Instance);
    158             if (miArray != null && miArray.Length > 0)
    159             {
    160                 var mi = miArray[0];
    161                 if (mi.MemberType == MemberTypes.Property)
    162                 {
    163                     ((PropertyInfo)mi).SetValue(_instance, value, null);
    164                     return true;
    165                 }
    166             }
    167             return false;                
    168         }
    169 
    170         /// <param name="instance"></param>
    171         /// <param name="name"></param>
    172         /// <param name="args"></param>
    173         /// <param name="result"></param>
    174         /// <returns></returns>
    175         protected bool InvokeMethod(object instance, string name, object[] args, out object result)
    176         {
    177             if (instance == null)
    178                 instance = this;
    179 
    180             // Look at the instanceType
    181             var miArray = _instanceType.GetMember(name,
    182                                     BindingFlags.InvokeMethod |
    183                                     BindingFlags.Public | BindingFlags.Instance);
    184 
    185             if (miArray != null && miArray.Length > 0)
    186             {
    187                 var mi = miArray[0as MethodInfo;
    188                 result = mi.Invoke(_instance, args);
    189                 return true;
    190             }
    191 
    192             result = null;
    193             return false;
    194         }
    195 
    196         public object this[string key]
    197         {
    198             get
    199             {
    200                 try
    201                 {
    202                     // try to get from properties collection first
    203                     return Properties[key];
    204                 }
    205                 catch (KeyNotFoundException ex)
    206                 {
    207                     // try reflection on instanceType
    208                     object result = null;
    209                     if (GetProperty(_instance, key, out result))
    210                         return result;
    211 
    212                     // nope doesn't exist
    213                     throw;
    214                 }
    215             }
    216             set
    217             {
    218                 if (Properties.ContainsKey(key))
    219                 {
    220                     Properties[key] = value;
    221                     return;
    222                 }
    223 
    224                 // check instance for existance of type first
    225                 var miArray = _instanceType.GetMember(key, BindingFlags.Public | BindingFlags.GetProperty);
    226                 if (miArray != null && miArray.Length > 0)
    227                     SetProperty(_instance, key, value);
    228                 else
    229                     Properties[key] = value;
    230             }
    231         }
    232 
    233         /// <param name="includeInstanceProperties"></param>
    234         /// <returns></returns>
    235         public IEnumerable<KeyValuePair<string,object>> GetProperties(bool includeInstanceProperties = false)
    236         {
    237             if (includeInstanceProperties && _instance != null)
    238             {
    239                 foreach (var prop in this._instancePropertyInfos)
    240                     yield return new KeyValuePair<stringobject>(prop.Name, prop.GetValue(_instance, null));
    241             }
    242 
    243             foreach (var key in this.Properties.Keys)
    244                yield return new KeyValuePair<stringobject>(key, this.Properties[key]);
    245 
    246         }
    247 
    248         /// <param name="item"></param>
    249         /// <param name="includeInstanceProperties"></param>
    250         /// <returns></returns>
    251         public bool Contains(KeyValuePair<stringobject> item, bool includeInstanceProperties = false)
    252         {
    253             bool res = Properties.ContainsKey(item.Key);
    254             if (res)
    255                 return true;
    256 
    257             if (includeInstanceProperties && _instance != null)
    258             {
    259                 foreach (var prop in this._instancePropertyInfos)
    260                 {
    261                     if (prop.Name == item.Key)
    262                         return true;
    263                 }
    264             }
    265 
    266             return false;
    267         }
    268         /// <param name="key"></param>
    269         /// <returns></returns>
    270         public bool Contains(string key, bool includeInstanceProperties = false)
    271         {
    272             bool res = Properties.ContainsKey(key);
    273             if (res)
    274                 return true;
    275 
    276             if (includeInstanceProperties && _instance != null)
    277             {
    278                 foreach (var prop in this._instancePropertyInfos)
    279                 {
    280                     if (prop.Name == key)
    281                         return true;
    282                 }
    283             }
    284 
    285             return false;
    286         }
    287         
    288     }
    289 }
    ExtensionObject

        具体的使用,仅需继承该对象即可。为了更好的说明具体用法,请查看如下已测试通过的单元测试代码:

      1         [Serializable]
      2         public class User : ExtensionObject
      3         {
      4             public Guid UserId { getset; }
      5             public string Email { getset; }
      6             public string Password { getset; }
      7             public string Name { getset; }
      8             public bool Active { getset; }
      9             public DateTime? ExpiresOn { getset; }
     10 
     11             public User()
     12                 : base()
     13             { }
     14 
     15             // only required if you want to mix in seperate instance
     16             public User(object instance)
     17                 : base(instance)
     18             {
     19             }
     20         }
     21 
     22         /// <summary>
     23         /// ExtensionData 的测试
     24         ///</summary>
     25         [TestMethod()]
     26         public void ExtensionObjectTest()
     27         {
     28             //清空数据库存储的属性值,方便进行测试
     29             ExtensionDataApi.ClearExtensionDataOfApplication();
     30 
     31             var user = new User();
     32             // 设置已有属性
     33             dynamic duser = user;
     34             user.UserId = Guid.NewGuid();
     35             duser.Email = "19892257@qq.com";
     36             user.Password = "YbSofteareFactory";
     37 
     38             // 设置动态属性
     39             duser.FriendUserName = "YB";
     40             duser.CreatedDate = DateTime.Now;
     41             duser.TodayNewsCount = 1;
     42             duser.Age = 27.5;
     43             duser.LastUpdateId = (Guid?)null;
     44             duser.LastUpdatedDate=null;
     45 
     46             // 动态属性值保存
     47             ExtensionDataApi.SaveExtensionObject(user.UserId,user);
     48             
     49             // 从数据库中加载属性值
     50             var obj = user.LoadExtensionData(user.UserId);
     51             
     52             // 测试是否加载正确
     53             Assert.AreEqual(obj.FriendUserName, "YB");
     54             Assert.IsNotNull(obj.CreatedDate);
     55             Assert.AreEqual(obj.TodayNewsCount, 1);
     56             Assert.AreEqual(obj.Age, 27.5);
     57             Assert.IsNull(obj.LastUpdateId);
     58             Assert.IsNull(obj.LastUpdatedDate);
     59 
     60             var items = ExtensionDataApi.FindExtensionDataBy(user.UserId.ToString(), user);
     61             //测试保存的动态属性数
     62             Assert.IsTrue(items.Count() == 6);
     63 
     64             // 修改动态属性值
     65             duser.Age = 28;
     66             // 新增动态属性
     67             duser.Tag = null;
     68             duser.NewProperty = 12;
     69             //使用扩展方法进行保存动态属性值至数据库
     70             user.SaveExtensionData(user.UserId);
     71             
     72             items = ExtensionDataApi.FindExtensionDataBy(user.UserId.ToString(), user);
     73             //判断保存的属性数量是否正确
     74             Assert.IsTrue(items.Count() == 8);
     75 
     76             //使用扩展方法动态从数据库中加载属性
     77             obj = user.LoadExtensionData(user.UserId);
     78 
     79             Assert.AreEqual(obj.Tag, null);
     80             Assert.AreEqual(obj.NewProperty, 12);
     81 
     82             duser.ComplexObject = user;
     83 
     84             //设置新值
     85             duser.Tag = true;
     86             ExtensionDataApi.SaveExtensionObject(user.UserId, user);
     87             obj = ExtensionDataApi.LoadExtensionObject(user.UserId, user);
     88             // 验证加载的属性新值是否正确
     89             Assert.IsTrue(obj.Tag);
     90 
     91             //返回对象数组的属性字典方法测试
     92             var dic = ExtensionDataApi.FindExtensionDataDictionaryBy(new string[]{user.UserId.ToString()}, user.GetType().FullName);
     93             Assert.IsTrue(dic.Count>0);
     94 
     95             //byte[] 测试,对可方便存储文件、图片等内容
     96             duser.Image = new byte[] {225524123616191283290};
     97             ExtensionDataApi.SaveExtensionObject(user.UserId, user);
     98             obj = ExtensionDataApi.LoadExtensionObject(user.UserId, user);
     99             Assert.AreEqual(obj.Image.Length, 9);
    100             Assert.AreEqual(obj.Image[8], 90);
    101 
    102             //Json 序列化测试,对 Web Api 等非常重要
    103             string json = JsonConvert.SerializeObject(duser, Formatting.Indented, new JsonSerializerSettings
    104             {
    105                 TypeNameHandling = TypeNameHandling.All,
    106                 TypeNameAssemblyFormat = FormatterAssemblyStyle.Full
    107             });
    108             Assert.IsNotNull(json);
    109             json = JsonConvert.SerializeObject(user);
    110             Assert.IsNotNull(json);
    111         }
    ExtensionObjectTest

    二、键值生成器

        键值的生成看似简单,其实实现起来却并不容易,因为这里面有并发性、生成效率等等方面的考虑。同时,对键值的管理也是非常重要的,试想想,不同位置的两个客户端同时生成了相同的键值是什么后果吧。

        本章要介绍的键值生成器组件非常灵活和高效,它具有如下非常实用的功能:

    1. 支持绝大多数情况下指定格式的键值生成,例如可指定前缀、后缀、客户端应用程序编号(多客户端下非常有用)、日期(例如yyyy、yyyyMM、yyyyMMdd、yyyyMMddHH等)以及流水号长度。
    2. 支持批量生成键值,一次可以生成指定数量的键值组。
    3. 在满足特定性能的前提下,可有效解决常见的并发情况,有效防止键值冲突。

       对于具体的使用方式,同样还是来看看已通过测试的部分单元测试代码:

      1         /// <summary>
      2         ///GetNextID 的测试
      3         ///</summary>
      4         [TestMethod()]
      5         public void GetNextIDTest()
      6         {
      7             IdGeneratorApi.ClearAllIdGenerator();
      8 
      9             var user = new User();
     10 
     11             //生成类似 U-01-201308-001格式的ID,%A表示输出客户端编号,%D表示输出日期时间
     12             var idGen = new IdGenerator()
     13                 {
     14                     Type = typeof (User).FullName,
     15                     DateFormat = "yyyyMM",
     16                     GenFormat = "U-%A-%D-",
     17                     Id = Guid.NewGuid(),
     18                     StartValue = 1,
     19                     NextValue = 1,
     20                     ValueLength = 3
     21                 };
     22             //API基本方法测试
     23             IdGeneratorApi.SaveOrUpdateIdGenerator(idGen);
     24             var item = IdGeneratorApi.GetIdGeneratorBy(idGen.Id);
     25             Assert.IsNotNull(item);
     26             item = IdGeneratorApi.GetIdGeneratorBy(user);
     27             Assert.IsNotNull(item);
     28             item = IdGeneratorApi.GetIdGeneratorBy("not exist's record");
     29             Assert.IsNull(item);
     30             //API基本方法测试
     31             Assert.IsTrue(IdGeneratorApi.IdGeneratorExists(user));
     32             Assert.IsFalse(IdGeneratorApi.IdGeneratorExists("dkakd_test_a"));
     33 
     34             //生成ID号
     35             var str = IdGeneratorApi.GetNextID(user);
     36             Assert.AreEqual("U-02-201308-001", str);
     37             str = IdGeneratorApi.GetNextID(user);
     38             Assert.AreEqual("U-02-201308-002", str);
     39 
     40             idGen = IdGeneratorApi.GetIdGeneratorBy(idGen.Id);
     41             //无需生成日期,当前生成的ID号类似于U-02--003
     42             idGen.DateFormat = string.Empty;
     43 
     44             IdGeneratorApi.SaveOrUpdateIdGenerator(idGen);
     45             idGen = IdGeneratorApi.GetIdGeneratorBy(idGen.Id);
     46 
     47             //生成下一ID号
     48             str = IdGeneratorApi.GetNextID(user);
     49             Assert.AreEqual("U-02--003", str);
     50             str = IdGeneratorApi.GetNextID(user);
     51             Assert.AreEqual("U-02--004", str);
     52 
     53             idGen = IdGeneratorApi.GetIdGeneratorBy(idGen.Id);
     54             // 如下代码修改生成的ID号类似于U-0005-
     55             idGen.DateFormat = "yyyyMM";
     56             //未设置%D,将不再输出日期
     57             idGen.GenFormat = "U-%v-";
     58             //修改生成编号的长度为4
     59             idGen.ValueLength = 4
     60             IdGeneratorApi.SaveOrUpdateIdGenerator(idGen);
     61 
     62             str = IdGeneratorApi.GetNextID(user);
     63             Assert.AreEqual("U-0005-", str);
     64             str = IdGeneratorApi.GetNextID(user);
     65             Assert.AreEqual("U-0006-", str);
     66 
     67             //API基本方法测试
     68             IdGeneratorApi.DeleteIdGenerator(idGen);
     69             item = IdGeneratorApi.GetIdGeneratorBy(idGen.Id);
     70             Assert.IsNull(item);
     71             item = IdGeneratorApi.GetIdGeneratorBy(user);
     72             Assert.IsNull(item);
     73 
     74             IdGeneratorApi.ClearAllIdGeneratorOfApplication();
     75         }
     76 
     77         /// <summary>
     78         ///GetNextGroupID 的测试,批量生产ID号
     79         ///</summary>
     80         [TestMethod()]
     81         public void GetNextGroupIDTest()
     82         {
     83             IdGeneratorApi.ClearAllIdGeneratorOfApplication();
     84 
     85             var user = new User();
     86 
     87             var idGen = new IdGenerator()
     88             {
     89                 Type = typeof(User).FullName,
     90                 DateFormat = "yyyyMM",
     91                 GenFormat = "U-%a-%D-%v",
     92                 Id = Guid.NewGuid(),
     93                 StartValue = 1,
     94                 NextValue = 1,
     95                 ValueLength = 3
     96             };
     97 
     98             IdGeneratorApi.SaveOrUpdateIdGenerator(idGen);
     99 
    100             //批量生成3个ID号
    101             var str = IdGeneratorApi.GetNextGroupID(user,3);
    102             Assert.IsTrue(str.Length==3);
    103             Assert.IsTrue(str[0]=="U-02-201308-001");
    104             Assert.IsTrue(str[1]=="U-02-201308-002");
    105             Assert.IsTrue(str[2]=="U-02-201308-003");
    106 
    107             idGen = IdGeneratorApi.GetIdGeneratorBy(idGen.Id);
    108             // 如下修改将生成类似于T0004的ID,将忽略日期和客户端编号
    109             idGen.GenFormat = "T%v";
    110             idGen.ValueLength = 4;
    111             IdGeneratorApi.SaveOrUpdateIdGenerator(idGen);
    112 
    113             str = IdGeneratorApi.GetNextGroupID(user,2);
    114             Assert.IsTrue(str.Length==2);
    115             Assert.IsTrue(str[0]=="T0004");
    116             Assert.IsTrue(str[1]=="T0005");
    117 
    118             idGen = IdGeneratorApi.GetIdGeneratorBy(idGen.Id);
    119             //修改生成的ID格式
    120             idGen.DateFormat = "yyyy";
    121             //生成类似于01-0010/2013的ID号,%a为客户端编号,%v为流水号,%d将输出日期时间,此处为年份
    122             idGen.GenFormat = "%a-%v/%d";
    123             //指明流水号长度为4,类似于0001
    124             idGen.ValueLength = 4;
    125             IdGeneratorApi.SaveOrUpdateIdGenerator(idGen);
    126 
    127             str = IdGeneratorApi.GetNextGroupID(user,2);
    128             Assert.IsTrue(str.Length==2);
    129             Assert.IsTrue(str[0]=="02-0001/2013");
    130             Assert.IsTrue(str[1]=="02-0002/2013");
    131 
    132             IdGeneratorApi.ClearAllIdGenerator();
    133         }
    134 
    135         public class User
    136         {
    137             public string Id { getset; }
    138             public string UserName { getset; }
    139         }
    IdGeneratorTest

       目前的开发重心将逐渐向审批流的开发过渡,未来的审批流组件将由表单设计器、流程设计器和审批流底层组件三大部分组成,具有灵活、简单、易用的特点,如下是流程设计器的预览界面:

     

  • 相关阅读:
    二进制流 最后一段数据是最后一次读取的byte数组没填满造成的
    java中的匿名内部类总结
    决策树构建算法之—C4.5
    Segment公司--整合数据进行分析
    UBuntu安裝使用PIP
    undefined reference to “boost” in Qt—Ubuntu
    Ubuntu14.04引导菜单修复
    ubuntu16.04下编译安装OpenCV
    PCL:Ubuntu下安装配置PCL
    Ubuntu安装配置Python.pyDev
  • 原文地址:https://www.cnblogs.com/gyche/p/3223341.html
Copyright © 2011-2022 走看看