Cache is an important part of the reactive application. Let’s imagine that we don’t have any cache on the server side. In that case every request for every page of your report will cause document rendering. Some documents can be rather huge and it’s not efficient to call document rendering. In the ideal situation we should call document rendering once. On the other side we shouldn’t think about cache like about durable storage on the server side.
We have several implementations of cache on the server side. You may use them in your applications. Their names are SessionCache and AspNetCache. SessionCache as you can guess is based on session as well as AspNetCache based on the AspNet Cache.
You may think that all the problems are solved. But sometimes document is not living for a long time in the cache for some reasons. Today I’ll show you how to organize cache based on the MS SQL database storage. It won’t be cache no longer in the direct meaning but storage which is available on the server side. Real cache is stored in memory. The main idea is to prevent multiple rendering of the document.
There is an interface PerpetuumSoft.ReportingServices.Viewer.Server.IReportCache, which we should implement in our “cache” substitute. Let’s call this “cache” StorageCache.
Here is a code of our StorageCache implementation:
{
private AspNetCache rapidCache = new AspNetCache();
public virtual void AddDocumentToCache(string key, object value, params object[] parameters)
{
DocumentStorage dataSet = new DocumentStorage();
//Add document to AspNet Cache
rapidCache.AddDocumentToCache(key, value, parameters);
//Add document to database
AddToDB(key,value);
}
private void AddToDB(string key, object value)
{
try
{
//create dataset
DocumentStorage storageDS = new DocumentStorage();
DataRow dr = storageDS._DocumentStorage.NewRow();
//add key to database
dr["docKey"] = key;
//serialize
BinaryFormatter serializer = new BinaryFormatter();
MemoryStream memStream = new MemoryStream();
serializer.Serialize(memStream, value);
memStream.Position = 0;
//add document value
dr["docVal"] = memStream.ToArray();
storageDS._DocumentStorage.Rows.Add(dr);
//check if the record already exists
DocumentStorageTableAdapters.DocumentStorageTableAdapter tableAdapter = new DocumentStorageTableAdapters.DocumentStorageTableAdapter();
SampleApplicationSL4.Web.DocumentStorage.DocumentStorageDataTable table = tableAdapter.GetDataByKey(key);
if (table.Rows.Count == 0)
{
//add
tableAdapter.Update(storageDS._DocumentStorage);
}
else
{
//update
table.Rows[0]["docVal"]=memStream.ToString();
tableAdapter.Update(table);
}
memStream.Flush();
}
catch (Exception ee)
{
}
}
private object GetFromDB(string key)
{
DataTable docTable=null;
try
{
//retrieves data from database
DocumentStorageTableAdapters.DocumentStorageTableAdapter adapter = new DocumentStorageTableAdapters.DocumentStorageTableAdapter();
docTable = adapter.GetDataByKey(key);
}
catch(Exception ee)
{
CoreLogger.Error("GetFromDB", ee);
}
if (docTable.Rows.Count > 0)
{
//deserialize
byte[] result = (byte[]) docTable.Rows[0]["docVal"];
MemoryStream memStream = new MemoryStream(result);
BinaryFormatter serializer = new BinaryFormatter();
object res = serializer.Deserialize(memStream);
memStream.Flush();
return res;
}
return null;
}
public virtual object GetDocumentFromCache(string key)
{
if (key == null) return null;
//try to retrieve from Asp.net cache
object result = rapidCache.GetDocumentFromCache(key);
if (result == null)
{
//durable storage request
result = GetFromDB(key);
rapidCache.AddDocumentToCache(key, result);
}
return result;
}
public void AddExecutionToCache(string executionId, object executionCacheObject)
{
//add to Asp.net cache
rapidCache.AddExecutionToCache(executionId, executionCacheObject);
//add to durable storage
AddToDB(executionId, executionCacheObject);
}
public object GetExecutionFromCache(string executionId)
{
//try to retrieve data from Asp.net cache
object result = rapidCache.GetExecutionFromCache(executionId);
if (result == null)
{//retrieve data from durable storage
result = GetFromDB(executionId);
rapidCache.AddExecutionToCache(executionId, result);
}
return result;
}
public void DeleteFromCache(string key)
{
DocumentStorageTableAdapters.DocumentStorageTableAdapter ta = new DocumentStorageTableAdapters.DocumentStorageTableAdapter();
//Delete object from Asp.net cache
rapidCache.DeleteFromCache(key);
//Delete object from the database
ta.DeleteByKey(key);
}
}
In your ReportService implementation you need to override CreateCache method:
public class ReportService : ReportServiceRemote
{
protected override IReportCache CreateCache()
{
return new StorageCache();
}
}
Of course this is only a demo, which demonstrates how you can override cache behavior and if you want to add this solution in your live system you might also think about deleting useless information from the database and to store timestamp field for that purposes in your database. You may schedule a deleting job for your SQL Agent to be run in the specific time interval.