SoloCodigo
CLR: .Net / Mono / Boo / Otros CLR => C# => Mensaje iniciado por: U2_Caparzo en Jueves 14 de Febrero de 2013, 04:57
-
Hola a todos.
Hoy vengo con algo un poco mas avanzado(no avanzado en nivel de programacion, sino que en cantidad ^^) a lo que antes habia puesto aqui.
Pasa que por alguna razon, MySql es excesivamente lento en mi pc(sobre 1 segundo para ejecutar un comando) y decidi hacer una base de datos con FileStreams, no soy muy experimentado como programador, por eso decidi postearlo aqui, porque quisiera comentarios de que esta mal, o el porque no es una buena idea(en caso de no serlo), que seria una buena idea para mejorarlo, etc...
Funciona de la siguiente manera.
-Se crea un archivo .map que contiene las ubicaciones de cada valor dentro del archivo, este se crea antes de usar la base de datos(como en Sql)
-Al crear la clase DBEngine, se escoge una Tabla(una carpeta donde estan los archivos y asi se obtienen los datos cargados previamente del .map)
-Se usa el metodo Select() para escoger un objeto (archivo sobre el cual actuara el FileStream), en este metodo se crea el FileStream.
2 metodos principales para escribir y leer, los demas son solo conversiones a otros tipos de variables.
DBEngine hereda la interface IDisposable para usar la palabra clave 'using' como con cualquier otro tipo de Stream
MappedFile.cs
//Field vs offset vs Size
using Map = Selector<string, int, int>;
/// <summary>
/// clase con los mapas de los archivos de la database
/// </summary>
internal class MappedFile
{
static bool hasLoadedMaps = false;
const string MapsPath = "C:\\Database\\DBMaps\\";
/// <summary>
/// table name vs fileMap
/// </summary>
static Dictionary<string, Map> DBMaps = new Dictionary<string, Map>();
static void GetMaps()
{
if (hasLoadedMaps)
return;
if (!Directory.Exists(MapsPath))
Directory.CreateDirectory(MapsPath);
foreach (string file in Directory.GetFiles(MapsPath))
{
if (file.EndsWith(".map"))
{
Map map = new Map();
StreamReader sr = new StreamReader(file);
while (sr.Peek() > 0)
{
string line = sr.ReadLine();
if (line.Contains('='))
{
string[] field = line.Split('=');
map.TryAdd(field[0], int.Parse(field[1]), int.Parse(field[2]));
}
}
string mapName = file.Substring(MapsPath.Length);
mapName = mapName.Remove(mapName.Length - 4);
DBMaps.Add(mapName, map);
}
}
hasLoadedMaps = true;
}
public static Selector<string, int, int> GetMap(string table)
{
if (!hasLoadedMaps)
GetMaps();
if (DBMaps.ContainsKey(table))
return DBMaps[table];
else
throw new Exception("El mapa para la tabla " + table + " no ha sido creado!");
}
}
VariableType.cs
/// <summary>
/// Contiene el tipo de la variable y su longitudo maxima como string mas el caracter '>'
/// </summary>
internal class VariableSize
{
public const int Int8 = 4,
Int16 = 6,
Int32 = 11,
Int64 = 20,
SmallString = 30,
MediumString = 65,
BigString = 100;
}
DBEngine.cs
public class DBEngine : IDisposable
{
/// <summary>
/// Obtiene el nombre del objeto que se esta manipulando
/// </summary>
public string CurrentObject { get; private set; }
/// <summary>
/// obtiene el nombre de la tabla a la que pertenece el objeto que se esta manipulando
/// </summary>
public string CurrentTable { get; private set; }
const char NullChar = '~';
private Selector<string, int, int> FileMap;
private FileStream fs;
/// <summary>
/// Crea una nueva instancia de DBEngine con la tabla seleccionada
/// </summary>
/// <param name="Object">tabla en la que se encuentra el objeto a manipular</param>
public DBEngine(string Table)
{
FileMap = MappedFile.GetMap(Table);
CurrentTable = Table;
}
/// <summary>
/// Objeto a manipular por el DBEngine
/// </summary>
/// <param name="Object"></param>
public DBEngine SelectObject(string Object)
{
CurrentObject = Object;
fs = new FileStream(Constants.DatabaseFolder + CurrentTable + "\\" + CurrentObject + ".dbe", FileMode.OpenOrCreate);
return this;
}
/// <summary>
/// devuelve true si el objeto existe en la base de datos, o false si no exist
/// </summary>
/// <param name="Object"></param>
/// <returns></returns>
public bool Exist(string Object)
{
return File.Exists(Constants.DatabaseFolder + CurrentTable + "\\" + Object + ".dbe");
}
/// <summary>
/// Escribe el campo 'field' a la variable 'value'
/// </summary>
/// <param name="value"></param>
/// <param name="field"></param>
public void Write(object value, string field)
{
int size;
FileMap.TryGetValue(field, out size);
lock (fs)
{
StringBuilder sb = new StringBuilder(value.ToString(), size);
sb.Append(NullChar, size - value.ToString().Length);
int offset;
if (FileMap.TryGetKey2(field, out offset))
{
byte[] writeBuffer = Encoding.ASCII.GetBytes(sb.ToString());
fs.Seek(offset, SeekOrigin.Begin);
fs.Write(writeBuffer, 0, size);
fs.Flush();
}
else
throw new Exception("El campo " + field + " no existe en la tabla " + CurrentTable);
}
}
private string MainRead(string field, int length)
{
lock (fs)
{
int offset;
FileMap.TryGetKey2(field, out offset);
StringBuilder sb = new StringBuilder(length);
fs.Seek(offset, SeekOrigin.Begin);
for (int i = 0; i < length; i++)
{
char e;
e = (char)fs.ReadByte();
if (e == NullChar)
break;
else
sb.Append(e);
}
return sb.ToString();
}
}
public bool ReadBool(string field)
{
return ReadByte(field) == 1;
}
public byte ReadByte(string field)
{
return byte.Parse(MainRead(field, VariableSize.Int8));
}
public sbyte ReadSByte(string field)
{
return sbyte.Parse(MainRead(field, VariableSize.Int8));
}
public short ReadInt16(string field)
{
return short.Parse(MainRead(field, VariableSize.Int16));
}
public ushort ReadUInt16(string field)
{
return ushort.Parse(MainRead(field, VariableSize.Int16));
}
public int ReadInt32(string field)
{
return int.Parse(MainRead(field, VariableSize.Int32));
}
public uint ReadUInt32(string field)
{
return uint.Parse(MainRead(field, VariableSize.Int32));
}
public long ReadInt64(string field)
{
return long.Parse(MainRead(field, VariableSize.Int64));
}
public ulong ReadUInt64(string field)
{
return ulong.Parse(MainRead(field, VariableSize.Int64));
}
/// <summary>
/// Lee un String de 30 caracteres
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
public string ReadSmallString(string field)
{
return MainRead(field, VariableSize.SmallString);
}
/// <summary>
/// Lee un string de 65 caracteres
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
public string ReadMediumString(string field)
{
return MainRead(field, VariableSize.MediumString);
}
/// <summary>
/// Lee un string de 100 caracteres
/// </summary>
/// <param name="field"></param>
/// <returns></returns>
public string BigString(string field)
{
return MainRead(field, VariableSize.BigString);
}
/// <summary>
/// Escribe los nombres de los campos y les asigna el valor nullChar('~').
/// </summary>
public void Create()
{
int length;
//int offset;
System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
sw.Start();
StringBuilder sb = new StringBuilder();
foreach (string field in FileMap.Keys1)
{
FileMap.TryGetValue(field, out length);
sb.Append(field + '=');
sb.Append(NullChar, length);
sb.Append(Environment.NewLine);
}
byte[] writeBuffer = Encoding.ASCII.GetBytes(sb.ToString());
fs.Write(writeBuffer, 0, writeBuffer.Length);
sw.Stop();
Output.Line(sw.ElapsedMilliseconds + "-" + sw.ElapsedTicks);
}
public void Dispose()
{
try
{
fs.Dispose();
fs.Close();
}
catch { }
}
}
tambien usa otra clase llamada Selector que son 3 ConcurrentDictionaries para tener valores que pueden ser obtenidos desde 2 claves distintas
public class Selector<TKey1, TKey2, TValue>
{
ConcurrentDictionary<TKey1, TValue> Key1ToValue;
ConcurrentDictionary<TKey2, TKey1> Key2ToKey1;
ConcurrentDictionary<TKey1, TKey2> Key1ToKey2;
public Selector()
{
Key1ToValue = new ConcurrentDictionary<TKey1, TValue>();
Key2ToKey1 = new ConcurrentDictionary<TKey2, TKey1>();
Key1ToKey2 = new ConcurrentDictionary<TKey1, TKey2>();
}
public bool TryGetValue(TKey1 key, out TValue value)
{
return Key1ToValue.TryGetValue(key, out value);
}
public bool TryGetValue(TKey2 key2, out TValue value)
{
TKey1 key1;
if (Key2ToKey1.TryGetValue(key2, out key1))
return Key1ToValue.TryGetValue(key1, out value);
value = default(TValue);
return false;
}
public bool TryGetKey1(TKey2 key2, out TKey1 value)
{
return Key2ToKey1.TryGetValue(key2, out value);
}
public bool TryGetKey2(TKey1 key1, out TKey2 value)
{
return Key1ToKey2.TryGetValue(key1, out value);
}
public bool TryAdd(TKey1 key1, TKey2 key2, TValue value)
{
return (Key1ToKey2.TryAdd(key1, key2) &&
Key1ToValue.TryAdd(key1, value) &&
Key2ToKey1.TryAdd(key2, key1));
}
public bool TryRemove(TKey1 key1)
{
TValue deletedval;
TKey1 deleted1;
TKey2 key2;
if (Key1ToKey2.TryRemove(key1, out key2))
return (Key2ToKey1.TryRemove(key2, out deleted1) &&
Key1ToValue.TryRemove(key1, out deletedval));
return false;
}
public bool ContainsKey1(TKey1 value)
{
return Key1ToValue.ContainsKey(value);
}
public bool ContainsKey2(TKey2 value)
{
return Key2ToKey1.ContainsKey(value);
}
public bool ContainsValue(TValue value)
{
return Key1ToValue.Values.Contains(value);
}
public ICollection<TKey1> Keys1
{
get { return Key1ToKey2.Keys; }
}
public ICollection<TKey2> Keys2
{
get { return Key2ToKey1.Keys; }
}
public ICollection<TValue> Values
{
get { return Key1ToValue.Values; }
}
}
Espero comentarios :)