zoukankan      html  css  js  c++  java
  • 基于本地文件系统的LocalDB

    零、前言

      之前写一些小工具的时候,需要用到数据存储方面的技术,但是用数据库又觉得太大了,本地文件存储txt文件存储又不是很规范,于是乎想到了去编写一个简单的基于本地文件系统的数据存储库,暂且叫它localdb吧,总之也是为了方便使用,特别是数据量不大,但是又特别想要本地数据存储的。(抛开access不说)

    一、工具

      工欲善其事必先利其器,存储数据必然要有一个标准的数据格式,首先让我想到的就是json,xml,原因不必多说。所以下面编写的数据主要以json作为存储格式。json操作用Newtonjson,操作方便简单。

    二、文件系统的操作(LocalFileDB)

      本地数据库,必不可少对本地文件进行操作,所以对本地文件的读取创建统一管理,话不多说,直接上代码。

      首先添加自定义的异常:

        /// <summary>
        /// 文件数据库异常
        /// </summary>
        public class FileDatabaseException : Exception
        {
            public FileDatabaseException(string message)
                : base(message)
            { }
    
            public FileDatabaseException(string message, Exception innerException)
                : base(message, innerException)
            { }
        }
    View Code

      然后是具体操作文件的代码(这块有参考别人的,仅供学习使用,如有侵权,请联系我):

        /// <summary>
        /// 文件数据库,这是一个抽象类。(之前做的拿来用的,也可以应用于xml,但已有linq to xml,所以这边暂时仅用于json)
        /// </summary>
        public abstract class FileDatabase<TEntity>
        {
            #region Fields
    
            /// <summary>
            /// 文件数据库操作锁
            /// </summary>
            protected static readonly object operationLock = new object();
            private static HashSet<char> invalidFileNameChars;
    
            static FileDatabase()
            {
                invalidFileNameChars = new HashSet<char>() { '', ' ', '.', '$', '/', '\' };
                foreach (var c in Path.GetInvalidPathChars()) { invalidFileNameChars.Add(c); }
                foreach (var c in Path.GetInvalidFileNameChars()) { invalidFileNameChars.Add(c); }
            }
    
            /// <summary>
            /// 文件数据库
            /// </summary>
            /// <param name="directory">数据库文件所在目录</param>
            protected FileDatabase(string directory)
            {
                MyDirectory = directory;
            }
    
            public FileDatabase()
            {
    
            }
    
            #endregion
    
            #region Properties
    
            /// <summary>
            /// 数据库文件所在目录
            /// </summary>
            public virtual string MyDirectory { get; protected set; }
    
            /// <summary>
            /// 文件扩展名
            /// </summary>
            public virtual string FileExtension { get; set; }
    
            public virtual bool IsIndent { get; set; }
    
            #endregion
    
            #region Public Methods
    
            /// <summary>
            /// 保存文档
            /// </summary>
            /// <typeparam name="TEntity">文档类型</typeparam>
            /// <param name="id">文档ID</param>
            /// <param name="document">文档对象</param>
            /// <returns>文档ID</returns>
            public virtual string Save(string id, TEntity document)
            {
                if (string.IsNullOrEmpty(id))
                    throw new ArgumentNullException("id");
    
                if (document == null)
                    throw new ArgumentNullException("document");
    
                Delete(id);
    
                try
                {
                    string fileName = GenerateFileFullPath(id);
                    string output = Serialize(document, IsIndent);
    
                    lock (operationLock)
                    {
                        System.IO.FileInfo info = new System.IO.FileInfo(fileName);
                        System.IO.Directory.CreateDirectory(info.Directory.FullName);
                        System.IO.File.WriteAllText(fileName, output);
                    }
                }
                catch (Exception ex)
                {
                    throw new FileDatabaseException(
                      string.Format(CultureInfo.InvariantCulture,
                      "Save document failed with id [{0}].", id), ex);
                }
    
                return id;
            }
    
            /// <summary>
            /// 根据文档ID查找文档
            /// </summary>
            /// <typeparam name="TEntity">文档类型</typeparam>
            /// <param name="id">文档ID</param>
            /// <returns>文档对象</returns>
            public virtual TEntity FindOneById(string id)
            {
                if (string.IsNullOrEmpty(id))
                    throw new ArgumentNullException("id");
    
                try
                {
                    string fileName = GenerateFileFullPath(id);
                    if (File.Exists(fileName))
                    {
                        string fileData = File.ReadAllText(fileName);
                        return Deserialize(fileData);
                    }
    
                    return default(TEntity);
                }
                catch (Exception ex)
                {
                    throw new FileDatabaseException(
                      string.Format(CultureInfo.InvariantCulture,
                      "Find document by id [{0}] failed.", id), ex);
                }
            }
    
            /// <summary>
            /// 查找指定类型的所有文档
            /// </summary>
            /// <typeparam name="TEntity">文档类型</typeparam>
            /// <returns>文档对象序列</returns>
            public virtual IEnumerable<TEntity> FindAll()
            {
                try
                {
                    List<TEntity> list = new List<TEntity>();
    
                    if (Directory.Exists(GenerateFilePath()))
                    {
                        string[] files = System.IO.Directory.GetFiles(
                          GenerateFilePath(),
                          "*." + FileExtension,
                          SearchOption.TopDirectoryOnly);
    
                        foreach (string fileName in files)
                        {
                            string fileData = File.ReadAllText(fileName);
                            TEntity document = Deserialize(fileData);
                            if (document != null)
                            {
                                list.Add(document);
                            }
                        }
                    }
    
                    return list;
                }
                catch (Exception ex)
                {
                    throw new FileDatabaseException(
                      "Find all documents failed.", ex);
                }
            }
    
            /// <summary>
            /// 根据指定文档ID删除文档
            /// </summary>
            /// <typeparam name="TEntity">文档类型</typeparam>
            /// <param name="id">文档ID</param>
            public virtual void Delete(string id)
            {
                if (string.IsNullOrEmpty(id))
                    throw new ArgumentNullException("id");
    
                try
                {
                    string fileName = GenerateFileFullPath(id);
                    if (File.Exists(fileName))
                    {
                        lock (operationLock)
                        {
                            File.Delete(fileName);
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new FileDatabaseException(
                      string.Format(CultureInfo.InvariantCulture,
                      "Delete document by id [{0}] failed.", id), ex);
                }
            }
    
            /// <summary>
            /// 删除所有指定类型的文档
            /// </summary>
            /// <typeparam name="TEntity">文档类型</typeparam>
            public virtual void DeleteAll()
            {
                try
                {
                    if (Directory.Exists(GenerateFilePath()))
                    {
                        string[] files = System.IO.Directory.GetFiles(
                          GenerateFilePath(), "*." + FileExtension,
                          SearchOption.TopDirectoryOnly);
    
                        foreach (string fileName in files)
                        {
                            lock (operationLock)
                            {
                                File.Delete(fileName);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new FileDatabaseException(
                      "Delete all documents failed.", ex);
                }
            }
    
            /// <summary>
            /// 获取指定类型文档的数量
            /// </summary>
            /// <typeparam name="TEntity">文档类型</typeparam>
            /// <returns>文档的数量</returns>
            public virtual int Count()
            {
                try
                {
                    if (Directory.Exists(GenerateFilePath()))
                    {
                        string[] files = System.IO.Directory.GetFiles(
                          GenerateFilePath(),
                          "*." + FileExtension, SearchOption.TopDirectoryOnly);
                        if (files != null)
                        {
                            return files.Length;
                        }
                        else
                        {
                            return 0;
                        }
                    }
                    return 0;
                }
                catch (Exception ex)
                {
                    throw new FileDatabaseException(
                      "Count all documents failed.", ex);
                }
            }
    
            #endregion
    
            #region Protected Methods
    
            /// <summary>
            /// 生成文件全路径
            /// </summary>
            /// <typeparam name="TEntity">文档类型</typeparam>
            /// <param name="id">文档ID</param>
            /// <returns>文件路径</returns>
            protected virtual string GenerateFileFullPath(string id)
            {
                return Path.Combine(GenerateFilePath(),
                  GenerateFileName(id));
            }
    
            /// <summary>
            /// 生成文件路径
            /// </summary>
            /// <typeparam name="TEntity">文档类型</typeparam>
            /// <returns>文件路径</returns>
            protected virtual string GenerateFilePath()
            {
                return Path.Combine(this.MyDirectory, typeof(TEntity).Name);
            }
    
            /// <summary>
            /// 生成文件名
            /// </summary>
            /// <typeparam name="TEntity">文档类型</typeparam>
            /// <param name="id">文档ID</param>
            /// <returns>文件名</returns>
            protected virtual string GenerateFileName(string id)
            {
                if (string.IsNullOrEmpty(id))
                    throw new ArgumentNullException("id");
    
                foreach (char c in id)
                {
                    if (invalidFileNameChars.Contains(c))
                    {
                        throw new FileDatabaseException(
                          string.Format(CultureInfo.InvariantCulture,
                          "The character '{0}' is not a valid file name identifier.", c));
                    }
                }
    
                return string.Format(CultureInfo.InvariantCulture, "{0}.{1}", id, FileExtension);
            }
    
            /// <summary>
            /// 将指定的文档对象序列化至字符串
            /// </summary>
            /// <param name="value">指定的文档对象</param>
            /// <returns>文档对象序列化后的字符串</returns>
            protected abstract string Serialize(object value, bool isIndent = false);
    
            /// <summary>
            /// 将字符串反序列化成文档对象
            /// </summary>
            /// <typeparam name="TEntity">文档类型</typeparam>
            /// <param name="data">字符串</param>
            /// <returns>文档对象</returns>
            protected abstract TEntity Deserialize(string data);
    
            #endregion
        }
    View Code

    三、存储架构的设计

    1、基础设施的构建

      数据库采用的是类似仓储的设计,这样对数据的管理比较直观,并且易用。什么是仓储,园内有很多博客有阐述,在此就不赘述了。

      首先,要先有实体基类:

        /// <summary>
        ///     可持久到数据库的领域模型的基类。
        /// </summary>
        [Serializable]
        public abstract class EntityBase
        {
            #region 构造函数
    
            /// <summary>
            ///     数据实体基类
            /// </summary>
            protected EntityBase()
            {
            }
    
            #endregion
        }
    View Code

      其次,要有仓储接口:

        /// <summary>
        /// 数据基础操作规范
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        public interface IRepository<TEntity> 
            where TEntity : EntityBase
        {
            /// <summary>
            /// 添加实体并提交到数据服务器
            /// </summary>
            /// <param name="item">需要添加数据项</param>
            /// <returns>受影响条数</returns>
            int Insert(TEntity item);
    
            /// <summary>
            /// 移除实体并提交到数据服务器
            /// 如果表存在约束,需要先删除子表信息
            /// </summary>
            /// <param name="item">需要删除的数据项</param>
            /// <returns>受影响条数</returns>
            int Delete(TEntity item);
    
            /// <summary>
            /// 修改实体并提交到数据服务器
            /// </summary>
            /// <param name="item">需要修改的数据项</param>
            /// <returns>受影响条数</returns>
            int Update(TEntity item);
    
            /// <summary>
            /// 得到指定的实体集合(延时结果集)
            /// </summary>
            /// <returns>实体集合</returns>
            IQueryable<TEntity> GetModel();
    
            /// <summary>
            /// 根据主键得到实体
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            TEntity Find(params object[] id);
        }
    View Code

    2、基于json仓储的实现

      json实体基类:

        /// <summary>
        /// Json实体基类
        /// </summary>
        public abstract class JsonEntity : EntityBase
        {
            private string id = PublicHelper.GuidGenerator();
            /// <summary>
            /// Json实体主键
            /// </summary>
            public string RootID
            {
                get { return id; }
                set { id = value; }
            }
        }
    View Code

      json仓储接口:

        /// <summary>
        /// Json仓储接口
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        public interface IJsonRepository<TEntity>
            : IRepository<TEntity>
            where TEntity : JsonEntity
        {
            /// <summary>
            /// 是否缩进
            /// </summary>
            bool IsIndent { get; set; }
            /// <summary>
            /// 按文件存储,直接删除所有文件
            /// </summary>
            void DeleteAll();
            int Count();
        }
    View Code

      json仓储的具体实现:

        /// <summary>
        /// json仓储
        /// </summary>
        public class JsonRepository<TEntity> :
            FileDatabase<TEntity>, IJsonRepository<TEntity>
            where TEntity : JsonEntity,new()
        {
            string m_Directory = AppDomain.CurrentDomain.BaseDirectory;
    
            public new bool IsIndent
            {
                get
                {
                    return base.IsIndent;
                }
    
                set
                {
                    base.IsIndent = value;
                }
            }
    
            public JsonRepository(string directory)
                :base(directory)
            {
                FileExtension = @"db";
                this.m_Directory = directory;
            }
    
            public JsonRepository()
            {
                FileExtension = @"db";
                MyDirectory = m_Directory;
            }
    
            public int Delete(TEntity item)
            {
                Delete(item.RootID);
    
                return 1;
            }
    
            public TEntity Find(params object[] id)
            {
                return FindOneById(id[0].ToString());
            }
    
            public IQueryable<TEntity> GetModel()
            {
                return FindAll().AsQueryable();
            }
    
            public int Insert(TEntity item)
            {
                Save(item.RootID, item);
    
                return 1;
            }
    
            public int Update(TEntity item)
            {
                Save(item.RootID, item);
    
                return 1;
            }
    
            protected override string Serialize(object value, bool isIndent = false)
            {
                return JsonHelper.Serialize(value, isIndent);
            }
    
            protected override TEntity Deserialize(string data)
            {
                return JsonHelper.Deserialize<TEntity>(data);
            }
        }
    View Code

    四、工具类

      PublicHelper:

        /// <summary>
        ///     公共辅助操作类
        /// </summary>
        public static class PublicHelper
        {
            #region 公共方法
    
            /// <summary>
            /// Guid生成器
            /// </summary>
            /// <returns></returns>
            public static string GuidGenerator()
            {
                return Guid.NewGuid().ToString("N");
            }
            #endregion
        }
    View Code

      JsonHelper:

        public class JsonHelper
        {
            /// <summary>
            /// 序列化json
            /// </summary>
            /// <param name="value">对象</param>
            /// <param name="isIndented">是否缩进</param>
            /// <returns></returns>
            public static string Serialize(object value, bool isIndented)
            {
                if (value == null)
                {
                    throw new ArgumentNullException("value is null.");
                }
    
                return JsonConvert.SerializeObject(value, isIndented ? Formatting.Indented : Formatting.None);
            }
    
            /// <summary>
            /// 将字符反序列化成对象
            /// </summary>
            /// <typeparam name="TEntity">泛型对象</typeparam>
            /// <param name="data"></param>
            /// <returns></returns>
            public static TEntity Deserialize<TEntity>(string data)
            {
                if (string.IsNullOrEmpty(data))
                {
                    throw new ArgumentNullException("data is null or empty.");
                }
    
                return JsonConvert.DeserializeObject<TEntity>(data);
            }
        }
    View Code

    五、运行测试

      测试代码:

        class Program
        {
            static void Main(string[] args)
            {
                IJsonRepository<Student> jsonRepository = new JsonRepository<Student>();
    
                jsonRepository.Insert(new Student()
                {
                    SNo = 1,
                    Name = "张三",
                    Gender = "",
                    Age = 18,
                });
    
                jsonRepository.Insert(new Student()
                {
                    SNo = 2,
                    Name = "李四",
                    Gender = "",
                    Age = 18,
                });
    
                jsonRepository.Insert(new Student()
                {
                    SNo = 3,
                    Name = "王二",
                    Gender = "",
                    Age = 18,
                });
    
                var entities = jsonRepository.GetModel();
    
                foreach (var item in entities)
                {
                    Console.WriteLine(item.ToString());
                }
    
                Console.ReadKey();
            }
        }
    
        public class Student:JsonEntity
        {
            /// <summary>
            /// 学号
            /// </summary>
            public int SNo { get; set; }
            /// <summary>
            /// 名称
            /// </summary>
            public string Name { get; set; }
            /// <summary>
            /// 性别
            /// </summary>
            public string Gender { get; set; }
            /// <summary>
            /// 年龄
            /// </summary>
            public int Age { get; set; }
    
            public override string ToString()
            {
                return string.Format("学号:{0};姓名:{1};性别:{2};年龄:{3}", this.SNo, this.Name, this.Gender, this.Age);
            }
        }
    View Code

      

     

    六、结语

      整个本地文件系统数据就完成了,系统可以扩展为其它格式来进行数据存储,只需要扩展仓储就可以了,操作也是简便,性能上的话,本地数据库自然是比不上那些专用数据库的,但是满足日常肯定是没有任何问题的。代码比较完整,我就不上传代码了。

    注:如有转载请标注出处。

  • 相关阅读:
    新思路,坚持创新;好想法,坚持执行
    新的一年,新的梦想
    新的一年,新的梦想
    大秦帝国-《治秦九论》
    大秦帝国-《治秦九论》
    大学生应当趁早谋划未来(二)--给表弟的建议
    大学生应当趁早谋划未来(二)--给表弟的建议
    解读OpenRTB(实时竞价)生态系统
    Java实现蓝桥杯VIP 算法训练 阶乘末尾
    Java实现 蓝桥杯VIP 算法训练 sign函数
  • 原文地址:https://www.cnblogs.com/TwinkleStar/p/6871954.html
Copyright © 2011-2022 走看看