YbSoftwareFactory 各种插件的基础类库中又新增了两个方便易用的功能:动态属性扩展与键值生成器,本章将分别介绍这两个非常方便的组件。
一、动态属性扩展
在实际的开发过程中,你肯定会遇到数据库字段不够用的情况,临时增加一个字段有时是很麻烦的一件事。例如需要修改 SQL 语句、视图、存储过程等等,即使你使用的是 ORM 组件,也需要增加和配置映射,每次修改完成后还需反复进行测试,非常的不方便,如果软件已经为客户部署好了的话,嘿嘿,不用说,肯定更让你头疼;而客户临时要添加新的字段的情况却是非常普遍的。另外,有些对象其实不适合放到一张主表中,即使这是 1:1 的关系,因为直接添加到一张表可能会存在一定的性能问题,例如图片、文件等信息,某些时候查询 N 多记录返回大量信息通常不是合理和明智的做法,在字段数量很多的情况下,对于某些不重要的字段信息保存到其他表中通常是可以提升查询性能的。
本章介绍的动态属性扩展功能主要就是解决此类问题,可以灵活、方便的扩展属性。
注:动态属性扩展组件主要面向正在开发中的审批流组件而设计的,其目的是为终端用户提供灵活、方便、易用的属性自定义的功能。动态属性扩展组件已集成到数据字典组件、组织机构管理组件中。
本组件具有如下显著特点:
- 自动完成动态属性值的加载和保存,通过键/值对的方式实现动态扩展属性的数据库保存和加载,非常的方便。如果你想玩得更高级点,可以直接从界面绑定一个动态属性,然后保存到数据库并能重新加载并绑定到界面上,这一过程无需你像某些软件类似的对所谓的元数据进行管理和配置,非常灵活。
- 能自动完成属性类型的转换,因为字段的属性值是通过键值对的方式保存到指定的数据库表中,因此需要把数据库中保存的文本型的属性值自动转换成指定的类型(如日期、整数、二进制信息)等。本文介绍的动态属性扩展功能可完成此类型的转换。
- 支持对象的序列化,这对于使用 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[0] as 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<string, object>(prop.Name, prop.GetValue(_instance, null));
241 }
242
243 foreach (var key in this.Properties.Keys)
244 yield return new KeyValuePair<string, object>(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<string, object> 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 }
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[0] as 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<string, object>(prop.Name, prop.GetValue(_instance, null));
241 }
242
243 foreach (var key in this.Properties.Keys)
244 yield return new KeyValuePair<string, object>(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<string, object> 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 }