基于本地文件系统的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)
{ }
}
然后是具体操作文件的代码(这块有参考别人的,仅供学习使用,如有侵权,请联系我):
/// <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的更多相关文章
- 阿里云oss挂载到linux本地文件系统
对象存储 OSS 阿里云对象存储服务 (OSS) 是一种高度可伸缩且安全可靠的云对象存储服务,让您可以存储.备份和归档大量数据.阿里云 OSS 是一种简单易用的服务,让您每秒能处理数百万请求,它还支持 ...
- HBase、HDFS与本地文件系统之间的关系
一.文件系统 1. 概念 所谓文件系统,是操作系统用于明确磁盘或分区上的文件的方法和数据结构:即在磁盘上组织文件的方法.也指用于存储文件的磁盘或分区,或文件系统种类. 2. Local File Sy ...
- 基于本地存储的kvm虚拟机在线迁移
基于本地存储的kvm虚拟机在线迁移 kvm虚拟机迁移分为4种(1)热迁移基于共享存储(2)热迁移基于本地存储(3)冷迁移基于共享存储(4)冷迁移基于本地存储 这里介绍的是基于本地存储的热迁移 动态块迁 ...
- OSSFS将OSS bucket 挂载到本地文件系统及注意事项
OSSFS将OSS bucket 挂载到本地文件系统及注意事项 下载ossfs安装包 wget http://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/as ...
- 解决从linux本地文件系统上传文件到HDFS时的权限问题
当使用 hadoop fs -put localfile /user/xxx 时提示: put: Permission denied: user=root, access=WRITE, inode=& ...
- HTML5之本地文件系统API - File System API
HTML5之本地文件系统API - File System API 新的HTML5标准给我们带来了大量的新特性和惊喜,例如,画图的画布Canvas,多媒体的audio和video等等.除了上面我们提到 ...
- QTreeWidget实现动态加载本地文件系统
QT之前没有接触过,之所以做这个也是被临时拉去GoldenFarm组去做渲染的客户端:还别说,虽说是第一次,做出来的这个东西倒是挺让我满意的.先说一下具体需求,然后再上图吧: 渲染时在选择场景文件时, ...
- React Native之本地文件系统访问组件react-native-fs的介绍与使用
React Native之本地文件系统访问组件react-native-fs的介绍与使用 一,需求分析 1,需要将图片保存到本地相册: 2,需要创建文件,并对其进行读写 删除操作. 二,简单介绍 re ...
- 解决从本地文件系统上传到HDFS时的权限问题
当使用 hadoop fs -put localfile /user/xxx 时提示: put: Permission denied: user=root, access=WRITE, inode=& ...
随机推荐
- Python之路-Linux命令基础(2)
作业一: 1) 新建用户natasha,uid为1000,gid为555,备注信息为"master" 2) 修改natasha用户的家目录为/Natasha 3) ...
- C#研究OpenXML之路(3-OpenXMLSDKToolV25.msi)
一.OpenXMLSDKToolV25.msi 看了几天的OpenXml,感觉如果完全手写代码,将会是一件非常苦逼的事情,即要分析对应xlsx文件层次结构,以及包含的xml文件的xml标签结构,还要关 ...
- number问题
Missing Number Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one ...
- 采用Spring AOP+Log4j记录项目日志
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6567672.html 项目日志记录是项目开发.运营必不可少的内容,有了它可以对系统有整体的把控,出现任何问题 ...
- JDBC基础学习(五)—批处理插入数据
一.批处理介绍 当需要成批插入或者更新记录时.可以采用Java的批量更新机制,这一机制允许多条语句一次性提交给数据库批量处理.通常情况下比单独提交处理更有效率. JDBC的批量处理语句包括下 ...
- myeclipse2015复制项目需要修改的地方
项目下 D:\Workspaces\MyEclipse 2015\angular001\.settings 的org.eclipse.wst.common.component文件,修改里面未原来的 ...
- JS和CSS中引号的使用
font-family属性值如果是英文可以不加引号,如果是中文按照CSS标准则应该加引号,但不加引号也没关系.比如:font-family:Arial,"宋体"," ...
- struts2 之 ThreadLocal 和 ActionContext
1. ThreadLocal:该类提供了线程局部(thtead-local)变量.threadLocal是一个容器,该容器中存放的数据可以保证线程安全. 案例如: public class Threa ...
- 第一章 自定义MVC框架
第一章 自定义MVC框架1.1 MVC模式设计 组成:Model:模型,用于数据和业务的处理 View :视图,用于数据的显示 Controller:控制器 ...
- 纯css兼容个浏览器input[type='radio']不能自定义样式
各个浏览器对于表单input[type='radio'].input[type='checkbox']的样式总是各有差异 //html <div class="remember-a ...