Wahyu Kurniawan Blog


Some of your need maybe at here





See also: Other Geeks@INDC

A Wonderfull Cache - Manage Cache Per Module

A Wonderfull Cache? Hmm, for some people, maybe it's nothing. But for me it's more than anything. I'll try to learn cache just for one condition, it's for boost web application performance. Btw, cache is not just only one solution to make web application performance more better.

I'm new in cache, so I hope, I get feedback from this article.

OK.... until now I have just one conclusion about Cache. Cache is just only storage that store in memory server that can accessed by all user who accessing the application. There is 2 condition when we want to use Cache.

  1. Cache must be updated constantly. When cache was created at the first time, it will stay a live in memory server until expired. So, cache must be removed from memory when "ADD", "UPDATE", and "DELETE" in database (except you want to let it untill expired).
  2. When we want to use Cache in filtering data, we must creating some unique keys of Cache. If there is ten combination of filter data is equal as ten keys of Cache.

I have try to make an application to show list of data with Cache. First I make some method to manage Cache.
private ArrayList CacheRemoveAll(ArrayList CacheKeysList)
{
 foreach (string CacheKey in CacheKeysList)
 {
  Cache.Remove(CacheKey);
 }
 CacheKeysList = new ArrayList();
 return CacheKeysList;
}
private ArrayList CacheRemove(ArrayList CacheKeysList, string CacheKey)
{
 Cache.Remove(CacheKey);
 if (CacheKeysList.Count > 0) CacheKeysList.Remove(CacheKey);
 return CacheKeysList;
}
private ArrayList CacheAdd(ArrayList CacheKeysList, string CacheKey, object Value)
{
 //You can set time expiration in web config
 Cache.Add(CacheKey, Value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 10, 0), CacheItemPriority.Default, null);
 CacheKeysList.Add(CacheKey);
 return CacheKeysList;
}

Create private variable to store keys of Cache, and it should be able accessed by all user, so I use Cache too to store list of the keys.
If we want to create another module/page which get the data from other table, we must create a new private variable. Change "ModuleCacheKeys" to another key according to module name or table.

private ArrayList ModuleCacheKeys
{
 get { return (Cache["ModuleCacheKeys"] == null) ? new ArrayList() : (ArrayList)Cache["ModuleCacheKeys"]; }
 set { Cache["ModuleCacheKeys"] = value; }
}

When binding data to datagrid/gridview, we can use that methods to add or remove datasource to/from Cache. Here is my code when binding the data.

private void BindData()
{

 //txtKeyword.Text,ddl.SelectedValue is control in filter area
 //Used each control value to format cache key
 string CacheKey = string.Format("{0}{1}",txtKeyword.Text,ddl.SelectedValue);
 DataSet DataSource = new DataSet();
 DataSource = (DataSet)Cache[CacheKey];

 if (DataSource == null)
 {
  ModuleCacheKeys = CacheRemove(ModuleCacheKeys, CacheKey);
  ConnectionStringSettings Module = ConfigurationManager.ConnectionStrings["Module"];

  DataSet ds = new DataSet();
  ds = SqlHelper.ExecuteDataset(Module .ConnectionString, CommandType.Text, "SELECT * FROM TableName WHERE '" + txtKeyword.Text + "' IS NULL OR name LIKE '%" + txtKeyword.Text + "%' AND code = '" + ddl.SelectedValue + "'");

  DataSource = ds;
  ModuleCacheKeys = CacheAdd(ModuleCacheKeys, CacheKey, DataSource);
 }

 GridView1.DataSource = DataSource.Tables[0].DefaultView;
 GridView1.DataBind();
}

We can use CacheRemoveAll method to remove all cache. We can use it in Add, Update, or Delete method.

Note: You must change some code so that can according to .NET 1.1

Share this post: | | | |

Comments

bsrd said:

may I share my experiences?

1. Do not ever using a unique key that can be an UNUNIQUE KEY. Looking to your code above, I assume that you use/set CACHE KEY in UI layer. If I may give you suggestion, create it in one CENTRAL object that responsible with DATA MANIPULATION.

With this statement :

string CacheKey = string.Format("{0}{1}",txtKeyword.Text,ddl.SelectedValue);

There will be big possibilities that this unique key become ununique key, because you can't assure that txtKeyword.Text will be A UNIQUE KEY that won't be used/manipulated in any other part of your code then made unexpected result.

2. What I've done: - Make a domain level POCO which is has a UNIQUE NAMESPACE. Use one central MANAGER/SERVICE that manage add/removal process for the cache using the NAMESPACE+CLASS name of this domain object.

3. The last, I'll show the code which I created for this purpose. Anyway I still got a filling that my code is not 100% right either, so we can still DISCUSS about this CACHE things :D

# April 14, 2007 7:21 AM

wahyukurniawan said:

Thanks for your comment.

Can you explain why txtKeyword.Text will make a unique key become ununique key?

Is it whitespace? We can handle that with TRIM. It will become txtKeyword.Text.Trim().

Beside that, if the datasource that fill from the cache have null value, it will remove automatically before added again to the list of keys.

I'm sorry if I wrong. I look forward to your reply.

# April 16, 2007 10:56 AM

bsrd said:

Like what I said b4, you can't assure that combination between txtKeyword.Text and ddl.SelectedValue will produce a UNIQUE key, instead of you assure that the txtKeyword.Text is a unique word that won't be used in other part of your code. But to make sure this happen is hard, as your number of code/page increment.

I think it would be good if you create a cache key in CONTEXTUAL way and it can be assure in a UNIQUE DOMAIN level.

From the example you gave above. Let say we have 2 tables in our application: Table1 and Table2.

You do a query in 2 pages: Page1 and Page2, with Table1 and Table2 for each pages in order.

In initial condition let say, we haven't any cache created already. As in order user opens Page1 that has a txtKeyword.Text and ddl.SelectedValue, this Page1 will created cache with those key right, since there is no cache in the WebCache yet.

Next the user open Page2 with a txtKeyword1.Text and ddl1.SelectedValue combination. The problem arises when those two keyword produces the same key with one in the Page1. So it will query the data to the Cache, since it's already there. But, WRONG RESULT. And this will never query to the Table2 as user expected.

The simply resolution? Add text "Table1" and "Table2" as the prefix for each key, then it will become two different keys :D. This is what I'm talking about DOMAIN LEVEL key.

tx.

# April 16, 2007 1:21 PM

wahyukurniawan said:

IC, I think I understand what are you talking about.

You think like that 'cause I not yet given explanation more detail about private variable to store cache keys. I'm sorry about that.

I can make sure that the keys is unique because private variable occupied by cache key is unique per table/module. So, if you want to create another page which show data from other table, you must create private variable too.

I had to think like you too, but it's difficult for me to remove cache per table/module. Can you solve my problem? I think it's nice if we just create one private variable that can accessed all over the page.

# April 16, 2007 1:54 PM

wahyukurniawan said:

I have just added some information about private variable. I hope it will clear some miss understanding about that.

# April 16, 2007 2:09 PM

bsrd said:

Do you reffer private variable to "ModuleCacheKeys" as the KEY? So if I want to create another page I create an ArrayList with KEY: "ModuleCacheKeys1" ?

# April 16, 2007 3:02 PM

wahyukurniawan said:

Yes.

# April 16, 2007 3:30 PM