Mercurial > pub > bltoolkit
comparison Source/Aspects/CacheAspect.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:f990fcb411a9 |
---|---|
1 using System; | |
2 using System.Collections; | |
3 using System.Collections.Generic; | |
4 using System.Reflection; | |
5 using System.Threading; | |
6 | |
7 namespace BLToolkit.Aspects | |
8 { | |
9 using Common; | |
10 | |
11 public delegate bool IsCacheableParameterType(Type parameterType); | |
12 | |
13 /// <summary> | |
14 /// http://www.bltoolkit.net/Doc/Aspects/index.htm | |
15 /// </summary> | |
16 [System.Diagnostics.DebuggerStepThrough] | |
17 public class CacheAspect : Interceptor | |
18 { | |
19 #region Init | |
20 | |
21 static CacheAspect() | |
22 { | |
23 MaxCacheTime = int.MaxValue; | |
24 IsEnabled = true; | |
25 | |
26 CleanupThread.Init(); | |
27 } | |
28 | |
29 public CacheAspect() | |
30 { | |
31 _registeredAspects.Add(this); | |
32 } | |
33 | |
34 private MethodInfo _methodInfo; | |
35 private int? _instanceMaxCacheTime; | |
36 private bool? _instanceIsWeak; | |
37 | |
38 public override void Init(CallMethodInfo info, string configString) | |
39 { | |
40 base.Init(info, configString); | |
41 | |
42 info.CacheAspect = this; | |
43 | |
44 _methodInfo = info.MethodInfo; | |
45 | |
46 var ps = configString.Split(';'); | |
47 | |
48 foreach (var p in ps) | |
49 { | |
50 var vs = p.Split('='); | |
51 | |
52 if (vs.Length == 2) | |
53 { | |
54 switch (vs[0].ToLower().Trim()) | |
55 { | |
56 case "maxcachetime": _instanceMaxCacheTime = int. Parse(vs[1].Trim()); break; | |
57 case "isweak": _instanceIsWeak = bool.Parse(vs[1].Trim()); break; | |
58 } | |
59 } | |
60 } | |
61 } | |
62 | |
63 private static readonly IList _registeredAspects = ArrayList.Synchronized(new ArrayList()); | |
64 protected static IList RegisteredAspects | |
65 { | |
66 get { return _registeredAspects; } | |
67 } | |
68 | |
69 public static CacheAspect GetAspect(MethodInfo methodInfo) | |
70 { | |
71 lock (RegisteredAspects.SyncRoot) | |
72 foreach (CacheAspect aspect in RegisteredAspects) | |
73 if (aspect._methodInfo == methodInfo) | |
74 return aspect; | |
75 | |
76 return null; | |
77 } | |
78 | |
79 #endregion | |
80 | |
81 #region Overrides | |
82 | |
83 protected override void BeforeCall(InterceptCallInfo info) | |
84 { | |
85 if (!IsEnabled) | |
86 return; | |
87 | |
88 var cache = Cache; | |
89 | |
90 lock (cache.SyncRoot) | |
91 { | |
92 var key = GetKey(info); | |
93 var item = GetItem(cache, key); | |
94 | |
95 if (item != null && !item.IsExpired) | |
96 { | |
97 info.InterceptResult = InterceptResult.Return; | |
98 info.ReturnValue = item.ReturnValue; | |
99 | |
100 if (item.RefValues != null) | |
101 { | |
102 var pis = info.CallMethodInfo.Parameters; | |
103 var n = 0; | |
104 | |
105 for (var i = 0; i < pis.Length; i++) | |
106 if (pis[i].ParameterType.IsByRef) | |
107 info.ParameterValues[i] = item.RefValues[n++]; | |
108 } | |
109 | |
110 info.Cached = true; | |
111 } | |
112 else | |
113 { | |
114 info.Items["CacheKey"] = key; | |
115 } | |
116 } | |
117 } | |
118 | |
119 protected override void AfterCall(InterceptCallInfo info) | |
120 { | |
121 if (!IsEnabled) | |
122 return; | |
123 | |
124 var cache = Cache; | |
125 | |
126 lock (cache.SyncRoot) | |
127 { | |
128 var key = (CompoundValue)info.Items["CacheKey"]; | |
129 | |
130 if (key == null) | |
131 return; | |
132 | |
133 var maxCacheTime = _instanceMaxCacheTime ?? MaxCacheTime; | |
134 var isWeak = _instanceIsWeak ?? IsWeak; | |
135 | |
136 var item = new CacheAspectItem | |
137 { | |
138 ReturnValue = info.ReturnValue, | |
139 MaxCacheTime = maxCacheTime == int.MaxValue || maxCacheTime < 0 ? | |
140 DateTime.MaxValue : | |
141 DateTime.Now.AddMilliseconds(maxCacheTime), | |
142 }; | |
143 | |
144 var pis = info.CallMethodInfo.Parameters; | |
145 var n = 0; | |
146 | |
147 foreach (var pi in pis) | |
148 if (pi.ParameterType.IsByRef) | |
149 n++; | |
150 | |
151 if (n > 0) | |
152 { | |
153 item.RefValues = new object[n]; | |
154 | |
155 n = 0; | |
156 | |
157 for (var i = 0; i < pis.Length; i++) | |
158 if (pis[i].ParameterType.IsByRef) | |
159 item.RefValues[n++] = info.ParameterValues[i]; | |
160 } | |
161 | |
162 cache[key] = isWeak? (object)new WeakReference(item): item; | |
163 } | |
164 } | |
165 | |
166 #endregion | |
167 | |
168 #region Global Parameters | |
169 | |
170 public static bool IsEnabled { get; set; } | |
171 public static int MaxCacheTime { get; set; } | |
172 public static bool IsWeak { get; set; } | |
173 | |
174 #endregion | |
175 | |
176 #region IsCacheableParameterType | |
177 | |
178 private static IsCacheableParameterType _isCacheableParameterType = | |
179 IsCacheableParameterTypeInternal; | |
180 | |
181 public static IsCacheableParameterType IsCacheableParameterType | |
182 { | |
183 get { return _isCacheableParameterType; } | |
184 set { _isCacheableParameterType = value ?? IsCacheableParameterTypeInternal; } | |
185 } | |
186 | |
187 private static bool IsCacheableParameterTypeInternal(Type parameterType) | |
188 { | |
189 return parameterType.IsValueType || parameterType == typeof(string); | |
190 } | |
191 | |
192 #endregion | |
193 | |
194 #region Cache | |
195 | |
196 private IDictionary _cache; | |
197 public IDictionary Cache | |
198 { | |
199 get { return _cache ?? (_cache = CreateCache()); } | |
200 } | |
201 | |
202 protected virtual CacheAspectItem CreateCacheItem(InterceptCallInfo info) | |
203 { | |
204 return new CacheAspectItem(); | |
205 } | |
206 | |
207 protected virtual IDictionary CreateCache() | |
208 { | |
209 return Hashtable.Synchronized(new Hashtable()); | |
210 } | |
211 | |
212 protected static CompoundValue GetKey(InterceptCallInfo info) | |
213 { | |
214 var parInfo = info.CallMethodInfo.Parameters; | |
215 var parValues = info.ParameterValues; | |
216 var keyValues = new object[parValues.Length]; | |
217 var cacheParams = info.CallMethodInfo.CacheableParameters; | |
218 | |
219 if (cacheParams == null) | |
220 { | |
221 info.CallMethodInfo.CacheableParameters = cacheParams = new bool[parInfo.Length]; | |
222 | |
223 for (var i = 0; i < parInfo.Length; i++) | |
224 cacheParams[i] = IsCacheableParameterType(parInfo[i].ParameterType); | |
225 } | |
226 | |
227 for (var i = 0; i < parValues.Length; i++) | |
228 keyValues[i] = cacheParams[i] ? parValues[i] : null; | |
229 | |
230 return new CompoundValue(keyValues); | |
231 } | |
232 | |
233 protected static CacheAspectItem GetItem(IDictionary cache, CompoundValue key) | |
234 { | |
235 var obj = cache[key]; | |
236 | |
237 if (obj == null) | |
238 return null; | |
239 | |
240 var wr = obj as WeakReference; | |
241 | |
242 if (wr == null) | |
243 return (CacheAspectItem)obj; | |
244 | |
245 obj = wr.Target; | |
246 | |
247 if (obj != null) | |
248 return (CacheAspectItem)obj; | |
249 | |
250 cache.Remove(key); | |
251 | |
252 return null; | |
253 } | |
254 | |
255 /// <summary> | |
256 /// Clear a method call cache. | |
257 /// </summary> | |
258 /// <param name="methodInfo">The <see cref="MethodInfo"/> representing cached method.</param> | |
259 public static void ClearCache(MethodInfo methodInfo) | |
260 { | |
261 if (methodInfo == null) | |
262 throw new ArgumentNullException("methodInfo"); | |
263 | |
264 var aspect = GetAspect(methodInfo); | |
265 | |
266 if (aspect != null) | |
267 CleanupThread.ClearCache(aspect.Cache); | |
268 } | |
269 | |
270 /// <summary> | |
271 /// Clear a method call cache. | |
272 /// </summary> | |
273 /// <param name="declaringType">The method declaring type.</param> | |
274 /// <param name="methodName">The method name.</param> | |
275 /// <param name="types">An array of <see cref="System.Type"/> objects representing | |
276 /// the number, order, and type of the parameters for the method to get.-or- | |
277 /// An empty array of the type <see cref="System.Type"/> (for example, <see cref="System.Type.EmptyTypes"/>) | |
278 /// to get a method that takes no parameters.</param> | |
279 public static void ClearCache(Type declaringType, string methodName, params Type[] types) | |
280 { | |
281 ClearCache(GetMethodInfo(declaringType, methodName, types)); | |
282 } | |
283 | |
284 /// <summary> | |
285 /// Clear a method call cache. | |
286 /// </summary> | |
287 /// <param name="declaringType">The method declaring type.</param> | |
288 /// <param name="methodName">The method name.</param> | |
289 /// <param name="types">An array of <see cref="System.Type"/> objects representing | |
290 /// the number, order, and type of the parameters for the method to get.-or- | |
291 /// An empty array of the type <see cref="System.Type"/> (for example, <see cref="System.Type.EmptyTypes"/>) | |
292 /// to get a method that takes no parameters.</param> | |
293 /// <param name="values">An array of values of the parameters for the method to get</param> | |
294 public static void ClearCache(Type declaringType, string methodName, Type[] types, object[] values) | |
295 { | |
296 var methodInfo = GetMethodInfo(declaringType, methodName, types); | |
297 | |
298 if (methodInfo == null) | |
299 throw new ArgumentNullException("methodInfo"); | |
300 | |
301 var aspect = GetAspect(methodInfo); | |
302 | |
303 if (aspect != null) | |
304 CleanupThread.ClearCache(aspect.Cache, new CompoundValue(values)); | |
305 } | |
306 | |
307 public static void ClearCache(Type declaringType) | |
308 { | |
309 if (declaringType == null) | |
310 throw new ArgumentNullException("declaringType"); | |
311 | |
312 if (declaringType.IsAbstract) | |
313 declaringType = TypeBuilder.TypeFactory.GetType(declaringType); | |
314 | |
315 lock (RegisteredAspects.SyncRoot) | |
316 foreach (CacheAspect aspect in RegisteredAspects) | |
317 if (aspect._methodInfo.DeclaringType == declaringType) | |
318 CleanupThread.ClearCache(aspect.Cache); | |
319 } | |
320 | |
321 public static MethodInfo GetMethodInfo(Type declaringType, string methodName, params Type[] parameterTypes) | |
322 { | |
323 if (declaringType == null) | |
324 throw new ArgumentNullException("declaringType"); | |
325 | |
326 if (declaringType.IsAbstract) | |
327 declaringType = TypeBuilder.TypeFactory.GetType(declaringType); | |
328 | |
329 if (parameterTypes == null) | |
330 parameterTypes = Type.EmptyTypes; | |
331 | |
332 var methodInfo = declaringType.GetMethod( | |
333 methodName, | |
334 BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, | |
335 null, | |
336 parameterTypes, | |
337 null); | |
338 | |
339 if (methodInfo == null) | |
340 { | |
341 methodInfo = declaringType.GetMethod( | |
342 methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); | |
343 | |
344 if (methodInfo == null) | |
345 throw new ArgumentException(string.Format("Method '{0}.{1}' not found.", | |
346 declaringType.FullName, methodName)); | |
347 } | |
348 | |
349 return methodInfo; | |
350 } | |
351 | |
352 /// <summary> | |
353 /// Clear all cached method calls. | |
354 /// </summary> | |
355 public static void ClearCache() | |
356 { | |
357 CleanupThread.ClearCache(); | |
358 } | |
359 | |
360 #endregion | |
361 | |
362 #region Cleanup Thread | |
363 | |
364 public class CleanupThread | |
365 { | |
366 private CleanupThread() {} | |
367 | |
368 internal static void Init() | |
369 { | |
370 AppDomain.CurrentDomain.DomainUnload += CurrentDomain_DomainUnload; | |
371 Start(); | |
372 } | |
373 | |
374 static void CurrentDomain_DomainUnload(object sender, EventArgs e) | |
375 { | |
376 Stop(); | |
377 } | |
378 | |
379 static volatile Timer _timer; | |
380 static readonly object _syncTimer = new object(); | |
381 | |
382 private static void Start() | |
383 { | |
384 if (_timer == null) | |
385 lock (_syncTimer) | |
386 if (_timer == null) | |
387 { | |
388 var interval = TimeSpan.FromSeconds(10); | |
389 _timer = new Timer(Cleanup, null, interval, interval); | |
390 } | |
391 } | |
392 | |
393 private static void Stop() | |
394 { | |
395 if (_timer != null) | |
396 lock (_syncTimer) | |
397 if (_timer != null) | |
398 { | |
399 _timer.Dispose(); | |
400 _timer = null; | |
401 } | |
402 } | |
403 | |
404 private static void Cleanup(object state) | |
405 { | |
406 if (!Monitor.TryEnter(RegisteredAspects.SyncRoot, 10)) | |
407 { | |
408 // The Cache is busy, skip this turn. | |
409 // | |
410 return; | |
411 } | |
412 | |
413 var start = DateTime.Now; | |
414 var objectsInCache = 0; | |
415 | |
416 try | |
417 { | |
418 _workTimes++; | |
419 | |
420 var list = new List<DictionaryEntry>(); | |
421 | |
422 foreach (CacheAspect aspect in RegisteredAspects) | |
423 { | |
424 var cache = aspect.Cache; | |
425 | |
426 lock (cache.SyncRoot) | |
427 { | |
428 foreach (DictionaryEntry de in cache) | |
429 { | |
430 var wr = de.Value as WeakReference; | |
431 | |
432 bool isExpired; | |
433 | |
434 if (wr != null) | |
435 { | |
436 var ca = wr.Target as CacheAspectItem; | |
437 | |
438 isExpired = ca == null || ca.IsExpired; | |
439 } | |
440 else | |
441 { | |
442 isExpired = ((CacheAspectItem)de.Value).IsExpired; | |
443 } | |
444 | |
445 if (isExpired) | |
446 list.Add(de); | |
447 } | |
448 | |
449 foreach (var de in list) | |
450 { | |
451 cache.Remove(de.Key); | |
452 _objectsExpired++; | |
453 } | |
454 | |
455 list.Clear(); | |
456 | |
457 objectsInCache += cache.Count; | |
458 } | |
459 } | |
460 | |
461 _objectsInCache = objectsInCache; | |
462 } | |
463 finally | |
464 { | |
465 _workTime += DateTime.Now - start; | |
466 | |
467 Monitor.Exit(RegisteredAspects.SyncRoot); | |
468 } | |
469 } | |
470 | |
471 private static int _workTimes; | |
472 public static int WorkTimes | |
473 { | |
474 get { return _workTimes; } | |
475 } | |
476 | |
477 private static TimeSpan _workTime; | |
478 public static TimeSpan WorkTime | |
479 { | |
480 get { return _workTime; } | |
481 } | |
482 | |
483 private static int _objectsExpired; | |
484 public static int ObjectsExpired | |
485 { | |
486 get { return _objectsExpired; } | |
487 } | |
488 | |
489 private static int _objectsInCache; | |
490 public static int ObjectsInCache | |
491 { | |
492 get { return _objectsInCache; } | |
493 } | |
494 | |
495 public static void UnregisterCache(IDictionary cache) | |
496 { | |
497 lock (RegisteredAspects.SyncRoot) | |
498 RegisteredAspects.Remove(cache); | |
499 } | |
500 | |
501 public static void ClearCache(IDictionary cache) | |
502 { | |
503 lock (RegisteredAspects.SyncRoot) lock (cache.SyncRoot) | |
504 { | |
505 _objectsExpired += cache.Count; | |
506 cache.Clear(); | |
507 } | |
508 } | |
509 | |
510 public static void ClearCache(IDictionary cache, CompoundValue key) | |
511 { | |
512 lock (RegisteredAspects.SyncRoot) | |
513 lock (cache.SyncRoot) | |
514 { | |
515 _objectsExpired += 1; | |
516 cache.Remove(key); | |
517 } | |
518 } | |
519 | |
520 public static void ClearCache() | |
521 { | |
522 lock (RegisteredAspects.SyncRoot) | |
523 { | |
524 foreach (CacheAspect aspect in RegisteredAspects) | |
525 { | |
526 _objectsExpired += aspect.Cache.Count; | |
527 aspect.Cache.Clear(); | |
528 } | |
529 } | |
530 } | |
531 } | |
532 | |
533 #endregion | |
534 } | |
535 } |