From fcfe2ed9b2903dd7f0b60fafb700847cb8f7d8a4 Mon Sep 17 00:00:00 2001 From: bbaia Date: Sat, 12 Dec 2009 22:41:20 +0000 Subject: [PATCH] Use 'invocation.Method' as variable for cache attributes key expression resolution [SPRNET-959] --- .../Aspects/Cache/CacheParameterAdvice.cs | 47 ++++++++------- .../Aspects/Cache/CacheResultAdvice.cs | 35 ++++++----- .../Cache/CacheParameterAdviceTests.cs | 24 ++++++++ .../Aspects/Cache/CacheResultAdviceTests.cs | 58 +++++++++++++++++++ 4 files changed, 130 insertions(+), 34 deletions(-) diff --git a/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvice.cs index 713b345c..19f697d0 100644 --- a/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/Cache/CacheParameterAdvice.cs @@ -121,26 +121,33 @@ namespace Spring.Aspects.Cache CacheParameterInfo cpi = GetCacheParameterInfo(method); CacheParameterAttribute[][] cacheParameterAttributes = cpi.CacheParameterAttributes; - for (int i = 0; i < cacheParameterAttributes.Length; i++) - { - foreach (CacheParameterAttribute paramInfo in cacheParameterAttributes[i]) - { - if (EvalCondition(paramInfo.Condition, paramInfo.ConditionExpression, arguments[i], null)) - { - ICache cache = GetCache(paramInfo.CacheName); - - object key = paramInfo.KeyExpression.GetValue(arguments[i]); - - #region Instrumentation - if (isLogDebugEnabled) - { - logger.Debug(string.Format("Caching parameter for key [{0}] into cache [{1}].", key, paramInfo.CacheName)); - } - #endregion - - cache.Insert(key, arguments[i], paramInfo.TimeToLiveTimeSpan); - } - } + if (cacheParameterAttributes.Length > 0) + { + IDictionary vars = PrepareVariables(method, arguments); + for (int i = 0; i < cacheParameterAttributes.Length; i++) + { + foreach (CacheParameterAttribute paramInfo in cacheParameterAttributes[i]) + { + AssertUtils.ArgumentNotNull(paramInfo.KeyExpression, "Key", + "The cache attribute is missing the key definition."); + + if (EvalCondition(paramInfo.Condition, paramInfo.ConditionExpression, arguments[i], vars)) + { + ICache cache = GetCache(paramInfo.CacheName); + + object key = paramInfo.KeyExpression.GetValue(arguments[i], vars); + + #region Instrumentation + if (isLogDebugEnabled) + { + logger.Debug(string.Format("Caching parameter for key [{0}] into cache [{1}].", key, paramInfo.CacheName)); + } + #endregion + + cache.Insert(key, arguments[i], paramInfo.TimeToLiveTimeSpan); + } + } + } } } } diff --git a/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs b/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs index f7cb963f..a6883095 100644 --- a/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs +++ b/src/Spring/Spring.Aop/Aspects/Cache/CacheResultAdvice.cs @@ -118,12 +118,15 @@ namespace Spring.Aspects.Cache { CacheResultInfo cacheResultInfo = GetCacheResultInfo(invocation.Method); + // prepare variables for SpEL expressions + IDictionary vars = PrepareVariables(invocation.Method, invocation.Arguments); + bool cacheHit = false; - object returnValue = GetReturnValue(invocation, cacheResultInfo.ResultInfo, out cacheHit); + object returnValue = GetReturnValue(invocation, cacheResultInfo.ResultInfo, vars, out cacheHit); if (!cacheHit && cacheResultInfo.ItemInfoArray.Length > 0 && returnValue is IEnumerable) { - CacheResultItems((IEnumerable)returnValue, cacheResultInfo.ItemInfoArray); + CacheResultItems((IEnumerable)returnValue, cacheResultInfo.ItemInfoArray, vars); } return returnValue; @@ -141,13 +144,16 @@ namespace Spring.Aspects.Cache /// 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). /// + /// + /// Variables for expression evaluation. + /// /// /// 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) + private object GetReturnValue(IMethodInvocation invocation, CacheResultAttribute resultInfo, IDictionary vars, out bool cacheHit) { if (resultInfo != null) { @@ -155,15 +161,13 @@ namespace Spring.Aspects.Cache bool isLogDebugEnabled = logger.IsDebugEnabled; #endregion + AssertUtils.ArgumentNotNull(resultInfo.KeyExpression, "Key", + "The cache attribute is missing the key definition."); + object returnValue = null; - - 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); - returnValue = cache.Get(resultKey); cacheHit = (returnValue != null); if (!cacheHit) @@ -213,12 +217,15 @@ namespace Spring.Aspects.Cache /// /// Attributes specifying where and how to cache each item from the collection. /// - private void CacheResultItems(IEnumerable items, CacheResultItemsAttribute[] itemInfoArray) + /// + /// Variables for expression evaluation. + /// + private void CacheResultItems(IEnumerable items, CacheResultItemsAttribute[] itemInfoArray, IDictionary vars) { foreach (CacheResultItemsAttribute itemInfo in itemInfoArray) { - AssertUtils.ArgumentNotNull(itemInfo.KeyExpression, "KeyExpression", - "The cache attribute is missing the key definition."); + AssertUtils.ArgumentNotNull(itemInfo.KeyExpression, "Key", + "The cache attribute is missing the key definition."); ICache cache = GetCache(itemInfo.CacheName); @@ -227,9 +234,9 @@ namespace Spring.Aspects.Cache #endregion foreach (object item in items) { - if (EvalCondition(itemInfo.Condition, itemInfo.ConditionExpression, item, null)) + if (EvalCondition(itemInfo.Condition, itemInfo.ConditionExpression, item, vars)) { - object itemKey = itemInfo.KeyExpression.GetValue(item); + object itemKey = itemInfo.KeyExpression.GetValue(item, vars); #region Instrumentation if (isDebugEnabled) { diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheParameterAdviceTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheParameterAdviceTests.cs index c53a6f98..8cf57214 100644 --- a/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheParameterAdviceTests.cs +++ b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheParameterAdviceTests.cs @@ -72,6 +72,23 @@ namespace Spring.Aspects.Cache mocks.VerifyAll(); } + [Test] + public void TestSimpleWithMethodInfoParameterCaching() + { + MethodInfo method = typeof(SimpleWithMethodInfoCacheParameterTarget).GetMethod("Save"); + object[] args = new object[] { new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), "Serbian") }; + + ExpectCacheInstanceRetrieval("cache", cache); + mocks.ReplayAll(); + + // parameter value should be added to cache + advice.AfterReturning(null, method, args, null); + Assert.AreEqual(1, cache.Count); + Assert.AreEqual(args[0], cache.Get("Save-Nikola Tesla")); + + mocks.VerifyAll(); + } + [Test] public void TestMultipleParameterCaching() { @@ -126,6 +143,13 @@ namespace Spring.Aspects.Cache } } + public sealed class SimpleWithMethodInfoCacheParameterTarget : ICacheParameterTarget + { + public void Save([CacheParameter("cache", "#Save.Name + '-' + Name")] Inventor inventor) + { + } + } + public sealed class MultipleCacheParameterTarget : ICacheParameterTarget { public void Save([CacheParameter("cache", "Name")][CacheParameter("cache", "Nationality")] Inventor inventor) diff --git a/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs index 47da50eb..c0277455 100644 --- a/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs +++ b/test/Spring/Spring.Aop.Tests/Aspects/Cache/CacheResultAdviceTests.cs @@ -195,6 +195,7 @@ namespace Spring.Aspects.Cache object expectedReturnValue = new object[] { "one", "two", "three" }; ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, 5, expectedReturnValue); ExpectCallToProceed(new object[] { "one", "two", "three" }); ExpectCacheInstanceRetrieval("items", itemCache); @@ -228,6 +229,7 @@ namespace Spring.Aspects.Cache object expectedReturnValue = new object[] { "one", "two", "three" }; ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, 5, expectedReturnValue); ExpectCallToProceed(new object[] { "one", "two", "three" }); ExpectCacheInstanceRetrieval( "items", itemCache ); ExpectCacheInstanceRetrieval( "items", itemCache ); @@ -264,6 +266,7 @@ namespace Spring.Aspects.Cache object expectedReturnValue = new object[] { "one", "two", "three" }; ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, 5, expectedReturnValue); ExpectCallToProceed(new object[] { "one", "two", "three" }); ExpectCacheInstanceRetrieval( "items", itemCache ); @@ -370,6 +373,49 @@ namespace Spring.Aspects.Cache mockContext.Verify(); } + [Test] + public void CacheResultWithMethodInfo() + { + MethodInfo method = new EnumerableResultMethod(cacheResultTarget.CacheResultWithMethodInfo).Method; + object expectedReturnValue = new object[] { "one", "two", "three" }; + + ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, 5, expectedReturnValue); + ExpectCacheInstanceRetrieval("results", resultCache); + ExpectCallToProceed(new object[] { "one", "two", "three" }); + + // return value should be added to cache + object returnValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, returnValue); + Assert.AreEqual(1, resultCache.Count); + Assert.AreEqual(returnValue, resultCache.Get("CacheResultWithMethodInfo-5")); + + mockInvocation.Verify(); + mockContext.Verify(); + } + + [Test] + public void CacheResultItemsWithMethodInfo() + { + MethodInfo method = new EnumerableResultMethod(cacheResultTarget.CacheResultItemsWithMethodInfo).Method; + object expectedReturnValue = new object[] { "one", "two", "three" }; + + ExpectAttributeRetrieval(method); + ExpectCacheKeyGeneration(method, 5, expectedReturnValue); + ExpectCallToProceed(new object[] { "one", "two", "three" }); + ExpectCacheInstanceRetrieval("items", itemCache); + + // return value should be added to result cache and each item to item cache + object returnValue = advice.Invoke((IMethodInvocation)mockInvocation.Object); + Assert.AreEqual(expectedReturnValue, returnValue); + Assert.AreEqual(0, resultCache.Count); + Assert.AreEqual(3, itemCache.Count); + Assert.AreEqual("two", itemCache.Get("CacheResultItemsWithMethodInfo-two")); + + mockInvocation.Verify(); + mockContext.Verify(); + } + #region Helper methods @@ -499,6 +545,18 @@ namespace Spring.Aspects.Cache return elements; } + [CacheResult("results", "#CacheResultWithMethodInfo.Name + '-' + #key")] + public IEnumerable CacheResultWithMethodInfo(int key, params object[] elements) + { + return elements; + } + + [CacheResultItems("items", "#CacheResultItemsWithMethodInfo.Name + '-' + #this")] + public IEnumerable CacheResultItemsWithMethodInfo(int key, params object[] elements) + { + return elements; + } + [CacheResultItems( "items", "#this", Condition = "#this.StartsWith('t')" )] public IEnumerable CacheResultItemsWithCondition( int key, params object[] elements ) {