View Full Version : Explanation of CacheResultItems
bjornwang
01-28-2008, 12:31 PM
Hello,
Can someone explain the use of the CacheResultItems attribute?
Does it cache the whole result set AND the individual items? How then can I access the individual items?
I'm hoping to be able to apply it to the following scenario:
1. Caching of method IList<Thing> GetListOfThingsByType(type) -- should cache the whole return set.
2. Caching of individual domain objects retrieved by GetThing(id) -- caches the individual item by using the normal [CacheResult] attribute.
3. Shared caching between the two. If GetListOfTingsByType(type) has already retrieved an object which is later looked up by GetThing(id), can [CacheResultItems] and[CacheResult] be configured to share objects?
Bjorn
Bruno Baia
01-28-2008, 01:17 PM
Hi,
here is a good explanation from Aleks with a sample :
1. You can specify which named cache from the application context to store method result in. This allows you to use different caches for different purposes while still applying only a single aspect to your objects. This is not possible with the old implementation, as Cache is property of the advice, so you would have to define one cache advice per target cache and apply them selectively to your objects.
2. If the method returns a collection, you can cache either the collection itself, each of its items separately, or both. You can also use different named caches for the collection and individual items.
3. You can update value in cache based on the method argument. For example, if you pass an object to your DAO's Save method, you can updated cached value automatically as well.
4. You can invalidate one or more cache values (or even clear the whole cache), based on method arguments or return value.
Here is an example class with various caching-related attributes applied:
public sealed class InventorStore : IInventorStore
{
private IDictionary inventors = new ListDictionary();
public InventorStore()
{
Inventor tesla = new Inventor("Nikola Tesla", new DateTime(1856, 7, 9), null);
Inventor pupin = new Inventor("Mihajlo Pupin", new DateTime(1854, 10, 9), null);
inventors.Add("Nikola Tesla", tesla);
inventors.Add("Mihajlo Pupin", pupin);
}
[CacheResultItems("inventors", "Name")]
public IList GetAll()
{
return new ArrayList(inventors.Values);
}
[CacheResult("inventors", "#name")]
public Inventor Load(string name)
{
return (Inventor) inventors[name];
}
public void Save([CacheParameter("inventors", "Name")] Inventor inventor)
{
inventor.Nationality = "Serbian";
}
[InvalidateCache("inventors", Keys = "#inventor.Name")]
public void Delete(Inventor inventor)
{
}
[InvalidateCache("inventors")]
public void DeleteAll()
{
}
}
You can then apply CacheAspect to this class either programmatically:
CacheAspect cacheAspect = new CacheAspect();
cacheAspect.ApplicationContext = appContextContainingICacheInstances;
ProxyFactory pf = new ProxyFactory(new InventorStore());
pf.AddAspect(cacheAspect);
IInventorStore store = (IInventorStore) pf.GetProxy();
or declaratively:
<object id="cacheAspect" type="Spring.Aspects.Cache.CacheAspect, Spring.Aop"/>
<object id="inventorStore" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
<property name="Target">
<object type="DAL.InventorStore, MyDAL"/>
</property>
<property name="InterceptorNames" value="cacheAspect"/>
</object>
<object id="inventors" type="Spring.Caching.AspNetCache, Spring.Web"/>
As you can see, CacheAspect is a container holding all the advices and associated pointcut definitions that are necessary for everything to work. You can apply it to an object by simply adding its object ID to the list of interceptor names within ProxyFactoryObject definition.
As usual, feedback is appreciated.
Regards,
Aleks
About your scenario, you should be able to do it:
[CacheResult("thingsByType", "#type.Name")] // or #type for the cache key
[CacheResultItems("thingById", "Id")] // Will use Thing.Id as a key
public IList<Thing> GetListOfThingsByType(type)
{
...
}
[CacheResult("thingById", "#id")]
public Thing GetThingById(int id)
{
...
}
HTH,
Bruno
bjornwang
02-07-2008, 05:40 PM
Hello,
This works miracles for our application!!! WOW! Great!
To be able to make things even better, I have two follow-up questions. We can use the same code example:
[CacheResult("thingsByType", "#type.Name")] // or #type for the cache key
[CacheResultItems("thingById", "Id")] // Will use Thing.Id as a key
public IList<Thing> GetListOfThingsByType(type)
1. How can I handle parameters that can be NULL? This crashes generation of my cache key. I see this situation where my fellow programmers use NULL to denote some special case. In this example, it might mean "List all things".
2. Can I somehow use the expression language to better control the cache key of the CacheResultItems tag? It seems that it can only ose one specified parameter now? (Meaning I have to put it in a dedicated cache I guess.)
Thanks
Bruno Baia
02-10-2008, 05:14 PM
Hi,
1. How can I handle parameters that can be NULL? This crashes generation of my cache key. I see this situation where my fellow programmers use NULL to denote some special case. In this example, it might mean "List all things".
You can test that parameter first using SpEL :
[CacheResult("thingsByType", "#type == null ? 'AllThings' : #type.Name")]
public IList<Thing> GetListOfThingsByType(type)
And if you don't want to cache the return value when 'type' parameter is null, you can use the Condition property:
[CacheResult("thingsByType", "#type == null ? 'AllThings' : #type.Name", Condition = "#type != null")]
public IList<Thing> GetListOfThingsByType(type)
2. Can I somehow use the expression language to better control the cache key of the CacheResultItems tag? It seems that it can only ose one specified parameter now? (Meaning I have to put it in a dedicated cache I guess.)
Sure, as you can see for question 1.
Each expression have a context that you gonna target by default ("Id" means "myCurrentContext.Id") and variables you can access using prefix #.
When using the CacheResultItems attribute, the context is the current item from the list returned by the method, and variables are the arguments of the method.
HTH,
Bruno
sideout
02-24-2008, 05:44 PM
I'm not really understanding what the purpose of [CacheParameter("inventors", "Name")] is. The definition of caching method parameter doesn't help me understand why you'd do this since your Get methods are the ones that need to know what key you want to use for your cache.
Bruno Baia
02-24-2008, 06:07 PM
Hi,
I'm not really understanding what the purpose of [CacheParameter("inventors", "Name")] is. The definition of caching method parameter doesn't help me understand why you'd do this since your Get methods are the ones that need to know what key you want to use for your cache.
The CacheParameter is used here to update the value in cache and avoid another request.
[CacheResult("inventors", "#name")]
public Inventor Load(string name)
{
return (Inventor) inventors[name];
}
public void Save([CacheParameter("inventors", "Name")] Inventor inventor)
{
inventor.Nationality = "Serbian";
}
You can also use [InvalidateCache("inventors", Keys = "#inventor.Name")] on the Save method.
HTH,
Bruno
sideout
02-25-2008, 06:18 PM
Ah. That's neat. Thank you.
sideout
02-26-2008, 10:14 PM
If I call one of my methods with a CacheResult attribute on them from my test page, the caching works fine (whether I call the per item one or the whole collection one). If I call a method on the Dal that in turn calls the per item one, suddenly my cache is not used (Sql Profiler trace confirms and does the SP call each time). This is strange.
Also I find that Spring creates a separate cache entry for each collection value individually right when the main collection is requested. This seems like a waste of resources and we have a cache viewer for inspecting the cache and this bloats the list of items in it greatly. If the main collection with all the items is requested is there no way for Spring to simply retrieve the individual Myobject right from the cached collection?
Spring Config
<!-- Cache aspect -->
<object id="CacheAspect" type="Spring.Aspects.Cache.CacheAspect, Spring.Aop"/>
<object id="AspNetCache" type="Spring.Caching.AspNetCache, Spring.Web"/>
<!-- Apply aspects to DAOs -->
<!-- Spring object name, not your c# code name and it's case sensitive! -->
<object type="Spring.Aop.Framework.AutoProxy.ObjectNameAutoProxy Creator, Spring.Aop">
<property name="ObjectNames">
<list>
<value>myobjectDal</value>
</list>
</property>
<property name="InterceptorNames">
<list>
<value>CacheAspect</value>
</list>
</property>
</object>
<db:provider id="dbProvider"
provider="SqlServer-2.0"
connectionString="blahblah"/>
<object id="adoTemplate" type="Spring.Data.Core.AdoTemplate, Spring.Data">
<property name="DbProvider" ref="dbProvider"/>
</object>
<object id="myobjectDal" type="MyNs.SpringTest.MyobjectDal, MyNs">
<property name="AdoTemplate" ref="adoTemplate"/>
</object>
Dal
namespace MyNs.SpringTest
{
public class MyobjectDal : AdoDaoSupport, IMyobjectDal
{
[CacheResult("AspNetCache", "'Myobject'", TimeToLive = "0:1:0")]
[CacheResultItems("AspNetCache", "'Myobject.MyobjectID=' + MyobjectID", TimeToLive = "0:1:0")]
[CacheResultItems("AspNetCache", "'Myobject.Name=' + Name", TimeToLive = "0:1:0")]
public Myobject GetMyobjects()
{
Myobject results = new Myobject();
IList list = AdoTemplate.QueryWithRowMapper(CommandType.StoredP rocedure, "dbo.MyobjectListGet", myobjectMapper);
foreach (Myobject d in list)
{
results.Add(d);
}
return results;
}
[CacheResult("AspNetCache", "'Myobject.MyobjectID=' + #myobjectID", TimeToLive = "0:1:0")]
public Myobject GetMyobject(int myobjectID)
{
return (Myobject)AdoTemplate.QueryForObject(CommandType.S toredProcedure,
"dbo.MyobjectListGet",
myobjectMapper, "myobjectID", DbType.Int32, 0, myobjectID);
}
[CacheResult("AspNetCache", "'Myobject.Name=' + #name", TimeToLive = "0:1:0")]
public Myobject GetMyobject(string name)
{
return (Myobject)AdoTemplate.QueryForObject(CommandType.S toredProcedure,
"dbo.MyobjectListGet",
myobjectMapper, "name", DbType.String, 0, name);
}
public string GetSiteName(int myobjectID, string origin)
{
// origin stuff removed, it fails to use the cache even returning a simple property while GetMyojbect() called from outside the Dal
// uses the cache properly. strange
Myobject d = GetMyobject(myobjectID);
return d.Name;
}
}
}
Test Page
IApplicationContext ctx = ContextRegistry.GetContext();
IMyobjectDal dal = (IMyobjectDal)ctx.GetObject("myobjectDal", typeof(IMyobjectDal));
string siteName = dal.GetMyobject(1).Name; // pulled from cache properly
Response.Write(String.Format("name again: {0} timestamp: {1}<br>", siteName, DateTime.Now.ToString()));
siteName = dal.GetSiteName(1,"blah"); // never pulled from cache! why?
Myobject d = dal.GetMyobject(); // pulled from cache properly
Response.Write(String.Format("name: {0} timestamp: {1}<br>",d[1].Name,DateTime.Now.ToString()));
Bruno Baia
02-28-2008, 11:54 PM
Hi,
If I call one of my methods with a CacheResult attribute on them from my test page, the caching works fine (whether I call the per item one or the whole collection one). If I call a method on the Dal that in turn calls the per item one, suddenly my cache is not used (Sql Profiler trace confirms and does the SP call each time). This is strange.
Due to composition based proxy mechanismes, methods called within the object itself using this are not intercepted.
You can create a Service class with the GetSiteName method and with the DAO injected to do that.
There is also a work around with method injection to proxy 'this' : http://opensource.atlassian.com/confluence/spring/download/attachments/708/Spring.AopQuickStart.ProxyingThis.zip?version=1
Also I find that Spring creates a separate cache entry for each collection value individually right when the main collection is requested. This seems like a waste of resources and we have a cache viewer for inspecting the cache and this bloats the list of items in it greatly. If the main collection with all the items is requested is there no way for Spring to simply retrieve the individual Myobject right from the cached collection?
Just remove the CacheResult attribute from the GetMyobjects method i you don't want to cache the whole collection.
- Bruno
avidgoffer
06-21-2010, 02:06 PM
I have been reading this post to help me get going on my caching and am running into a problem. When I attempt to do call the method below I get the following error:
"Cannot initialize property or field node 'LocalTariffId' because the specified context is null." I thought the attribute syntax I am using below would use the LocalTariffId property of the result once it returns to cache my data. This error occurs as soon as I attempt to step into the method. It seems to me that it is trying access that property too soon. I must be missing something so any advice you could provide is greatly appreciated!
[CacheResult("AspNetCache", "'LocalTariff.Id=' + LocalTariffId", TimeToLive = "00:10:00")]
public Domain.LocalTariffs.LocalTariff GetDefault(string agencyCode)
I am also getting a weird error after the first error I was hoping somebody could shed some light on. It has to do with log4net at least I think it does. My logging is working so I am not sure what this one is about.
IGCSoftware.HHG.Business.LocalTariffsFacade - Exception thrown in GetDefaultLocalTariff;GetDefaultLocalTariff;9c0bb3 93-369c-4501-a2ce-9325fe525e38;183341 ms
<log4net.Error>Exception rendering object type [Spring.Core.NullValueInNestedPathException]<stackTrace>System.BadImageFormatException: The parameters and the signature of the method don't match.
at System.Reflection.RuntimeParameterInfo.GetParamete rs(IRuntimeMethodInfo methodHandle, MemberInfo member, Signature sig, ParameterInfo& returnParameter, Boolean fetchReturnParameter)
at System.Reflection.RuntimeMethodInfo.FetchNonReturn Parameters()
at System.Reflection.RuntimeMethodInfo.GetParameters( )
at System.Diagnostics.StackTrace.ToString(TraceFormat traceFormat)
at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
at System.Exception.GetStackTrace(Boolean needFileInfo)
at System.Exception.ToString(Boolean needFileLineInfo)
at System.Exception.ToString()
at log4net.ObjectRenderer.DefaultRenderer.RenderObjec t(RendererMap rendererMap, Object obj, TextWriter writer)
at log4net.ObjectRenderer.RendererMap.FindAndRender(O bject obj, TextWriter writer)</stackTrace></log4net.Error>
thank you in advance for any and all help!
Bruno Baia
07-06-2010, 06:50 PM
Hi,
You can't use the returned object to generate the key of the CacheResult attribute. You have to use parameters of the method to generate the key (here '#agencyCode').
Bruno, in regards to this post forum.springframework.net/… at the bottom of your posting I thought you were referencing a property of a return object ([CacheResultItems("thingById", "Id")] // Will use Thing.Id as a key). Could you clarify this a little more for me?
But that's CacheResultItems attribute, not CacheResult attribute.
CacheResultItems attribute is used when returning a collection as a return value to cache elements of the collection separatly.
[CacheResult("thingsByType", "#type.Name")] // or #type for the cache key
[CacheResultItems("thingById", "Id")] // Will use Thing.Id as a key
public IList<Thing> GetListOfThingsByType(type)
[CacheResult("thingById", "thingId")]
public Thing GetThingById(string thingId)
In this example, call to GetListOfThingsByType will cache every 'Thing' object of the collection. So if you call GetThingById it will return the 'Thing' object from the cache.
HTH, Bruno
Powered by vBulletin® Version 4.2.0 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.