SharpFileDB - a file database for small apps

本文中文版在此处

I'm not an expert of database. Please feel free to corect my mistakes.

This article (http://www.cnblogs.com/gaochundong/archive/2013/04/24/csharp_file_database.html) helpes a lot. Thank you!

目标(Goal)

I've decided to write a micro database library that spports 10 thousand level applications.

It's better not to rely on other drives or tools in case of high difficulty in deployment. After all, it's a small database for a small application.

It's a DLL from totally C# codes. It's easy for reading and using.

It supports CRUD of course.

It doesn't use any SQL. I'm not familiar with SQL and I don't like it. And it's not necessary to use SQL here.

It saves data via plain text files or binary files. It's convenient to use plain text files when developing and debugging codes. And it's safer to use binary files at deployment time.

So, simply, it's a micro database library that uses no SQL, files as storage form and totally C# to implement a CRUD system. Let's name the library SharpFileDB.

I've put the newest project on Github. All codes in the library are noted in both chinese and engilsh. I hope that will help to communicate.

设计草图(sketch)

使用场景(User Scene)

The typical user scene of SharpFileDB is as follows.

                 // common cases to use SharpFileDB.
FileDBContext db = new FileDBContext(); Cat cat = new Cat();
cat.Name = "xiao xiao bai";
db.Create(cat); Predicate<Cat> pre = new Predicate<Cat>(x => x.Name == "xiao xiao bai");
IList<Cat> cats = db.Retrieve(pre); cat.Name = "xiao bai";
db.Update(cat); db.Delete(cat);

This routine contains lines that create a database and uses CRUD operations.

Let's start the first version of SharpFileDB according to this user scene.

表vs类型(Table vs Type)

Let's take the type 'Cat' as an example.

     /// <summary>
/// demo file object
/// </summary>
public class Cat : FileObject
{
public string Name { get; set; }
public int Legs { get; set; } public override string ToString()
{
return string.Format("{0}, Name: {1}, Legs: {2}", base.ToString(), Name, Legs);
}
}

The type 'Cat' is equivalent to a 'Table' in a relational database.

An instance of 'Cat' is equivalent to a record of a 'Table'.

Lets' call types like 'Cat' a table-type.

全局唯一的主键(global unique main key)

We need a global unique main key for every instance of a table-type to diffentiate them. So let's do this in an abstract class.

     /// <summary>
/// 可在文件数据库中使用CRUD操作的所有类型的基类。
/// Base class for all classed that can use CRUD in SharpFileDB.
/// </summary>
[Serializable]
public abstract class FileObject
{
/// <summary>
/// 主键.
/// main key.
/// </summary>
public Guid Id { get; set; } /// <summary>
/// 创建一个文件对象,并自动为其生成一个全局唯一的Id。
/// <para>Create a <see cref="FileObject"/> and generate a global unique id for it.</para>
/// </summary>
public FileObject()
{
this.Id = Guid.NewGuid();
} public override string ToString()
{
return string.Format("Id: {0}", this.Id);
}
}

数据库(FileDBContext)

All table-types' CRUD operations are done in a FileDBContext.

     /// <summary>
/// 文件数据库。
/// Represents a file database.
/// </summary>
public class FileDBContext
{
#region Fields /// <summary>
/// 文件数据库操作锁
/// <para>database operation lock.</para>
/// </summary>
protected static readonly object operationLock = new object(); /// <summary>
/// 文件数据库
/// <para>Represents a file database.</para>
/// </summary>
/// <param name="directory">数据库文件所在目录<para>Directory for all files of database.</para></param>
public FileDBContext(string directory = null)
{
if (directory == null)
{
this.Directory = Environment.CurrentDirectory;
}
else
{
Directory = directory;
}
} #endregion public override string ToString()
{
return string.Format("@: {0}", Directory);
} #region Properties /// <summary>
/// 数据库文件所在目录
/// <para>Directory of database files.</para>
/// </summary>
public virtual string Directory { get; protected set; } #endregion protected string Serialize(FileObject item)
{
using (StringWriterWithEncoding sw = new StringWriterWithEncoding(Encoding.UTF8))
{
XmlSerializer serializer = new XmlSerializer(item.GetType());
serializer.Serialize(sw, item);
string serializedString = sw.ToString(); return serializedString;
}
} /// <summary>
/// 将字符串反序列化成文档对象
/// </summary>
/// <typeparam name="TDocument">文档类型</typeparam>
/// <param name="serializedFileObject">字符串</param>
/// <returns>
/// 文档对象
/// </returns>
protected TFileObject Deserialize<TFileObject>(string serializedFileObject)
where TFileObject : FileObject
{
if (string.IsNullOrEmpty(serializedFileObject))
throw new ArgumentNullException("data"); using (StringReader sr = new StringReader(serializedFileObject))
{
XmlSerializer serializer = new XmlSerializer(typeof(TFileObject));
object deserializedObj = serializer.Deserialize(sr);
TFileObject fileObject = deserializedObj as TFileObject;
return fileObject;
}
} protected string GenerateFileFullPath(FileObject item)
{
string path = GenerateFilePath(item.GetType());
string name = item.GenerateFileName();
string fullname = Path.Combine(path, name);
return fullname;
} /// <summary>
/// 生成文件路径
/// </summary>
/// <typeparam name="TDocument">文档类型</typeparam>
/// <returns>文件路径</returns>
protected string GenerateFilePath(Type type)
{
string path = Path.Combine(this.Directory, type.Name);
return path;
} #region CRUD /// <summary>
/// 增加一个<see cref="FileObject"/>到数据库。这实际上创建了一个文件。
/// <para>Create a new <see cref="FileObject"/> into database. This operation will create a new file.</para>
/// </summary>
/// <param name="item"></param>
public virtual void Create(FileObject item)
{
string fileName = GenerateFileFullPath(item);
string output = Serialize(item); lock (operationLock)
{
System.IO.FileInfo info = new System.IO.FileInfo(fileName);
System.IO.Directory.CreateDirectory(info.Directory.FullName);
System.IO.File.WriteAllText(fileName, output);
}
} /// <summary>
/// 检索符合给定条件的所有<paramref name="TFileObject"/>。
/// <para>Retrives all <paramref name="TFileObject"/> that satisfies the specified condition.</para>
/// </summary>
/// <typeparam name="TFileObject"></typeparam>
/// <param name="predicate">检索出的对象应满足的条件。<para>THe condition that should be satisfied by retrived object.</para></param>
/// <returns></returns>
public virtual IList<TFileObject> Retrieve<TFileObject>(Predicate<TFileObject> predicate)
where TFileObject : FileObject
{
IList<TFileObject> result = new List<TFileObject>();
if (predicate != null)
{
string path = GenerateFilePath(typeof(TFileObject));
string[] files = System.IO.Directory.GetFiles(path, "*.xml", SearchOption.AllDirectories);
foreach (var item in files)
{
string fileContent = File.ReadAllText(item);
TFileObject deserializedFileObject = Deserialize<TFileObject>(fileContent);
if (predicate(deserializedFileObject))
{
result.Add(deserializedFileObject);
}
}
} return result;
} /// <summary>
/// 更新给定的对象。
/// <para>Update specified <paramref name="FileObject"/>.</para>
/// </summary>
/// <param name="item">要被更新的对象。<para>The object to be updated.</para></param>
public virtual void Update(FileObject item)
{
string fileName = GenerateFileFullPath(item);
string output = Serialize(item); lock (operationLock)
{
System.IO.FileInfo info = new System.IO.FileInfo(fileName);
System.IO.Directory.CreateDirectory(info.Directory.FullName);
System.IO.File.WriteAllText(fileName, output);
}
} /// <summary>
/// 删除指定的对象。
/// <para>Delete specified <paramref name="FileObject"/>.</para>
/// </summary>
/// <param name="item">要被删除的对象。<para>The object to be deleted.</para></param>
public virtual void Delete(FileObject item)
{
if (item == null)
{
throw new ArgumentNullException(item.ToString());
} string filename = GenerateFileFullPath(item);
if (File.Exists(filename))
{
lock (operationLock)
{
File.Delete(filename);
}
}
} #endregion CRUD }

FileDBContext

文件存储方式(Way to store files)

SharpFileDB creates a directory for every table-types in the database's folder. Every instance of a table-type are stored in its respective directory as a single XML file. The file's name is same with the instance's Id.

下载(Download)

I've put the project on github(https://github.com/bitzhuwei/SharpFileDB/) and you are welcom to try, create issues and fork it.

PS: I think this small file database library can help some people. It's where we are that needs us.

SharpFileDB - a file database for small apps的更多相关文章

  1. 小型文件数据库 (a file database for small apps) SharpFileDB

    小型文件数据库 (a file database for small apps) SharpFileDB For english version of this article, please cli ...

  2. BIT祝威博客汇总(Blog Index)

    +BIT祝威+悄悄在此留下版了个权的信息说: 关于硬件(Hardware) <穿越计算机的迷雾>笔记 继电器是如何成为CPU的(1) 继电器是如何成为CPU的(2) 关于操作系统(Oper ...

  3. EBS R12 修改 apps 密码[Z]

    注意:修改密码时应保证所有用户已退出, 最好是关闭应用实例.不用关闭数据库.在修改密码之前一定要改备下数据库中的FND_ORACLE_USERID和FND_USER表.FNDCPASS工具会自动把AP ...

  4. File System Programming --- (二)

    File System Basics The file systems in OS X and iOS handle the persistent storage of data files, app ...

  5. swat主流域文件(file.cio)参数详解——引自http://blog.sciencenet.cn/blog-922140-710636.html

    % file.clo,即主流域文件用于文件管理,包括与模型选项.气候输入.数据库和输出控制相关的信息. Master Watershed File: file.cio Project Descript ...

  6. Database(Mysql)发版控制二

    author:skate time:2014/08/18 Database(Mysql)发版控制 The Liquibase Tool related Database 一.Installation ...

  7. Inno Setup connection to the database and create

    原文 Inno Setup connection to the database and create Description: the first half of this program in I ...

  8. DISQLite3 - A self-contained, embeddable, zero-configuration SQL database engine for Delphi

    DISQLite3 implements a self-contained, embeddable, zero-configuration SQL database engine for Delphi ...

  9. How to get the MD5 checksum for a file: md5sum, digest, csum, fciv

    LINUX: md5sum fileName In Linux, the md5sum utility can be used: aemtux1:/ % md5sum binary.file 0c46 ...

随机推荐

  1. [算法总结]three-way partition

    procedure three-way-partition(A : array of values, mid : value): i ← 0 j ← 0 n ← size of A - 1 while ...

  2. Xcode8控制台乱码的解决方式

    Xcode8里边 Edit Scheme-> Run -> Arguments, 在Environment Variables里边添加 OS_ACTIVITY_MODE = Disable ...

  3. LeetCode 1. Two Sum

    Problem: Given an array of integers, return indices of the two numbers such that they add up to a sp ...

  4. 管理Scope和Lifetime

    Nick Blumhardt’s Autofac lifetime primer 是一个学习Autofac Scope和Lifetime的好地方.这里有很多未理解的,混淆的概念,因此我们将尝试在这里完 ...

  5. [翻译svg教程]svg 中的g元素

    svg 中的<g>元素用来组织svg元素.如果一组svg元素被g元素包裹了,你可以通过对g元素进行变换(transform),被g元素包裹的元素也将被变换,就好这些被svg包裹的元素是一个 ...

  6. 再记录一下如何配置oracle instantclient

    这问题遇到很多次,每次重装系统就遇到一次,却总是搞半天才搞定. 今天再次花费几个小时解决,终于有一个清晰的认识必须记录一下. 一.下载解压,不建任何目录,直接复制tnsname.ora过来.(当然也可 ...

  7. HTML5 与 CSS3 jQuery部分知识总结【转】

    一.    HTML5 为什么需要HTML5 什么是HTML5 HTML5现状及浏览器支持 HTML5优点与缺点 HTML5语法规则与文档声明 HTML5新增表达标签 HTML5多媒体组件 HTML5 ...

  8. CentOS 简单设置samba服务

    1.安装 yum -y install samba 2.设置配置文件 1) 备份Samba的配置文件:cp  /etc/samba/smb.conf  /etc/samba/smb.conf.bak ...

  9. 设计模式(十二):bridge模式

    刚开始看到这个模式并不是很理解,之后在网上看到别人的博客,才大致抓住了脉络. 何谓抽象和实现分离:就是将一个实际的物件跟它的所具有的功能分离.<大话设计模式>中有对手机品牌和具体的手机应用 ...

  10. TPC-H生成.tbl文件导入postgresql数据库的坑

    数据库project好好的不用主流的MySQL和Microsoft server而要求用听都没听过的postgresql (当然,可能你三个都没听过) 这里的坑主要是把生成的那八张.tbl的表导入pg ...