在项目中,经常会需要对一些特定的业务对象进行属性的扩展,而且这些属性的扩展还具备极不可预测性、相互关系松散等特点。大部分的开发人员是最讨厌这类涉及到数据字段扩展的需求变更。这种调整,轻则数据要加字段,重则程序代码要做大量的调整。在几微助手的开发过程中,也会涉及大量的类似需求的变更、扩展,甚至随着业务发展,自然就有大量的新的东东需要进行扩展。不需要在修改数据库字段、增加字段/属性后不需要大范围的代码调整合,可以根据业务需求改变储存5、未来的扩展开发只需要关注属性扩展可以业务逻辑,无需重复底层的数据开发
基于以上目标,我们初步在整理一下开发/设计思路:预定义、代码级扩展、配置文件扩展,理论上支持这三种方式,后期的代码维护就简单了
好了下面开始!先做好数据库设计:
这里很简单,数据库里面就两张表(SettingExtensions:通用扩展表,ModuleItemSettiingExtensions:单项个别扩展表),这里我们线建立一个概念,将所有扩展看做是原主体对象的设置。另外,有些主体对象可能还是一个集合,例如产品,那么这里我们可以把全部产品的管理当成一个主模块,对于这个主模块,可能会有需要扩展的通用的属性,那么在这个模块内的每个产品项(这里我们展示叫ModuleItem)还会有自己的单独扩展项,可能是从通用继承,可能是特殊的。
所以数据库就这两张表,一张放通用、一张放个别,如果需要开发的对象无所谓通用、个别的关系,那么我就统一放在SettingExtensions。关于字段,就比较简单了,主要是Key,Value字段,value是一个长文本字段,便于未来可以存放json、转成base64的二进制等复杂对象。另外在SettingExtensions里面多了一个TypeName,这个主要是区别一下模块类型名称,众多模块如果放在一个数据库,Key是有可能重复的。在ModuleItemSettiingExtensions表里面,ModuleType是记录当前个别项所属的集合类型名,同SettingExtensions->TypeName,ModuleItemID就是当前Item主体在系统中的唯一ID,例如:这个项是扩展产品的属性,那这里就应该是产品ID
开始写代码,先自定义以个Key-Value的对象,这里我们从DictionaryBase继承,在此基础上扩展,主要是忽略掉Key的大小写(我不喜欢大小写敏感的Key)其实用系统默认提供的一些字典对象也可以干,不过我比较喜欢强类型,这里就全自己定义个类型,在代码里面看到这个东东就知道这个玩意是我自己的一个扩展属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | /// <summary> /// 自定义设置的字典对象,继承DictionaryBase, /// 在SettingDictionary实现中索引或者设置键值对,对键key的大小写不敏感 /// </summary> public class SettingDictionary:DictionaryBase { public event ItemChanged OnItemChanged; /// <summary> /// 添加键值对到hashtable /// </summary> /// <param name="key"></param> /// <param name="value"></param> public virtual void Add( string key, string value) { this .InnerHashtable.Add(key.ToLower(), value); } /// <summary> /// 获取或设置键值对中的值 /// </summary> /// <param name="key"></param> /// <returns></returns> public string this [ string key] { get { return ( string ) this .InnerHashtable[key.ToLower()]; } set { if ( this .ContainsKey(key) || ! this .InnerHashtable[key.ToLower()].Equals(value)) { if ( this .OnItemChanged != null ) this .OnItemChanged( this , key); } this .InnerHashtable[key.ToLower()] = value; } } /// <summary> /// 判断关键字是否存在 /// </summary> /// <param name="key"></param> /// <returns></returns> public bool ContainsKey( string key) { return this .InnerHashtable.ContainsKey(key.ToLower()); } private class SettingDictionaryEnumerator : IDictionaryEnumerator { DictionaryEntry[] items; Int32 index = -1; public SettingDictionaryEnumerator(SettingDictionary settings) { items = new DictionaryEntry[settings.Count]; settings.CopyTo(items, 0); //items.OrderBy(m => m.Key); //Array.Reverse(items); } // 返回当前项 public Object Current { get { ValidateIndex(); return new SettingItem { Key = items[index].Key.ToString(), Value = items[index].Value.ToString() }; } } // 返回当前字典实例 public DictionaryEntry Entry { get { return (DictionaryEntry)Current; } } // 返回当前项的Key public Object Key { get { ValidateIndex(); return items[index].Key; } } // 返回当前项目的Value public Object Value { get { ValidateIndex(); return items[index].Value; } } public Boolean MoveNext() { if (index < items.Length - 1) { index++; return true ; } return false ; } private void ValidateIndex() { if (index < 0 || index >= items.Length) throw new InvalidOperationException( "超出项目索引边界" ); } public void Reset() { index = -1; } } public IDictionaryEnumerator GetEnumerator() { return new SettingDictionaryEnumerator( this ); } } |
为字典对象的单项定义一个强类型结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /// <summary> /// 设置项目的结构 /// </summary> public struct SettingItem { private string _key; /// <summary> /// 设置实例的键(名称) /// </summary> public string Key { get { return this ._key.ToLower(); } set { this ._key = value.ToLower(); } } /// <summary> /// 设置实例的值 /// </summary> public string Value { get ; set ; } } |