标签:
小型文件数据库 (a file database for small apps) SharpFileDB
For english version of this article, please click here.
我并不擅长数据库,如有不当之处,请多多指教。
本文参考了(http://www.cnblogs.com/gaochundong/archive/2013/04/24/csharp_file_database.html),在此表示感谢!
我决定做一个以支持小型应用(万人级别)为目标的数据库。
既然是小型的数据库,那么最好不要依赖其它驱动、工具包,免得拖泥带水难以实施。
完全用C#编写成DLL,易学易用。
支持CRUD(增加(Create)、读取(Retrieve)、更新(Update)和删除(Delete))。
不使用SQL,客观原因我不擅长SQL,主观原因我不喜欢SQL,情景原因没有必要。
直接用文本文件或二进制文件存储数据。开发时用文本文件,便于调试;发布时用二进制文件,比较安全。
简单来说,就是纯C#、小型、无SQL。此类库就命名为SharpFileDB。
为了便于共同开发,我把这个项目放到Github上,并且所有类库代码的注释都是中英文双语的。中文便于理解,英文便于今后国际化。也许我想的太多了。
SharpFileDB库的典型使用场景如下。
1 // common cases to use SharpFileDB. 2 FileDBContext db = new FileDBContext(); 3 4 Cat cat = new Cat(); 5 cat.Name = "xiao xiao bai"; 6 db.Create(cat); 7 8 Predicate<Cat> pre = new Predicate<Cat>(x => x.Name == "xiao xiao bai"); 9 IList<Cat> cats = db.Retrieve(pre); 10 11 cat.Name = "xiao bai"; 12 db.Update(cat); 13 14 db.Delete(cat);
这个场景里包含了创建数据库和使用CRUD操作的情形。
我们就从这个使用场景开始设计出第一版最简单的一个文件数据库。
为方便叙述,下面我们以Cat为例进行说明。
1 /// <summary> 2 /// demo file object 3 /// </summary> 4 public class Cat : FileObject 5 { 6 public string Name { get; set; } 7 public int Legs { get; set; } 8 9 public override string ToString() 10 { 11 return string.Format("{0}, Name: {1}, Legs: {2}", base.ToString(), Name, Legs); 12 } 13 }
Cat这个类型就等价于关系数据库里的一个Table。
Cat的一个实例,就等价于关系数据库的Table里的一条记录。
以后我们把这样的类型称为表类型。
类似关系数据库的主键,我们需要用全局唯一的Id来区分每个对象。每个表类型的实例都需要这样一个Id,那么我们就用一个abstract基类做这件事。
1 /// <summary> 2 /// 可在文件数据库中使用CRUD操作的所有类型的基类。 3 /// Base class for all classed that can use CRUD in SharpFileDB. 4 /// </summary> 5 [Serializable] 6 public abstract class FileObject 7 { 8 /// <summary> 9 /// 主键. 10 /// main key. 11 /// </summary> 12 public Guid Id { get; set; } 13 14 /// <summary> 15 /// 创建一个文件对象,并自动为其生成一个全局唯一的Id。 16 /// <para>Create a <see cref="FileObject"/> and generate a global unique id for it.</para> 17 /// </summary> 18 public FileObject() 19 { 20 this.Id = Guid.NewGuid(); 21 } 22 23 public override string ToString() 24 { 25 return string.Format("Id: {0}", this.Id); 26 } 27 }
一个数据库上下文负责各种类型的文件对象的CRUD操作。
1 /// <summary> 2 /// 文件数据库。 3 /// Represents a file database. 4 /// </summary> 5 public class FileDBContext 6 { 7 #region Fields 8 9 /// <summary> 10 /// 文件数据库操作锁 11 /// <para>database operation lock.</para> 12 /// </summary> 13 protected static readonly object operationLock = new object(); 14 15 /// <summary> 16 /// 文件数据库 17 /// <para>Represents a file database.</para> 18 /// </summary> 19 /// <param name="directory">数据库文件所在目录<para>Directory for all files of database.</para></param> 20 public FileDBContext(string directory = null) 21 { 22 if (directory == null) 23 { 24 this.Directory = Environment.CurrentDirectory; 25 } 26 else 27 { 28 Directory = directory; 29 } 30 } 31 32 #endregion 33 34 public override string ToString() 35 { 36 return string.Format("@: {0}", Directory); 37 } 38 39 #region Properties 40 41 /// <summary> 42 /// 数据库文件所在目录 43 /// <para>Directory of database files.</para> 44 /// </summary> 45 public virtual string Directory { get; protected set; } 46 47 #endregion 48 49 50 protected string Serialize(FileObject item) 51 { 52 using (StringWriterWithEncoding sw = new StringWriterWithEncoding(Encoding.UTF8)) 53 { 54 XmlSerializer serializer = new XmlSerializer(item.GetType()); 55 serializer.Serialize(sw, item); 56 string serializedString = sw.ToString(); 57 58 return serializedString; 59 } 60 } 61 62 /// <summary> 63 /// 将字符串反序列化成文档对象 64 /// </summary> 65 /// <typeparam name="TDocument">文档类型</typeparam> 66 /// <param name="serializedFileObject">字符串</param> 67 /// <returns> 68 /// 文档对象 69 /// </returns> 70 protected TFileObject Deserialize<TFileObject>(string serializedFileObject) 71 where TFileObject : FileObject 72 { 73 if (string.IsNullOrEmpty(serializedFileObject)) 74 throw new ArgumentNullException("data"); 75 76 using (StringReader sr = new StringReader(serializedFileObject)) 77 { 78 XmlSerializer serializer = new XmlSerializer(typeof(TFileObject)); 79 object deserializedObj = serializer.Deserialize(sr); 80 TFileObject fileObject = deserializedObj as TFileObject; 81 return fileObject; 82 } 83 } 84 85 protected string GenerateFileFullPath(FileObject item) 86 { 87 string path = GenerateFilePath(item.GetType()); 88 string name = item.GenerateFileName(); 89 string fullname = Path.Combine(path, name); 90 return fullname; 91 } 92 93 /// <summary> 94 /// 生成文件路径 95 /// </summary> 96 /// <typeparam name="TDocument">文档类型</typeparam> 97 /// <returns>文件路径</returns> 98 protected string GenerateFilePath(Type type) 99 { 100 string path = Path.Combine(this.Directory, type.Name); 101 return path; 102 } 103 104 #region CRUD 105 106 /// <summary> 107 /// 增加一个<see cref="FileObject"/>到数据库。这实际上创建了一个文件。 108 /// <para>Create a new <see cref="FileObject"/> into database. This operation will create a new file.</para> 109 /// </summary> 110 /// <param name="item"></param> 111 public virtual void Create(FileObject item) 112 { 113 string fileName = GenerateFileFullPath(item); 114 string output = Serialize(item); 115 116 lock (operationLock) 117 { 118 System.IO.FileInfo info = new System.IO.FileInfo(fileName); 119 System.IO.Directory.CreateDirectory(info.Directory.FullName); 120 System.IO.File.WriteAllText(fileName, output); 121 } 122 } 123 124 /// <summary> 125 /// 检索符合给定条件的所有<paramref name="TFileObject"/>。 126 /// <para>Retrives all <paramref name="TFileObject"/> that satisfies the specified condition.</para> 127 /// </summary> 128 /// <typeparam name="TFileObject"></typeparam> 129 /// <param name="predicate">检索出的对象应满足的条件。<para>THe condition that should be satisfied by retrived object.</para></param> 130 /// <returns></returns> 131 public virtual IList<TFileObject> Retrieve<TFileObject>(Predicate<TFileObject> predicate) 132 where TFileObject : FileObject 133 { 134 IList<TFileObject> result = new List<TFileObject>(); 135 if (predicate != null) 136 { 137 string path = GenerateFilePath(typeof(TFileObject)); 138 string[] files = System.IO.Directory.GetFiles(path, "*.xml", SearchOption.AllDirectories); 139 foreach (var item in files) 140 { 141 string fileContent = File.ReadAllText(item); 142 TFileObject deserializedFileObject = Deserialize<TFileObject>(fileContent); 143 if (predicate(deserializedFileObject)) 144 { 145 result.Add(deserializedFileObject); 146 } 147 } 148 } 149 150 return result; 151 } 152 153 /// <summary> 154 /// 更新给定的对象。 155 /// <para>Update specified <paramref name="FileObject"/>.</para> 156 /// </summary> 157 /// <param name="item">要被更新的对象。<para>The object to be updated.</para></param> 158 public virtual void Update(FileObject item) 159 { 160 string fileName = GenerateFileFullPath(item); 161 string output = Serialize(item); 162 163 lock (operationLock) 164 { 165 System.IO.FileInfo info = new System.IO.FileInfo(fileName); 166 System.IO.Directory.CreateDirectory(info.Directory.FullName); 167 System.IO.File.WriteAllText(fileName, output); 168 } 169 } 170 171 /// <summary> 172 /// 删除指定的对象。 173 /// <para>Delete specified <paramref name="FileObject"/>.</para> 174 /// </summary> 175 /// <param name="item">要被删除的对象。<para>The object to be deleted.</para></param> 176 public virtual void Delete(FileObject item) 177 { 178 if (item == null) 179 { 180 throw new ArgumentNullException(item.ToString()); 181 } 182 183 string filename = GenerateFileFullPath(item); 184 if (File.Exists(filename)) 185 { 186 lock (operationLock) 187 { 188 File.Delete(filename); 189 } 190 } 191 } 192 193 #endregion CRUD 194 195 }
在数据库目录下,SharpFileDB为每个表类型创建一个文件夹,在各自文件夹内存储每个对象。每个对象都占用一个XML文件。暂时用XML格式,因为是.NET内置的格式,省的再找外部序列化工具。XML文件名与其对应的对象Id相同。
我已将源码放到(https://github.com/bitzhuwei/SharpFileDB/),欢迎试用、提建议或Fork此项目。
PS:我国大多数县的人口为几万到几十万。目前,县里各种政府部门急需实现信息化网络化办公办事,但他们一般用不起那种月薪上万的开发者和高端软件公司。我注意到,一个县级政府部门日常应对的人群数量就是万人左右,甚至常常是千人左右。所以他们不需要太高端复杂的系统设计,用支持万人级别的数据库就可以了。另一方面,初级开发者也不能充分利用那些看似高端复杂的数据库的优势。做个小型系统而已,还是简单一点好。
所以我就想做这样一个小型文件数据库,我相信这会帮助很多人。能以己所学惠及大众,才是我们的价值所在。
小型文件数据库 (a file database for small apps) SharpFileDB
标签:
原文地址:http://www.cnblogs.com/bitzhuwei/p/SharpFileDB.html