From 0e716ecfa0f8f925f254f0046a9c16533fef9b3b Mon Sep 17 00:00:00 2001 From: eeichinger Date: Wed, 14 Oct 2009 18:09:22 +0000 Subject: [PATCH] fixed SPRNET-1259 --- .../Aspects/Cache/BaseCacheAdvice.cs | 45 +- .../Aspects/Cache/CacheParameterAdvice.cs | 64 ++- .../Aspects/Cache/CacheResultAdvice.cs | 433 +++++++++--------- .../Aspects/Cache/InvalidateCacheAdvice.cs | 17 +- .../Spring.Core/Caching/BaseCacheAttribute.cs | 3 +- ...tractFallbackTransactionAttributeSource.cs | 9 +- .../Aspects/Cache/CacheResultAdviceTests.cs | 7 +- 7 files changed, 335 insertions(+), 243 deletions(-) diff --git a/src/Spring/Spring.Aop/Aspects/Cache/BaseCacheAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/BaseCacheAdvice.cs index 05deee99..6985b83d 100644 --- a/src/Spring/Spring.Aop/Aspects/Cache/BaseCacheAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/Cache/BaseCacheAdvice.cs @@ -22,7 +22,7 @@ using System; using System.Reflection; - +using Common.Logging; using Spring.Caching; using Spring.Context; using Spring.Expressions; @@ -37,10 +37,23 @@ namespace Spring.Aspects.Cache /// access to common functionality, such as obtaining a cache instance. /// /// Aleksandar Seovic - public class BaseCacheAdvice : IApplicationContextAware + public abstract class BaseCacheAdvice : IApplicationContextAware { + /// + /// Shared logger instance + /// + protected readonly ILog logger; + private IApplicationContext applicationContext; - + + /// + /// Create a new default instance. + /// + protected BaseCacheAdvice() + { + logger = LogManager.GetLogger(this.GetType()); + } + /// /// Sets the that this /// object runs in. @@ -138,8 +151,8 @@ namespace Spring.Aspects.Cache /// /// Retrieves custom attribute for the specified attribute type. /// - /// - /// Method to get attribute from. + /// + /// Method/Parameter to get attribute from. /// /// /// Attribute type. @@ -147,14 +160,32 @@ namespace Spring.Aspects.Cache /// /// Attribute instance if one is found, null otherwise. /// - protected static object GetCustomAttribute(MethodInfo method, Type attributeType) + protected object GetCustomAttribute(ICustomAttributeProvider attributeProvider, Type attributeType) { - object[] attributes = method.GetCustomAttributes(attributeType, false); + object[] attributes = attributeProvider.GetCustomAttributes(attributeType, false); if (attributes.Length > 0) { return attributes[0]; } return null; } + + /// + /// Retrieves custom attribute for the specified attribute type. + /// + /// + /// Method/Parameter to get attribute from. + /// + /// + /// Attribute type. + /// + /// + /// Attribute instance if one is found, null otherwise. + /// + protected object[] GetCustomAttributes(ICustomAttributeProvider attributeProvider, Type attributeType) + { + object[] attributes = attributeProvider.GetCustomAttributes(attributeType, false); + return attributes; + } } } \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvice.cs index caa3d5fa..10a46b8b 100644 --- a/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvice.cs @@ -20,6 +20,8 @@ #region Imports +using System; +using System.Collections; using System.Reflection; using Common.Logging; using Spring.Aop; @@ -52,8 +54,19 @@ namespace Spring.Aspects.Cache /// Aleksandar Seovic public class CacheParameterAdvice : BaseCacheAdvice, IAfterReturningAdvice { - // shared logger instance - private static readonly ILog logger = LogManager.GetLogger(typeof(CacheParameterAdvice)); + private class CacheParameterInfo + { + public readonly ParameterInfo[] Parameters; + public readonly CacheParameterAttribute[][] CacheParameterAttributes; + + public CacheParameterInfo(ParameterInfo[] parameters, CacheParameterAttribute[][] cacheParameterAttributes) + { + Parameters = parameters; + CacheParameterAttributes = cacheParameterAttributes; + } + } + + private readonly Hashtable _cacheParameterInfoCache = new Hashtable(); /// /// Executes after target @@ -78,35 +91,54 @@ namespace Spring.Aspects.Cache /// public void AfterReturning(object returnValue, MethodInfo method, object[] arguments, object target) { - ParameterInfo[] parameters = method.GetParameters(); - for (int i = 0; i < parameters.Length; i++) + bool isLogDebugEnabled = logger.IsDebugEnabled; + + CacheParameterInfo cpi = GetCacheParameterInfo(method); + CacheParameterAttribute[][] cacheParameterAttributes = cpi.CacheParameterAttributes; + + for (int i = 0; i < cacheParameterAttributes.Length; i++) { - ParameterInfo p = parameters[i]; - CacheParameterAttribute[] paramInfoArray = - (CacheParameterAttribute[])p.GetCustomAttributes(typeof(CacheParameterAttribute), false); - - bool isLogDebugEnabled = logger.IsDebugEnabled; - foreach (CacheParameterAttribute paramInfo in paramInfoArray) + foreach (CacheParameterAttribute paramInfo in cacheParameterAttributes[i]) { if (EvalCondition(paramInfo.Condition, paramInfo.ConditionExpression, arguments[i], null)) { ICache cache = GetCache(paramInfo.CacheName); - AssertUtils.ArgumentNotNull(cache, "CacheName", - "Parameter cache with the specified name [" + paramInfo.CacheName + - "] does not exist."); + if (cache == null) + { + throw new ArgumentNullException("CacheName", string.Format("Parameter cache with the specified name [{0}] does not exist.", paramInfo.CacheName)); + } object key = paramInfo.KeyExpression.GetValue(arguments[i]); - #region Instrumentation + #region Instrumentation if (isLogDebugEnabled) { - logger.Debug("Caching parameter for key [" + key + "]."); + logger.Debug(string.Format("Caching parameter for key [{0}].", key)); } - #endregion + cache.Insert(key, arguments[i], paramInfo.TimeToLiveTimeSpan); } } } } + + private CacheParameterInfo GetCacheParameterInfo(MethodInfo method) + { + CacheParameterInfo cpi = (CacheParameterInfo) _cacheParameterInfoCache[method]; + if (cpi == null) + { + ParameterInfo[] parameters = method.GetParameters(); + CacheParameterAttribute[][] parameterInfos = new CacheParameterAttribute[parameters.Length][]; + for (int i = 0; i < parameters.Length; i++) + { + ParameterInfo p = parameters[i]; + CacheParameterAttribute[] paramInfoArray = (CacheParameterAttribute[])GetCustomAttributes(p, typeof(CacheParameterAttribute)); + parameterInfos[i] = paramInfoArray; + } + cpi = new CacheParameterInfo(parameters, parameterInfos); + _cacheParameterInfoCache[method] = cpi; + } + return cpi; + } } } \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs index ca5f087b..2e3aca93 100644 --- a/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs @@ -1,5 +1,5 @@ -#region License - +#region License + /* * Copyright © 2002-2006 the original author or authors. * @@ -14,213 +14,236 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ - -#endregion - -#region Imports - -using System.Collections; -using System.Reflection; -using AopAlliance.Intercept; -using Common.Logging; -using Spring.Caching; -using Spring.Expressions; -using Spring.Util; - -#endregion - -namespace Spring.Aspects.Cache -{ - /// - /// Implementation of a result caching advice. - /// - /// - ///

- /// This advice can be used to cache the return value of the method. - ///

- ///

- /// Parameters that determine where, how and for how long the return value - /// will be cached are retrieved from the and/or - /// that are defined on the pointcut. - ///

- ///
- /// - /// - /// Aleksandar Seovic - public class CacheResultAdvice : BaseCacheAdvice, IMethodInterceptor - { - // shared logger instance - private static readonly ILog logger = LogManager.GetLogger(typeof (CacheResultAdvice)); - - // NullValue - private static readonly object NullValue = new object(); - - /// - /// Applies caching around a method invocation. - /// - /// - ///

- /// This method tries to retrieve an object from the cache, using the supplied - /// to generate a cache key. If an object is found - /// in the cache, the cached value is returned and the method call does not - /// proceed any further down the invocation chain. - ///

- ///

- /// If object does not exist in the cache, the advised method is called (using - /// ) - /// and any return value is cached for the next method invocation. - ///

- ///
- /// - /// The method invocation that is being intercepted. - /// - /// - /// A cached object or the result of the - /// call. - /// - /// - /// If any of the interceptors in the chain or the target object itself - /// throws an exception. - /// - public object Invoke(IMethodInvocation invocation) - { - CacheResultAttribute resultInfo = - (CacheResultAttribute) GetCustomAttribute(invocation.Method, typeof (CacheResultAttribute)); - CacheResultItemsAttribute[] itemInfoArray = - (CacheResultItemsAttribute[])invocation.Method.GetCustomAttributes(typeof(CacheResultItemsAttribute), false); - - bool cacheHit = false; - object returnValue = GetReturnValue(invocation, resultInfo, out cacheHit); - - if (!cacheHit && itemInfoArray.Length > 0 && returnValue is IEnumerable) - { - CacheResultItems((IEnumerable)returnValue, itemInfoArray); - } - - return returnValue; - } - - /// - /// Obtains return value either from cache or by invoking target method - /// and caches it if necessary. - /// - /// - /// The method invocation that is being intercepted. - /// - /// - /// Attribute specifying where and how to cache return value. Can be null, - /// in which case no caching of the result as a whole will be performed - /// (if the result is collection, individual items could still be cached separately). - /// - /// - /// Returns true if the return value was found in cache, false otherwise. - /// - /// - /// Return value for the specified . - /// - private object GetReturnValue(IMethodInvocation invocation, CacheResultAttribute resultInfo, out bool cacheHit) - { - if (resultInfo != null) - { - object returnValue = null; - bool isLogDebugEnabled = logger.IsDebugEnabled; - + */ + +#endregion + +#region Imports + +using System.Collections; +using System.Reflection; +using AopAlliance.Intercept; +using Common.Logging; +using Spring.Caching; +using Spring.Expressions; +using Spring.Util; + +#endregion + +namespace Spring.Aspects.Cache +{ + /// + /// Implementation of a result caching advice. + /// + /// + ///

+ /// This advice can be used to cache the return value of the method. + ///

+ ///

+ /// Parameters that determine where, how and for how long the return value + /// will be cached are retrieved from the and/or + /// that are defined on the pointcut. + ///

+ ///
+ /// + /// + /// Aleksandar Seovic + public class CacheResultAdvice : BaseCacheAdvice, IMethodInterceptor + { + // NullValue + private static readonly object NullValue = new object(); + + private class CacheResultInfo + { + public readonly CacheResultAttribute ResultInfo; + public readonly CacheResultItemsAttribute[] ItemInfoArray; + + public CacheResultInfo(CacheResultAttribute resultInfo, CacheResultItemsAttribute[] itemInfoArray) + { + ResultInfo = resultInfo; + ItemInfoArray = itemInfoArray; + } + } + + private readonly Hashtable _cacheResultAttributeCache = new Hashtable(); + + /// + /// Applies caching around a method invocation. + /// + /// + ///

+ /// This method tries to retrieve an object from the cache, using the supplied + /// to generate a cache key. If an object is found + /// in the cache, the cached value is returned and the method call does not + /// proceed any further down the invocation chain. + ///

+ ///

+ /// If object does not exist in the cache, the advised method is called (using + /// ) + /// and any return value is cached for the next method invocation. + ///

+ ///
+ /// + /// The method invocation that is being intercepted. + /// + /// + /// A cached object or the result of the + /// call. + /// + /// + /// If any of the interceptors in the chain or the target object itself + /// throws an exception. + /// + public object Invoke(IMethodInvocation invocation) + { + CacheResultInfo cacheResultInfo = GetCacheResultInfo(invocation.Method); + + bool cacheHit = false; + object returnValue = GetReturnValue(invocation, cacheResultInfo.ResultInfo, out cacheHit); + + if (!cacheHit && cacheResultInfo.ItemInfoArray.Length > 0 && returnValue is IEnumerable) + { + CacheResultItems((IEnumerable)returnValue, cacheResultInfo.ItemInfoArray); + } + + return returnValue; + } + + /// + /// Obtains return value either from cache or by invoking target method + /// and caches it if necessary. + /// + /// + /// The method invocation that is being intercepted. + /// + /// + /// Attribute specifying where and how to cache return value. Can be null, + /// in which case no caching of the result as a whole will be performed + /// (if the result is collection, individual items could still be cached separately). + /// + /// + /// Returns true if the return value was found in cache, false otherwise. + /// + /// + /// Return value for the specified . + /// + private object GetReturnValue(IMethodInvocation invocation, CacheResultAttribute resultInfo, out bool cacheHit) + { + if (resultInfo != null) + { + object returnValue = null; + bool isLogDebugEnabled = logger.IsDebugEnabled; + IDictionary vars = PrepareVariables(invocation.Method, invocation.Arguments); AssertUtils.ArgumentNotNull(resultInfo.KeyExpression, "KeyExpression", - "The cache attribute is missing the key definition."); - object resultKey = resultInfo.KeyExpression.GetValue(null, vars); - ICache cache = GetCache(resultInfo.CacheName); - AssertUtils.ArgumentNotNull(cache, "CacheName", - "Result cache with the specified name [" + resultInfo.CacheName + - "] does not exist."); - - returnValue = cache.Get(resultKey); - cacheHit = (returnValue != null); - if (!cacheHit) - { - #region Instrumentation - - if (isLogDebugEnabled) - { - logger.Debug("Object for key [" + resultKey + "] was not found in cache. Proceeding..."); - } - - #endregion - - returnValue = invocation.Proceed(); - if (EvalCondition(resultInfo.Condition, resultInfo.ConditionExpression, returnValue, vars)) - { - #region Instrumentation - - if (isLogDebugEnabled) - { - logger.Debug("Caching object for key [" + resultKey + "]."); - } - - #endregion - cache.Insert(resultKey, (returnValue==null)?NullValue:returnValue, resultInfo.TimeToLiveTimeSpan); - } - } - else - { - #region Instrumentation - - if (isLogDebugEnabled) - { - logger.Debug("Cache hit for [" + resultKey + "]. Aborting invocation..."); - } - - #endregion - } - - return (returnValue==NullValue)?null:returnValue; - } - - cacheHit = false; - return invocation.Proceed(); - } - - /// - /// Caches each item from the collection returned by target method. - /// - /// - /// A collection of items to cache. - /// - /// - /// Attributes specifying where and how to cache each item from the collection. - /// - private void CacheResultItems(IEnumerable items, CacheResultItemsAttribute[] itemInfoArray) - { - foreach (CacheResultItemsAttribute itemInfo in itemInfoArray) - { - ICache cache = GetCache(itemInfo.CacheName); - AssertUtils.ArgumentNotNull(cache, "CacheName", - "Result item cache with the specified name [" + itemInfo.CacheName + + "The cache attribute is missing the key definition."); + object resultKey = resultInfo.KeyExpression.GetValue(null, vars); + ICache cache = GetCache(resultInfo.CacheName); + AssertUtils.ArgumentNotNull(cache, "CacheName", + "Result cache with the specified name [" + resultInfo.CacheName + + "] does not exist."); + + returnValue = cache.Get(resultKey); + cacheHit = (returnValue != null); + if (!cacheHit) + { + #region Instrumentation + + if (isLogDebugEnabled) + { + logger.Debug("Object for key [" + resultKey + "] was not found in cache. Proceeding..."); + } + + #endregion + + returnValue = invocation.Proceed(); + if (EvalCondition(resultInfo.Condition, resultInfo.ConditionExpression, returnValue, vars)) + { + #region Instrumentation + + if (isLogDebugEnabled) + { + logger.Debug("Caching object for key [" + resultKey + "]."); + } + + #endregion + cache.Insert(resultKey, (returnValue == null) ? NullValue : returnValue, resultInfo.TimeToLiveTimeSpan); + } + } + else + { + #region Instrumentation + + if (isLogDebugEnabled) + { + logger.Debug("Cache hit for [" + resultKey + "]. Aborting invocation..."); + } + + #endregion + } + + return (returnValue == NullValue) ? null : returnValue; + } + + cacheHit = false; + return invocation.Proceed(); + } + + /// + /// Caches each item from the collection returned by target method. + /// + /// + /// A collection of items to cache. + /// + /// + /// Attributes specifying where and how to cache each item from the collection. + /// + private void CacheResultItems(IEnumerable items, CacheResultItemsAttribute[] itemInfoArray) + { + foreach (CacheResultItemsAttribute itemInfo in itemInfoArray) + { + ICache cache = GetCache(itemInfo.CacheName); + AssertUtils.ArgumentNotNull(cache, "CacheName", + "Result item cache with the specified name [" + itemInfo.CacheName + "] does not exist."); AssertUtils.ArgumentNotNull(itemInfo.KeyExpression, "KeyExpression", - "The cache attribute is missing the key definition."); - - bool isDebugEnabled = logger.IsDebugEnabled; - foreach (object item in items) - { - if (EvalCondition(itemInfo.Condition, itemInfo.ConditionExpression, item, null)) - { - object itemKey = itemInfo.KeyExpression.GetValue(item); - #region Instrumentation - - if (isDebugEnabled) - { - logger.Debug("Caching collection item for key [" + itemKey + "]."); - } - - #endregion - cache.Insert(itemKey, (item==null?NullValue:item), itemInfo.TimeToLiveTimeSpan); - } - } - } - } - } + "The cache attribute is missing the key definition."); + + bool isDebugEnabled = logger.IsDebugEnabled; + foreach (object item in items) + { + if (EvalCondition(itemInfo.Condition, itemInfo.ConditionExpression, item, null)) + { + object itemKey = itemInfo.KeyExpression.GetValue(item); + #region Instrumentation + + if (isDebugEnabled) + { + logger.Debug("Caching collection item for key [" + itemKey + "]."); + } + + #endregion + cache.Insert(itemKey, (item == null ? NullValue : item), itemInfo.TimeToLiveTimeSpan); + } + } + } + } + + private CacheResultInfo GetCacheResultInfo(MethodInfo method) + { + CacheResultInfo cacheResultInfo = (CacheResultInfo)_cacheResultAttributeCache[method]; + // no need for locking here - last one wins + if (cacheResultInfo == null) + { + CacheResultAttribute resultInfo = (CacheResultAttribute)GetCustomAttribute(method, typeof(CacheResultAttribute)); + CacheResultItemsAttribute[] itemInfoArray = (CacheResultItemsAttribute[])GetCustomAttributes(method, typeof(CacheResultItemsAttribute)); + + cacheResultInfo = new CacheResultInfo(resultInfo, itemInfoArray); + _cacheResultAttributeCache[method] = cacheResultInfo; + } + return cacheResultInfo; + } + } } \ No newline at end of file diff --git a/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvice.cs index 37be5eda..f6c708ea 100644 --- a/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/Cache/InvalidateCacheAdvice.cs @@ -55,8 +55,7 @@ namespace Spring.Aspects.Cache /// Aleksandar Seovic public class InvalidateCacheAdvice : BaseCacheAdvice, IAfterReturningAdvice { - // shared logger instance - //private static readonly ILog logger = LogManager.GetLogger(typeof(InvalidateCacheAdvice)); + private readonly Hashtable _invalidateCacheAttributeCache = new Hashtable(); /// /// Executes after @@ -81,8 +80,7 @@ namespace Spring.Aspects.Cache /// public void AfterReturning(object returnValue, MethodInfo method, object[] arguments, object target) { - InvalidateCacheAttribute[] cacheInfoArray = - (InvalidateCacheAttribute[]) method.GetCustomAttributes(typeof(InvalidateCacheAttribute), false); + InvalidateCacheAttribute[] cacheInfoArray = GetInvalidateCacheInfo(method); if (cacheInfoArray.Length > 0) { @@ -115,6 +113,17 @@ namespace Spring.Aspects.Cache } } } + } + + private InvalidateCacheAttribute[] GetInvalidateCacheInfo(MethodInfo method) + { + InvalidateCacheAttribute[] cacheInfoArray = (InvalidateCacheAttribute[]) _invalidateCacheAttributeCache[method]; + if (cacheInfoArray == null) + { + cacheInfoArray = (InvalidateCacheAttribute[])GetCustomAttributes(method, typeof(InvalidateCacheAttribute)); + _invalidateCacheAttributeCache[method] = cacheInfoArray; + } + return cacheInfoArray; } } } \ No newline at end of file diff --git a/src/Spring/Spring.Core/Caching/BaseCacheAttribute.cs b/src/Spring/Spring.Core/Caching/BaseCacheAttribute.cs index 09e2f76d..62a25e00 100644 --- a/src/Spring/Spring.Core/Caching/BaseCacheAttribute.cs +++ b/src/Spring/Spring.Core/Caching/BaseCacheAttribute.cs @@ -170,6 +170,5 @@ namespace Spring.Caching get { return timeToLiveTimeSpan; } } - #endregion - } + #endregion } } \ No newline at end of file diff --git a/src/Spring/Spring.Data/Transaction/Interceptor/AbstractFallbackTransactionAttributeSource.cs b/src/Spring/Spring.Data/Transaction/Interceptor/AbstractFallbackTransactionAttributeSource.cs index 69cb04d8..6d4e4e47 100644 --- a/src/Spring/Spring.Data/Transaction/Interceptor/AbstractFallbackTransactionAttributeSource.cs +++ b/src/Spring/Spring.Data/Transaction/Interceptor/AbstractFallbackTransactionAttributeSource.cs @@ -77,7 +77,7 @@ namespace Spring.Transaction.Interceptor /// /// Cache of s, keyed by method and target class. /// - private IDictionary _transactionAttibuteCache = new Hashtable(); + private readonly IDictionary _transactionAttibuteCache = new Hashtable(); /// /// Creates a new instance of the @@ -135,11 +135,10 @@ namespace Spring.Transaction.Interceptor public ITransactionAttribute ReturnTransactionAttribute(MethodInfo method, Type targetType) { object cacheKey = getCacheKey(method, targetType); - object cached = null; - lock (_transactionAttibuteCache) + lock (_transactionAttibuteCache) { - cached = _transactionAttibuteCache[cacheKey]; + object cached = _transactionAttibuteCache[cacheKey]; if (cached != null) { @@ -228,7 +227,7 @@ namespace Spring.Transaction.Interceptor private object getCacheKey(MethodBase method, Type targetType) { - return targetType + String.Empty + method.GetHashCode(); + return string.Intern(targetType.AssemblyQualifiedName + "." + method); } private ITransactionAttribute computeTransactionAttribute(MethodInfo method, Type targetType) diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs index 00628c22..47da50eb 100644 --- a/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs +++ b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs @@ -375,14 +375,13 @@ namespace Spring.Aspects.Cache private void ExpectAttributeRetrieval( MethodInfo method ) { - mockInvocation.ExpectAndReturn( "Method", method ); - mockInvocation.ExpectAndReturn( "Method", method ); + mockInvocation.SetValue( "Method", method ); } private void ExpectCacheKeyGeneration( MethodInfo method, params object[] arguments ) { - mockInvocation.ExpectAndReturn( "Method", method ); - mockInvocation.ExpectAndReturn( "Arguments", arguments ); +// mockInvocation.ExpectAndReturn( "Method", method ); + mockInvocation.SetValue( "Arguments", arguments ); } private void ExpectCacheInstanceRetrieval( string cacheName, ICache cache )