/// <summary> /// Defines the IL that caches the result of calling the base method. /// </summary> /// <param name="il">The il generator to use.</param> /// <param name="returnValueLocal">The local that the result of reading from the cache will be stored into.</param> /// <param name="cacheKeyLocal">The local variable that contains a reference to the calculated cache key.</param> /// <param name="cacheServiceField">The field that contains a reference to the cache service.</param> /// <param name="cacheParams">The cacheable method attribute data that describes the cache behavior for the method.</param> private static void CacheResult(ILGenerator il, LocalBuilder returnValueLocal, LocalBuilder cacheKeyLocal, FieldBuilder cacheServiceField, CacheableMethodAttribute cacheParams) { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldfld, cacheServiceField); il.Emit(OpCodes.Ldloc, cacheKeyLocal); il.Emit(OpCodes.Ldloc, returnValueLocal); il.Emit(OpCodes.Ldc_I4, cacheParams.CacheSeconds); var methodInfo = typeof(IDynaCacheService) .GetMethod("SetCachedObject") .MakeGenericMethod(returnValueLocal.LocalType); il.EmitCall(OpCodes.Callvirt, methodInfo, null); }
/// <summary> /// Defines a method in the dynamic type that wraps the caching behavior around the underlying type's method call. /// </summary> /// <param name="cacheableModule">The cacheable module.</param> /// <param name="cacheServiceField">The cache service field.</param> /// <param name="methodInfo">The method info.</param> /// <param name="cacheParams">The cacheable method attribute data that describes the cache behavior for the method.</param> private static void DefineMethod(TypeBuilder cacheableModule, FieldBuilder cacheServiceField, MethodInfo methodInfo, CacheableMethodAttribute cacheParams) { if (methodInfo.IsFinal || !methodInfo.IsVirtual) { throw new DynaCacheException( string.Format("Cacheable methods must be overridable. Correct method <{0}> in type <{1}>.", methodInfo.Name, methodInfo.DeclaringType.Name) ); } var methodParams = methodInfo.GetParameters().ToArray(); if (methodParams.Any(p => p.ParameterType.IsByRef)) { throw new DynaCacheException( string.Format("Reference parameters (out/ref) are not supported for cacheable methods. Correct method <{0}> in type <{1}>.", methodInfo.Name, methodInfo.DeclaringType.Name) ); } foreach (var methodParam in methodParams) { var paramType = methodParam.ParameterType; if (!(paramType.IsEnum || paramType.ContainsGenericParameters || ToStringableTypes.Contains(paramType) || (paramType.IsGenericType && paramType.GetGenericTypeDefinition() == typeof(Nullable <>) && ToStringableTypes.Contains(paramType.GetGenericArguments()[0])))) { if (paramType.GetCustomAttributes(typeof(ToStringableAttribute), false).Any()) { ToStringableTypes.Add(paramType); } else if (!CustomConverters.ContainsKey(paramType)) { throw new DynaCacheException( String.Format( "Cacheable method has parameter without unique ToString() implementation: consider writing it and mark parameter type with ToStringable attribute." + "Method: {0}, Parameter {1} of type {2}", methodInfo.Name, methodParam.Name, paramType)); } } } var method = cacheableModule.DefineMethod( methodInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.ReuseSlot, methodInfo.ReturnType, methodParams.Select(pa => pa.ParameterType).ToArray()); var il = method.GetILGenerator(); var cacheKeyLocal = il.DeclareLocal(typeof(string)); var returnValueLocal = il.DeclareLocal(method.ReturnType); var cacheOutValueLocal = il.DeclareLocal(typeof(object)); var cacheKeyTemplate = CreateCacheKeyTemplate(methodInfo, methodParams); FormatCacheKey(methodParams, il, cacheKeyLocal, cacheKeyTemplate); TryGetFromCache(cacheOutValueLocal, cacheKeyLocal, returnValueLocal, il, cacheServiceField); CallBaseMethod(methodInfo, methodParams, il, returnValueLocal); CacheResult(il, returnValueLocal, cacheKeyLocal, cacheServiceField, cacheParams); il.Emit(OpCodes.Ldloc, returnValueLocal); il.Emit(OpCodes.Ret); }