零、前言

  之前写一些小工具的时候,需要用到数据存储方面的技术,但是用数据库又觉得太大了,本地文件存储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. Servlet过滤器和监听器知识总结(转)

    Servlet过滤器和监听器知识总结(转)  Servlet过滤器是 Servlet 程序的一种特殊用法,主要用来完成一些通用的操作,如编码的过滤.判断用户的登录状态.过滤器使得Servlet开发者能 ...

  2. Tcl与Design Compiler (十三)——Design Compliler中常用到的命令(示例)总结

    本文如果有错,欢迎留言更正:此外,转载请标明出处 http://www.cnblogs.com/IClearner/  ,作者:IC_learner 本文将描述在Design Compliler中常用 ...

  3. BinarySearch的一些注意事项

    BinarySearch原理比较简单,不过在处理实际问题的过程中需要注意几个小问题: 1. 找出有序数组中第一个为某特定值的数,以及没找到则返回-1 2. 找出有序数组中最后一个为某特定值的数,以及没 ...

  4. ArcGIS API for JavaScript 4.2学习笔记[20] 使用参数查询要素(油井和地震关系)

    这个例子相当复杂.我先简单说说这个例子是干啥的. 在UI上,提供了一个下拉框.两个滑动杆,以确定三个参数,使用这三个参数进行空间查询.这个例子就颇带空间查询的意思了. 这个例子是干嘛的呢?第一个参数是 ...

  5. 设计模式(三)—代理模式

    目录: 一.概述 二.静态代理 三.动态代理 四.静态代理和动态代理的区别 一.概述      代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对 ...

  6. Java基础学习(五)—Collection

    一.Collection概述 1.数组和集合的区别 (1)长度区别      数组长度是固定的,集合长度是可变的. (2)存储内容区别      数组只能存放同一种类型的元素(基本类型/引用类型). ...

  7. JavaScript学习总结(一)DOM文档对象模型

    一.文档(D) 一个网页运行在浏览器中,他就是一个文档对象. 二.对象(O) "对象"是一种自足的数据集合.与某个特定对象相关联的变量被称为这个对象的属性,只能通过某个对象调用的函 ...

  8. 事务隔离级别与传播机制,spring+mybatis+atomikos实现分布式事务管理

    1.事务的定义:事务是指多个操作单元组成的合集,多个单元操作是整体不可分割的,要么都操作不成功,要么都成功.其必须遵循四个原则(ACID). 原子性(Atomicity):即事务是不可分割的最小工作单 ...

  9. 谱聚类(Spectral clustering)(2):NCut

    作者:桂. 时间:2017-04-13  21:19:41 链接:http://www.cnblogs.com/xingshansi/p/6706400.html 声明:欢迎被转载,不过记得注明出处哦 ...

  10. 线段树 poj 3468

    Description You have N integers, A1, A2, ... ,AN. You need to deal with two kinds of operations. One ...