View Full Version : Where has AspNetCacheAdvice gone?
bjornwang
01-22-2007, 10:42 PM
I'm unable to find AspNetCacheAdvice in the newest Spring build. Where has it gone?
I was very happy to see JIRA-issue [SPRNET-360] fixed in the changelog ("CacheAdvice does not take into account the parameters passed to the method"), and rushed to download the Spring 1.1 P3 build. But alas, the AspNetCacheAdvice class seems to be gone.
I was stupid enough to delete my previous version of Spring, so no my code doesn't run at all. Naturally I'm very keen to find a solution.
Is there any way to get hold of the new, bugfixed cache classes?
Or am I missing something? Have they moved or changed name?
Aleks Seovic
01-27-2007, 10:20 PM
Hi,
My appologies -- Bruno fixed this particular issue, but as we are working on the complete overhaul of the cache advice for RC1, which should both fix this bug and provide pluggable cache store mechanism, I removed AspNetCacheAdvice for the time being as it won't be used in the future. I was hoping to get this done for P3, but unfortunately there wasn't enough time.
I'll work on this next week and let you know as soon as it's fully implemented and tested. In the meantime, if you really need this class in order to get your code to run, you can download one of the older releases from Sourceforge or look for it in the CVS attic and add it to Spring.Web manually.
Once again, sorry for the confusion and all the trouble this has caused on your end.
- Aleks
Aleks Seovic
02-08-2007, 08:11 AM
Hi Bjorn,
I just committed new implementation of the CacheAdvice.
Major difference compared to the previous implementation is that CacheAdvice is no longer abstract. Instead, it is a complete advice that delegates retrieval and storage of cache items to a configured ICache instance. There are two ICache implementations at the moment: NonExpiringCache, which is really nothing more than a fully synchronized hashtable, and AspNetCache, which I believe is the one you want to use. I will also add integration projects for EntLib Caching Block and Tangosol Coherence for .NET shortly, which will allow you to use them as cache stores as well.
These changes imply that advice configuration is somewhat different. In order to configure cache advice that uses ASP.NET Cache as a store, you need to create the following definitions:
<object id="cache" type="Spring.Caching.AspNetCache, Spring.Web"/>
<object id="cacheAdvice" type="Spring.Aop.Advice.CacheAdvice, Spring.Aop">
<property name="Cache" ref="cache"/>
</object>
Then you just need to apply advice as usual, using ProxyFactoryObject or one of the autoproxy creators.
Another improvement I made is the addition of VaryBy property to CacheAttribute. It allows you to specify a comma-separated list of method argument names that should be used to create cache key. By default, all method arguments will be used within the key, but you can use VaryBy property to modify that behavior:
[Cache(VaryBy = "name,resolution")]
public byte[] Tiff2Pdf(string name, Resolution resolution, byte[] tiffContent)
{ ... }
In the example above, resoluting PDF content will be cached for each unique name/resolution combo, but the original TIFF content will not be key element.
Important thing to note is that in order to be able to use AspNetCache, all argument types need to implement ToString method that returns same string if two instances are equal and different strings for non-equal instances. This limitation is due to the fact that ASP.NET Cache uses strings as keys instead of objects, so we need to convert all arguments to strings when creating string version of the cache key.
Feel free to try new CacheAdvice out and let me know i you run into any issues.
Regards,
Aleks
bjornwang
02-19-2007, 01:11 PM
Hello Aleks,
The new CacheAdvice works like a charm!
Kudos for the new VaryByParam property. :-)
Aleks Seovic
02-20-2007, 03:25 PM
Hi Bjorn,
Glad you like it :-)
Based on the discussion I had with Bruno, I also added VaryByExpression property, which allows you to use SpEL expressions instead of just parameter names to specify key elements. For example, you can do something like this:
[Cache(VaryByExpression = "#user.EMail")]
public IPermissionSet GetUserPermissions(User user)
{ .. }
As you can see, this allows you to use properties of one or more method arguments as key elements (you can separate multiple elements with commas, just like with VaryByParam property).
This is also fairly obvious from the example, but I'll state it anyway -- method arguments are passed to the expression engine as variables, keyed by argument name, so you need to use variable expression to access them within expression.
That said, CacheAdvice can already be considered somwhat of a legacy ;) I'm not even sure if it will remain in the codebase at this point, as there is a already a new implementation of the caching aspect that adds a number of features:
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
EnergyRecrui
11-21-2008, 03:33 AM
I tend to agree with Federico on this one -- different
separators based on locale are a bad idea. Imagine a
situation where a company builds a product using Spring and
ships it all over the world. It would be really, really bad
if the same object definitions didn't work regardless of the
locale on the machine application is installed on. Some
things simply shouldn't be locale-sensitive, and I believe
*everything* that is defined in the config file falls into
this category.Decimal number parsing also falls into the same category --
we just need to decide on the format (and for better or
worse, invariant culture, which is the same as en-US is the
most logical choice) and stick with it, as most of the
expressions will be defined in the config file. Allowing for
locale-specific formats is probably not a good idea in this
case either.
Energy jobs
12-19-2008, 02:37 PM
I tend to agree with Federico on this one -- different
separators based on locale are a bad idea. Imagine a
situation where a company builds a product using Spring and
ships it all over the world. It would be really, really bad
if the same object definitions didn't work regardless of the
locale on the machine application is installed on. Some
things simply shouldn't be locale-sensitive, and I believe
*everything* that is defined in the config file falls into
this category.ecimal number parsing also falls into the same category --
we just need to decide on the format (and for better or
worse, invariant culture, which is the same as en-US is the
most logical choice) and stick with it, as most of the
expressions will be defined in the config file. Allowing for
locale-specific formats is probably not a good idea in this
case either. I will modify expression node for real numbers
to use invariant culture so the same tests work regardless
of the locale. Thanks for catching this Federico.
vBulletin® v3.7.3, Copyright ©2000-2009, Jelsoft Enterprises Ltd.