Monday, June 4, 2007

Cache

Por razões de desempenho, para fazer menos requisições e execuções de querys na DB, surgio a necessidade de fazer cache, mas como manter os filtros e os resultado de diversas querys distintas? Tive a idéia de fazer serialização dos DataTable, e para isto fiz uma classe, que pode além de DataTable, fazer serialização de qualquer objecto que suporte serialization.

Exemplo do uso da classe Cache:

[CODE]
Cache cache = new Cache(WebConfigurationManager.AppSettings["CacheFolder"]);
if (cache.PleaseSaveMe(query))
{
cache.Save(query, dv.Table, WebConfigurationManager.AppSettings["CacheTime"]);
}
else
{
(DataTable)cache.Load(query);
}
[/CODE]

Neste caso usei a query para servir de ID para o Cache, ou seja sempre que for uma query diferente é gravado em Cache o DataTable gerado por esta query, ou se for uma query já executada então o DataTable é recuperado.

Descrição:

[CODE]
new Cache("CACHE_FOLDER");
if (cache.PleaseSaveMe("CACHE_ID"))
{
cache.Save("CACHE_ID", CACHE_OBJECT, "CACHE_TIME");
}
else
{
(CACHE_OBJECT)cache.Load("CACHE_ID")
}
[/CODE]

CACHE_FOLDER é o caminho para a pasta que vai conter os arquivos de cache.

CACHE_ID é o ID de referência de um objeto em cache, é a chave para poder recupera-lo.

CACHE_OBJECT é o objecto que será gravado em cache, lembrando que tem o objeto tem que suportar serialization.

CACHE_TIME define o tempo em que o objeto ficará em cache, pode ser passado no seguinte formato (também é possível em long):
  - "2W" = 2 semanas
  - "5D" = 5 dias
  - "1H" = 1 hora
  - "15M" = 15 minutos
  - "50S" = 50 segundos

cache.PleaseSaveMe verifica se é necessário gravar o cache.

cache.Save grava um novo ou sobrepõe o objeto em cache.

cache.Load recupera o objeto que esta em cache.

Para cada objeto em cache é gerado 3 arquivos.

  - [GUID].id.cache identificador do cache, o tal ID que é passado no save, e que é usado para recuperar o objeto.

  - [GUID].obj.cache arquivo do objeto serializado.

  - [GUID].time.cache tempo que esta em cache.

Código da Class Cache:

[CODE]
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
 
public class Cache
{
public const string ID_EXT = ".id.cache";
public const string TIME_EXT = ".time.cache";
public const string OBJ_EXT = ".obj.cache";
 
private string folder = "";
 
public const long WEEK = 6048000000000L;
public const long DAY = 864000000000L;
public const long HOUR = 36000000000L;
public const long MINUTE = 600000000L;
public const long SECOND = 10000000L;
 
public Cache(string _folder)
{
folder = _folder;
}
 
public string GetGuid(string id)
{
string[] files = Directory.GetFiles(folder);
string guid = "";
foreach (string file_id in files)
{
if (file_id.EndsWith(ID_EXT))
{
StreamReader srID = new StreamReader(file_id);
if (id == srID.ReadToEnd())
{
guid = file_id.Replace(folder, "").Replace(ID_EXT, "");
}
srID.Close();
if (guid != "")
{
break;
}
}
}
return guid;
}
 
public object Load(string id)
{
string fileOBJ = folder + GetGuid(id) + OBJ_EXT;
FileStream fsOBJ = new FileStream(fileOBJ, FileMode.Open);
object obj = new BinaryFormatter().Deserialize(fsOBJ);
fsOBJ.Close();
return obj;
}
 
public void Save(string id, object obj, long lifeTime)
{
Save(id, obj, lifeTime.ToString(), true);
}
 
public void Save(string id, object obj, string lifeTime)
{
Save(id, obj, lifeTime, false);
}
 
private void Save(string id, object obj, string lifeTime, bool lifeTimeIsLong)
{
Clear(id);
if (!lifeTimeIsLong)
{
string timeType = lifeTime.Substring(lifeTime.Length - 1);
long time = Convert.ToInt64(lifeTime.Substring(0, lifeTime.Length - 1));
switch (timeType.ToUpper())
{
case "W":
lifeTime = ""+ (time * WEEK);
break;
case "D":
lifeTime = ""+ (time * DAY);
break;
case "H":
lifeTime = ""+ (time * HOUR);
break;
case "M":
lifeTime = ""+ (time * MINUTE);
break;
case "S":
lifeTime = ""+ (time * SECOND);
break;
default:
break;
}
}
string file = folder + Guid.NewGuid().ToString();
string file_id = file + ID_EXT;
string file_time = file + TIME_EXT;
string file_obj = file + OBJ_EXT;
StreamWriter swID = File.CreateText(file_id);
swID.Write(id);
swID.Close();
StreamWriter swTIME = File.CreateText(file_time);
swTIME.WriteLine(DateTime.Now.Ticks.ToString());
swTIME.WriteLine(lifeTime);
swTIME.Close();
 
MemoryStream msOBJ = new MemoryStream();
new BinaryFormatter().Serialize(msOBJ, obj);
FileStream fsOBJECT = File.Create(file_obj);
fsOBJECT.Write(msOBJ.ToArray(), 0, (int)msOBJ.Length);
fsOBJECT.Flush();
fsOBJECT.Close();
msOBJ.Close();
}
 
public bool PleaseSaveMe(string id)
{
 
string[] files = Directory.GetFiles(folder);
bool found = false;
bool result = false;
foreach (string file_id in files)
{
if (file_id.EndsWith(ID_EXT))
{
string file_time = file_id.Replace(ID_EXT, TIME_EXT);
StreamReader srID = new StreamReader(file_id);
StreamReader srTIME = new StreamReader(file_time);
string _id = srID.ReadToEnd();
long createTime = Convert.ToInt64(srTIME.ReadLine());
long lifeTime = Convert.ToInt64(srTIME.ReadLine());
srID.Close();
srTIME.Close();
if (id == _id)
{
found = true;
if (DateTime.Now.Ticks - lifeTime > createTime)
{
result = true;
}
break;
}
}
}
if (found == false)
{
return true;
}
return result;
}
 
public void Clear()
{
Clear("");
}
 
public void Clear(string id)
{
string[] files = Directory.GetFiles(folder);
foreach (string file_id in files)
{
if (file_id.EndsWith(ID_EXT))
{
string file_time = file_id.Replace(ID_EXT, TIME_EXT);
StreamReader srID = new StreamReader(file_id);
StreamReader srTIME = new StreamReader(file_time);
string _id = srID.ReadToEnd();
long createTime = Convert.ToInt64(srTIME.ReadLine());
long lifeTime = Convert.ToInt64(srTIME.ReadLine());
srID.Close();
srTIME.Close();
if (id == _id)
{
File.Delete(file_id);
File.Delete(file_time);
File.Delete(file_id.Replace(ID_EXT, OBJ_EXT));
}
else
{
if (DateTime.Now.Ticks - lifeTime > createTime)
{
File.Delete(file_id);
File.Delete(file_time);
File.Delete(file_id.Replace(ID_EXT, OBJ_EXT));
}
}
}
}
}
}

[/CODE]

No comments:

Post a Comment