• Sábado 20 de Abril de 2024, 06:41

Autor Tema:  Base de datos en base a FileStreams  (Leído 1345 veces)

U2_Caparzo

  • Miembro activo
  • **
  • Mensajes: 45
  • Nacionalidad: cl
  • Super duper divertido xDD
    • Ver Perfil
Base de datos en base a FileStreams
« en: Jueves 14 de Febrero de 2013, 04:57 »
0
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
Código: [Seleccionar]
    //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
Código: [Seleccionar]
/// <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
Código: [Seleccionar]
   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

Código: [Seleccionar]
    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 :)
« última modificación: Jueves 14 de Febrero de 2013, 05:00 por U2_Caparzo »
Lo dificil se hace... lo imposible se intenta
Todos somos muy ignorantes. Lo que ocurre es que no todos ignoramos las mismas cosas.(Frase de Albert Einstein)