views:

50

answers:

3

In C#, is it possible to decorate a method with an annotation to populate the cache object with the return value of the method?

Currently I'm using the following class to cache data objects:

public class SiteCache
{
// 7 days + 6 hours (offset to avoid repeats peak time)
    private const int KeepForHours = 174; 
    public static void Set(string cacheKey, Object o)
    {
        if (o != null)
            HttpContext.Current.Cache.Insert(cacheKey, o, null, DateTime.Now.AddHours(KeepForHours), TimeSpan.Zero);
    }
    public static object Get(string cacheKey)
    {
        return HttpContext.Current.Cache[cacheKey];
    }
    public static void Clear(string sKey)
    {
        HttpContext.Current.Cache.Remove(sKey);
    }
    public static void Clear()
    {
        foreach (DictionaryEntry item in HttpContext.Current.Cache)
        {
            Clear(item.Key.ToString());
        }
    }
}

In methods I want to cache I do this:

[DataObjectMethod(DataObjectMethodType.Select)]
public static SiteSettingsInfo SiteSettings_SelectOne_Name(string Name)
{
    var ck = string.Format("SiteSettings_SelectOne_Name-Name_{0}-", Name.ToLower());
    var dt = (DataTable)SiteCache.Get(ck);
    if (dt == null)
    {
        dt = new DataTable();
        dt.Load(ModelProvider.SiteSettings_SelectOne_Name(Name));
        SiteCache.Set(ck, dt);
    }
    var info = new SiteSettingsInfo();
    foreach (DataRowView dr in dt.DefaultView)
        info = SiteSettingsInfo_Load(dr);
    return info;
 }

Is it possible to separate those concerns like so: (notice the new annotation)

[CacheReturnValue]
[DataObjectMethod(DataObjectMethodType.Select)]
public static SiteSettingsInfo SiteSettings_SelectOne_Name(string Name)
{
    var dt = new DataTable();
    dt.Load(ModelProvider.SiteSettings_SelectOne_Name(Name));

    var info = new SiteSettingsInfo();
    foreach (DataRowView dr in dt.DefaultView)
        info = SiteSettingsInfo_Load(dr);
    return info;
 }
A: 

Yes, you'll need to use a process like PostSharp to give you the power to execute before/after code on method-attributes.

Current version of PostSharp is FREE if you dont need class/assembly level attributes.

Aren
A: 

Your idea is nice, but still, it is not so easy. I myself also thought about something similar.

It is pretty easy to create an attribute, but... actually, an attribute in itself doesn't do anything.

You'll need to use AOP (Aspect-Oriented Programming) to achieve this. There are plenty of good frameworks that let you do it in some way.

They basically work by creating so-called "proxies" around objects - they derive from your type progmatically, implement some stuff around your code, and then return the new type.
Some of them does this on the fly, while others use ILmerge and other stuff to manipulate your code after compilation.

Basically, you have to use Reflection to retrieve which items use your attribute, and do something about it. MSDN: Attributes

So, here's what you have to do:

  • Create a custom attribute with properties that describe what you need to achieve
  • Create a class that uses the attribute. It should retrieve which items use the attribute, and then do something about it.

There's more stuff: attributes tutorial, creating custom attributes. You can search and find even more about the topic.

Venemo
I was inspired by a podcast from Scott Hanselman about AOP. So validation controls work in MVC then if annotations don't do anything?
craigmoliver
Annotations in themselves don't do anything. But the validation framework does check what attributes do your properties have, and it contains the behaviour about what to do with them. (Edited my post to clarify.)
Venemo
A: 

It's not as elegant as using an attribute, but you could also add a method to your cache container that takes a delegate as a parameter and encapsulates the logic itself.

public class SiteCache
{

    public static T Retrieve<T>(Delegate d, params object[] methodParameters)
    {
        string key = d.Method.ReflectedType + "#" + d.Method.Name;
        for (int i = 0; i < methodParameters.Length; i++)
        {
            key += methodParameters[i].ToString();
        }

        object retVal = Get(key);
        if (retVal == null)
        {
            retVal = d.DynamicInvoke(methodParameters);
            Set(key, retVal);
        }
        return (T) retVal;
    }
    // the rest of your class goes here

Then you could call it in your ASP.NET code behind:

    delegate string DoStuffDelegate(string key);

    protected void Page_Load(object sender, EventArgs e)
    {

        string s1 = SiteCache.Retrieve<string>(new DoStuffDelegate(DoStuff), "my key");
        string s2 = SiteCache.Retrieve<string>(new DoStuffDelegate(DoStuff), "my key");

        //at this point, s1 will be the same as s2
    }

    int doStuffCount = 0;
    private string DoStuff(string key)
    {
        doStuffCount++;
        return string.Format("Calling do stuff with key '{0}' - count = {1}", key, doStuffCount);
    }
MrGumbe