PageRenderTime 68ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/branches/v2.0 trunk/AjaxControlFramework/Reflection/MethodCache.cs

#
C# | 316 lines | 237 code | 64 blank | 15 comment | 67 complexity | 0764daf2dc8753946a1354a277fd372e MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Reflection.Emit;
  6. using System.Text;
  7. using System.Web;
  8. namespace AjaxControlFramework.Reflection
  9. {
  10. public delegate object MethodInvoke(object instance, object[] parameters);
  11. public static class MethodCache
  12. {
  13. //------// Fields \\------------------------------------------------\\
  14. private static object InternalMethodCollectionLock = new object();
  15. private static ReflectiveCacheCollection<MethodCacheEntry> InternalMethodCollection
  16. {
  17. get
  18. {
  19. ReflectiveCacheCollection<MethodCacheEntry> collection = (ReflectiveCacheCollection<MethodCacheEntry>)HttpContext.Current.Cache.Get("AjaxControlFramework.Reflection.MethodCache.InternalMethodCollection");
  20. if (collection == null)
  21. {
  22. collection = new ReflectiveCacheCollection<MethodCacheEntry>();
  23. HttpContext.Current.Cache.Insert("AjaxControlFramework.Reflection.MethodCache.InternalMethodCollection", collection, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 20, 0));
  24. }
  25. return collection;
  26. }
  27. }
  28. //------\\ Fields //------------------------------------------------//
  29. //------// Methods \\-----------------------------------------------\\
  30. /// <returns>Returns true if the method successfully resolved and was invoked, false otherwise.</returns>
  31. public static bool InvokeMethod<T>(object instance, string methodName, out object returnValue, object[] parameters)
  32. {
  33. return InvokeMethod(instance, typeof(T), methodName, out returnValue, parameters);
  34. }
  35. /// <returns>Returns true if the method successfully resolved and was invoked, false otherwise.</returns>
  36. public static bool InvokeMethod(object instance, Type instanceType, string methodName, out object returnValue, object[] parameters)
  37. {
  38. return InvokeMethod(instance, instanceType, methodName, out returnValue, parameters, null);
  39. }
  40. /// <returns>Returns true if the method successfully resolved and was invoked, false otherwise.</returns>
  41. public static bool InvokeMethod(object instance, Type instanceType, string methodName, out object returnValue, object[] parameters, AuthenticationStrategy authenticationStrategy)
  42. {
  43. if (instanceType == null)
  44. {
  45. if (instance == null) { throw new ArgumentNullException("instanceType", "When the \"instance\" parameter is null, the method to be invoked is considered to be static. In these cases where \"instance\" is null, the \"instanceType\" parameter may not be null."); }
  46. else { throw new ArgumentNullException("instanceType"); }
  47. }
  48. MethodCacheEntry cacheEntry = null;
  49. lock (InternalMethodCollectionLock)
  50. {
  51. cacheEntry = InternalMethodCollection[instanceType, methodName];
  52. }
  53. if (cacheEntry != null)
  54. {
  55. if (cacheEntry.Delegate == null)
  56. {
  57. returnValue = null;
  58. return false;
  59. }
  60. else
  61. {
  62. if (cacheEntry.Attribute.Protected && authenticationStrategy != null && (!authenticationStrategy.Authenticated || !authenticationStrategy.UserIsAuthorized(cacheEntry.Attribute.AuthorizedRoles)))
  63. {
  64. returnValue = null;
  65. return false;
  66. }
  67. else
  68. {
  69. returnValue = cacheEntry.Delegate(instance, parameters);
  70. return true;
  71. }
  72. }
  73. }
  74. else
  75. {
  76. // Check to see if the method collection cache for the entire instance type has expired.
  77. if (InternalMethodCollection[instanceType] == null)
  78. {
  79. // If nothing is in the cache for the instance type, re-query for AjaxControlMethods and cache them...
  80. CacheMethodsOfType(instanceType);
  81. // ...then check again for the desired method in the newly cached collection of AjaxControlMethods.
  82. lock (InternalMethodCollectionLock)
  83. {
  84. cacheEntry = InternalMethodCollection[instanceType, methodName];
  85. }
  86. if (cacheEntry != null)
  87. {
  88. // If the cached method can now be found, attempt to invoke it.
  89. if (cacheEntry.Delegate == null)
  90. {
  91. returnValue = null;
  92. return false;
  93. }
  94. else
  95. {
  96. if (cacheEntry.Attribute.Protected && authenticationStrategy != null && (!authenticationStrategy.Authenticated || !authenticationStrategy.UserIsAuthorized(cacheEntry.Attribute.AuthorizedRoles)))
  97. {
  98. returnValue = null;
  99. return false;
  100. }
  101. else
  102. {
  103. returnValue = cacheEntry.Delegate(instance, parameters);
  104. return true;
  105. }
  106. }
  107. }
  108. else
  109. {
  110. // If it can't be found (yet again) then bail out.
  111. returnValue = null;
  112. return false;
  113. }
  114. }
  115. else
  116. {
  117. MethodInvoke method = CacheMethod(instanceType, methodName, GetParameterTypes(parameters));
  118. if (method == null)
  119. {
  120. returnValue = null;
  121. return false;
  122. }
  123. else
  124. {
  125. if (cacheEntry.Attribute.Protected && authenticationStrategy != null && (!authenticationStrategy.Authenticated || !authenticationStrategy.UserIsAuthorized(cacheEntry.Attribute.AuthorizedRoles)))
  126. {
  127. returnValue = null;
  128. return false;
  129. }
  130. else
  131. {
  132. returnValue = method(instance, parameters);
  133. return true;
  134. }
  135. }
  136. }
  137. }
  138. }
  139. private static Type[] GetParameterTypes(object[] parameters)
  140. {
  141. Type[] parameterTypes = new Type[parameters.Length];
  142. for (int index = 0; index < parameters.Length; index++)
  143. {
  144. parameterTypes[index] = parameters[index].GetType();
  145. }
  146. return parameterTypes;
  147. }
  148. public static MethodInvoke CacheMethod(Type instanceType, string methodName, params Type[] parameterTypes)
  149. {
  150. if (instanceType == null) { throw new ArgumentNullException("instanceType"); }
  151. MethodInfo methodInfo = instanceType.GetMethod(methodName, BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance, null, parameterTypes, null);
  152. if (methodInfo == null) { throw new InvalidOperationException(String.Format("A method of the name {0} and parameter types provided could not be found.", methodName)); }
  153. AjaxControlMethodAttribute methodAttribute = (AjaxControlMethodAttribute)methodInfo.GetCustomAttributes(typeof(AjaxControlMethodAttribute), true).FirstOrDefault();
  154. return CacheMethod(instanceType, methodAttribute, methodInfo);
  155. }
  156. public static MethodInvoke CacheMethod(Type instanceType, string actualMethodName, string renderedMethodName, params Type[] parameterTypes)
  157. {
  158. if (instanceType == null) { throw new ArgumentNullException("instanceType"); }
  159. MethodInfo methodInfo = instanceType.GetMethod(actualMethodName, BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance, null, parameterTypes, null);
  160. if (methodInfo == null) { throw new InvalidOperationException(String.Format("A method of the name {0} and parameter types provided could not be found.", actualMethodName)); }
  161. return CacheMethod(instanceType, new AjaxControlMethodAttribute(renderedMethodName), methodInfo);
  162. }
  163. public static MethodInvoke CacheMethod(Type instanceType, AjaxControlMethodAttribute methodAttribute, MethodInfo methodInfo)
  164. {
  165. if (instanceType == null) { throw new ArgumentNullException("instanceType"); }
  166. if (methodAttribute == null) { throw new ArgumentNullException("methodAttribute"); }
  167. if (methodInfo == null) { throw new ArgumentNullException("methodInfo"); }
  168. string methodName = (methodAttribute.Name == null || methodAttribute.Name.Trim().Length == 0 ? methodInfo.Name : methodAttribute.Name.Trim());
  169. // Locking isn't going any further than this. Won't be checking again after the lock to see if the entry is STILL not in the
  170. // internal collection as I want to allow for overwritting of cache entries.
  171. lock (InternalMethodCollectionLock)
  172. {
  173. MethodCacheEntry cacheEntry = new MethodCacheEntry(methodName, methodAttribute);
  174. ParameterInfo[] parameters = methodInfo.GetParameters();
  175. DynamicMethod dynamicMethod = new DynamicMethod(instanceType.Name + "_" + methodName, typeof(object), new Type[] { typeof(object), typeof(object[]) }, true);
  176. ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
  177. ilGenerator.Emit(OpCodes.Ldarg_0);
  178. ilGenerator.Emit(OpCodes.Castclass, instanceType);
  179. for (int paramIndex = 0; paramIndex < parameters.Length; paramIndex++)
  180. {
  181. ilGenerator.Emit(OpCodes.Ldarg_1);
  182. ilGenerator.Emit(OpCodes.Ldc_I4, paramIndex);
  183. ilGenerator.Emit(OpCodes.Ldelem_Ref);
  184. if (parameters[paramIndex].ParameterType.IsValueType)
  185. {
  186. ilGenerator.Emit(OpCodes.Unbox_Any, parameters[paramIndex].ParameterType);
  187. }
  188. else
  189. {
  190. ilGenerator.Emit(OpCodes.Castclass, parameters[paramIndex].ParameterType);
  191. }
  192. }
  193. if (methodInfo.IsVirtual)
  194. {
  195. ilGenerator.Emit(OpCodes.Callvirt, methodInfo);
  196. }
  197. else
  198. {
  199. ilGenerator.Emit(OpCodes.Call, methodInfo);
  200. }
  201. if (methodInfo.ReturnType.Name == "Void")
  202. {
  203. ilGenerator.Emit(OpCodes.Ldnull);
  204. }
  205. else if (methodInfo.ReturnType.IsValueType)
  206. {
  207. ilGenerator.Emit(OpCodes.Box, methodInfo.ReturnType);
  208. }
  209. ilGenerator.Emit(OpCodes.Ret);
  210. MethodInvoke method = (MethodInvoke)dynamicMethod.CreateDelegate(typeof(MethodInvoke));
  211. if (method == null)
  212. {
  213. InternalMethodCollection[instanceType, methodName] = cacheEntry;
  214. return null;
  215. }
  216. cacheEntry.Info = methodInfo;
  217. cacheEntry.Delegate = method;
  218. InternalMethodCollection[instanceType, methodName] = cacheEntry;
  219. return method;
  220. }
  221. }
  222. public static void CacheMethodsOfType(Type instanceType)
  223. {
  224. // The following statement doesn't always work. The hierarchy tree needs to be scanned as private methods cannot be picked up within base classes by GetMethods.
  225. MethodInfo[] methods = instanceType.GetMethods(BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
  226. foreach (MethodInfo method in methods)
  227. {
  228. AjaxControlMethodAttribute methodAttribute = (AjaxControlMethodAttribute)method.GetCustomAttributes(typeof(AjaxControlMethodAttribute), true).FirstOrDefault();
  229. if (methodAttribute != null)
  230. {
  231. CacheMethod(instanceType, methodAttribute, method);
  232. }
  233. }
  234. }
  235. public static Dictionary<string, MethodCacheEntry> GetMethodsOfType(Type instanceType)
  236. {
  237. Dictionary<string, MethodCacheEntry> methodEntries = InternalMethodCollection[instanceType];
  238. if (methodEntries == null)
  239. {
  240. CacheMethodsOfType(instanceType);
  241. methodEntries = InternalMethodCollection[instanceType];
  242. }
  243. if (methodEntries == null) // If the collection is STILL null...
  244. {
  245. methodEntries = new Dictionary<string, MethodCacheEntry>(); // ...initialize the collection.
  246. InternalMethodCollection[instanceType] = methodEntries;
  247. }
  248. return methodEntries;
  249. }
  250. //------\\ Methods //-----------------------------------------------//
  251. }
  252. }