Misiu
Misiu

Reputation: 4919

Better Caching For .NET 3.5 site

I'm trying to optimize old application and right now I'm trying to decrease SQL requests to minimum. I've created simple caching mechanism, but it requires 3 methods:

public static List<Models.CarModel> GetCarModels()
{
    return GetCarModels(false);
}

public static List<Models.CarModel> GetCarModels(bool reload)
{
    const string my_key = "GetCarModels";
    object list = HttpContext.Current.Cache[my_key] as List<Models.CarModel>;
    if ((reload) || (list == null))
    {
        list = GetCarModels_P();
        HttpContext.Current.Cache.Insert(my_key, list, null, DateTime.Now.AddHours(1), TimeSpan.Zero);
    }
    return (List<Models.CarModel>)list;
}

private static List<Models.CarModel> GetCarModels_P()
{
    var tmp_list = new List<Models.CarModel>();

    using (var conn = new SqlConnection())
    {
        conn.ConnectionString = ConfigurationManager.ConnectionStrings["HelpDesk"].ToString();
        using (var cmd = new SqlCommand(@"SELECT_CAR_MODELS", conn))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandTimeout = 360;
            conn.Open();
            using (SqlDataReader sdr = cmd.ExecuteReader())
            {
                while (sdr.Read())
                {
                    var carModel = new Models.CarModel()
                    {
                        Id = Convert.ToInt32(sdr["Id"]),
                        Name= Convert.ToString(sdr["CarName"])
                    };
                    tmp_list.Add(carModel );
                }
            }
            conn.Close();
        }
    }
    return tmp_list;
}

This works fine, sql query results are cached for 1 hour, but for every old method I must create 3 new, so for 10 old methods I must write 30 new.
I would like to reduce number of code that I must write, there probably is a way to create generic method to read/write to cache.

GetCarModels_P will probably not change, but my 2 public methods can be optimised (I quess).

Upvotes: 0

Views: 1101

Answers (3)

Thorarin
Thorarin

Reputation: 48496

You could implement the pattern in a helper class, something like this:

internal class Cached<T> where T: class
{
    private readonly Func<T> _loadFunc;
    private readonly string _key;
    private readonly TimeSpan _expiration;

    public Cached(Func<T> loadFunc, string key, TimeSpan expiration)
    {
        _loadFunc = loadFunc;
        _key = key;
        _expiration = expiration;
    }

    public T GetValue(bool reload)
    {
        T value = (T)HttpContext.Current.Cache[_key];

        if (reload || value == null)
        {
            value = _loadFunc();
            HttpContext.Current.Cache.Insert(_key, value, null,
                DateTime.Now + _expiration, TimeSpan.Zero);
        }

        return value;
    }
}

You'd use it like so:

private static Cached<List<Models.CarModel>> _carModels =
      new Cached<List<Models.CarModel>>(
          GetCarModels_P, "GetCarModels", TimeSpan.FromHours(1));

public static List<Models.CarModel> GetCarModels()
{
    return _carModels.GetValue(false);
}

You can also do it with only a generic method of course (like one of the other answers posted while I was writing this). It depends on the situation which I would use. If you have a similar call that forces a reload, my method is a bit easier because you don't have to repeat yourself. However, on the downside, my implementation somewhat suggests that the Cached instance itself is retaining the data personally, while it's actually the HttpContext cache doing so.

Upvotes: 1

fejesjoco
fejesjoco

Reputation: 11903

You just wrote two very good reasons why you should use an ORM layer like NHibernate or Entity Framework. They take away the pain to write incredible amounts of boilerplate SQL-calling functions. They support caching, I know for a fact that it's easy as a pie with NH, not sure about EF.

No need to throw out any of your existing code, ORM's can coexist with your existing database code, or even use the exact same connections. You can slowly migrate your existing functions one by one, from barehanded SQL to the ORM of your choice. If you want to spend time optimizing an old app, I would say this is the best way to spend that time.

Upvotes: 0

Sklivvz
Sklivvz

Reputation: 31163

You can use generics and a lambda to simplify your change:

public static List<T> GetViaCache<T>(bool reload, string key, Func<List<T>> loader)
{
    object list = HttpContext.Current.Cache[key] as List<T>;
    if ((reload) || (list == null))
    {
        list = loader();
        HttpContext.Current.Cache.Insert(key, list, null, DateTime.Now.AddHours(1), TimeSpan.Zero);
    }
    return list;
}

This simplifies the loading code as

public static List<Models.CarModel> GetCarModels(bool reload)
{
    return GetViaCache<Models.CarModel>(reload, "GetCarModels", GetCarModels_P);
}

of course you will still need to generate a unique key for the cache.

Upvotes: 1

Related Questions