零、前言

  之前写一些小工具的时候,需要用到数据存储方面的技术,但是用数据库又觉得太大了,本地文件存储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)
{ }
}

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

    /// <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>() { '\0', ' ', '.', '$', '/', '\\' };
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 ;
}
}
return ;
}
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
}

三、存储架构的设计

1、基础设施的构建

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

  首先,要先有实体基类:

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

  其次,要有仓储接口:

    /// <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);
}

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; }
}
}

  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();
}

  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 ;
} public TEntity Find(params object[] id)
{
return FindOneById(id[].ToString());
} public IQueryable<TEntity> GetModel()
{
return FindAll().AsQueryable();
} public int Insert(TEntity item)
{
Save(item.RootID, item); return ;
} public int Update(TEntity item)
{
Save(item.RootID, item); return ;
} 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);
}
}

四、工具类

  PublicHelper:

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

  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);
}
}

五、运行测试

  测试代码:

    class Program
{
static void Main(string[] args)
{
IJsonRepository<Student> jsonRepository = new JsonRepository<Student>(); jsonRepository.Insert(new Student()
{
SNo = ,
Name = "张三",
Gender = "女",
Age = ,
}); jsonRepository.Insert(new Student()
{
SNo = ,
Name = "李四",
Gender = "女",
Age = ,
}); jsonRepository.Insert(new Student()
{
SNo = ,
Name = "王二",
Gender = "女",
Age = ,
}); 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);
}
}

  

六、结语

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

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

基于本地文件系统的LocalDB的更多相关文章

  1. 阿里云oss挂载到linux本地文件系统

    对象存储 OSS 阿里云对象存储服务 (OSS) 是一种高度可伸缩且安全可靠的云对象存储服务,让您可以存储.备份和归档大量数据.阿里云 OSS 是一种简单易用的服务,让您每秒能处理数百万请求,它还支持 ...

  2. HBase、HDFS与本地文件系统之间的关系

    一.文件系统 1. 概念 所谓文件系统,是操作系统用于明确磁盘或分区上的文件的方法和数据结构:即在磁盘上组织文件的方法.也指用于存储文件的磁盘或分区,或文件系统种类. 2. Local File Sy ...

  3. 基于本地存储的kvm虚拟机在线迁移

    基于本地存储的kvm虚拟机在线迁移 kvm虚拟机迁移分为4种(1)热迁移基于共享存储(2)热迁移基于本地存储(3)冷迁移基于共享存储(4)冷迁移基于本地存储 这里介绍的是基于本地存储的热迁移 动态块迁 ...

  4. OSSFS将OSS bucket 挂载到本地文件系统及注意事项

    OSSFS将OSS bucket 挂载到本地文件系统及注意事项 下载ossfs安装包 wget http://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/as ...

  5. 解决从linux本地文件系统上传文件到HDFS时的权限问题

    当使用 hadoop fs -put localfile /user/xxx 时提示: put: Permission denied: user=root, access=WRITE, inode=& ...

  6. HTML5之本地文件系统API - File System API

    HTML5之本地文件系统API - File System API 新的HTML5标准给我们带来了大量的新特性和惊喜,例如,画图的画布Canvas,多媒体的audio和video等等.除了上面我们提到 ...

  7. QTreeWidget实现动态加载本地文件系统

    QT之前没有接触过,之所以做这个也是被临时拉去GoldenFarm组去做渲染的客户端:还别说,虽说是第一次,做出来的这个东西倒是挺让我满意的.先说一下具体需求,然后再上图吧: 渲染时在选择场景文件时, ...

  8. React Native之本地文件系统访问组件react-native-fs的介绍与使用

    React Native之本地文件系统访问组件react-native-fs的介绍与使用 一,需求分析 1,需要将图片保存到本地相册: 2,需要创建文件,并对其进行读写 删除操作. 二,简单介绍 re ...

  9. 解决从本地文件系统上传到HDFS时的权限问题

    当使用 hadoop fs -put localfile /user/xxx 时提示: put: Permission denied: user=root, access=WRITE, inode=& ...

随机推荐

  1. 简单的叙述下SQL中行列转换的小知识!

    行列转换对于工作还是学习中总是不可避免的会遇到(虽然本人还尚未工作,萌萌哒的学生一枚),解决的方法也有很多,我这里就总结一下我所想解决的问题以及怎么去解决的方法, 可能网上已经有很多类似的方法了,有的 ...

  2. jq轮播图插件

    /* * 使用说明  *    *   1:需要提供一个标签   *   2:lis:图片的个数 *   3:轮播图的大小 width ,height *   4:图片的地址imgs[0].carou ...

  3. python webdriver安装

    前言 本次就python webdriver的安装和驱动不同浏览器的配置进行分享,以解决大家在入门过程中的一些基本的环境问题. python安装 目前python有2.x和3.x版本,笔者在这里推荐2 ...

  4. lumen 中的 .env 配置文件简介和适用场景

    lumen 是 laravel 的衍生品,核心功能的使用和 laravel 都是一致的,但配置文件这一方面,lumen 在 laravel 的基础上增加了更简便的配置方式: lumen 采用了 Dot ...

  5. 常用linux命令及其设置

    完成一个运维的工作,以下的命令和配置是经常会用到的,总结一下工作以来的命令和配置 linux常用命令 linux客户端挂接(mount)其他linux系统或UNIX系统的NFS共享 $ mkdir – ...

  6. Service详解

    /** * 后台执行的定时任务 */ public class LongRunningService extends Service { @Override public IBinder onBind ...

  7. 在阿里云Linux服务器上安装MySQL

    申请阿里云Linux服务器 昨天在阿里云申请了一个免费试用5天的Linux云服务器. 操作系统:Red Hat Enterprise Linux Server 5.4 64位. CPU:1核 内存:5 ...

  8. 哥哥我刚学的java---------数组插入法------------

    package charufa; public class Test02 { // 插入法(插入一个数字). public static void main(String[] args) { int[ ...

  9. ios app网址说明

    URLforBrowser = 'https://itunes.apple.com/cn/app/wan-huo-shi-shang-zhu-li-ren/id1077705***'; URLfori ...

  10. Elasticsearch搜索之explain评分分析

    Lucene的IndexSearcher提供一个explain方法,能够解释Document的Score是怎么得来的,具体每一部分的得分都可以详细地打印出来.这里用一个中文实例来纯手工验算一遍Luce ...