博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于本地文件系统的LocalDB
阅读量:4668 次
发布时间:2019-06-09

本文共 13870 字,大约阅读时间需要 46 分钟。

零、前言

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

一、工具

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

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

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

  首先添加自定义的异常:

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

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

///     /// 文件数据库,这是一个抽象类。(之前做的拿来用的,也可以应用于xml,但已有linq to xml,所以这边暂时仅用于json)    ///     public abstract class FileDatabase
{ #region Fields ///
/// 文件数据库操作锁 /// protected static readonly object operationLock = new object(); private static HashSet
invalidFileNameChars; static FileDatabase() { invalidFileNameChars = new HashSet
() { '\0', ' ', '.', '$', '/', '\\' }; foreach (var c in Path.GetInvalidPathChars()) { invalidFileNameChars.Add(c); } foreach (var c in Path.GetInvalidFileNameChars()) { invalidFileNameChars.Add(c); } } ///
/// 文件数据库 /// ///
数据库文件所在目录 protected FileDatabase(string directory) { MyDirectory = directory; } public FileDatabase() { } #endregion #region Properties ///
/// 数据库文件所在目录 /// public virtual string MyDirectory { get; protected set; } ///
/// 文件扩展名 /// public virtual string FileExtension { get; set; } public virtual bool IsIndent { get; set; } #endregion #region Public Methods ///
/// 保存文档 /// ///
文档类型
///
文档ID ///
文档对象 ///
文档ID
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; } ///
/// 根据文档ID查找文档 /// ///
文档类型
///
文档ID ///
文档对象
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); } } ///
/// 查找指定类型的所有文档 /// ///
文档类型
///
文档对象序列
public virtual IEnumerable
FindAll() { try { List
list = new List
(); 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); } } ///
/// 根据指定文档ID删除文档 /// ///
文档类型
///
文档ID 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); } } ///
/// 删除所有指定类型的文档 /// ///
文档类型
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); } } ///
/// 获取指定类型文档的数量 /// ///
文档类型
///
文档的数量
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 ///
/// 生成文件全路径 /// ///
文档类型
///
文档ID ///
文件路径
protected virtual string GenerateFileFullPath(string id) { return Path.Combine(GenerateFilePath(), GenerateFileName(id)); } ///
/// 生成文件路径 /// ///
文档类型
///
文件路径
protected virtual string GenerateFilePath() { return Path.Combine(this.MyDirectory, typeof(TEntity).Name); } ///
/// 生成文件名 /// ///
文档类型
///
文档ID ///
文件名
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); } ///
/// 将指定的文档对象序列化至字符串 /// ///
指定的文档对象 ///
文档对象序列化后的字符串
protected abstract string Serialize(object value, bool isIndent = false); ///
/// 将字符串反序列化成文档对象 /// ///
文档类型
///
字符串 ///
文档对象
protected abstract TEntity Deserialize(string data); #endregion }
View Code

三、存储架构的设计

1、基础设施的构建

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

  首先,要先有实体基类:

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

  其次,要有仓储接口:

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

2、基于json仓储的实现

  json实体基类:

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

  json仓储接口:

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

  json仓储的具体实现:

///     /// json仓储    ///     public class JsonRepository
: FileDatabase
, IJsonRepository
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
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
(data); } }
View Code

四、工具类

  PublicHelper:

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

  JsonHelper:

public class JsonHelper    {        ///         /// 序列化json        ///         /// 对象        /// 是否缩进        /// 
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); } /// /// 将字符反序列化成对象 /// ///
泛型对象
/// ///
public static TEntity Deserialize
(string data) { if (string.IsNullOrEmpty(data)) { throw new ArgumentNullException("data is null or empty."); } return JsonConvert.DeserializeObject
(data); } }
View Code

五、运行测试

  测试代码:

class Program    {        static void Main(string[] args)        {            IJsonRepository
jsonRepository = new JsonRepository
(); 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 { ///
/// 学号 /// public int SNo { get; set; } ///
/// 名称 /// public string Name { get; set; } ///
/// 性别 /// public string Gender { get; set; } ///
/// 年龄 /// 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

  

 

 

六、结语

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

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

转载于:https://www.cnblogs.com/TwinkleStar/p/6871954.html

你可能感兴趣的文章
轻松学SQL Server数据库pdf
查看>>
Oracle 日期查询
查看>>
说说今年的计划
查看>>
把discuzX 的用户登录信息添加到纯静态页面
查看>>
文件大小计算
查看>>
jQuery多选列表框插件Multiselect
查看>>
iOS:给图片置灰色
查看>>
Java 8 (5) Stream 流 - 收集数据
查看>>
ubuntu下安装JDK
查看>>
【C#】使用DWM实现无边框窗体阴影或全透窗体
查看>>
【MySql】脚本备份数据库
查看>>
keil5 配置 stm32f103rc 软件仿真
查看>>
RESTful到底是什么玩意??
查看>>
Oracle创建视图的一个问题
查看>>
(一)线性表
查看>>
hdu 1003 Max Sum (DP)
查看>>
mysql增备
查看>>
[APIO2015]雅加达的摩天楼
查看>>
andorid之帧布局FrameLayout
查看>>
(转,记录用)jQuery页面加载初始化的3种方法
查看>>