文章出處

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.

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

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.

 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     }

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.

 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     }

 

數據庫(FileDBContext)

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

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


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()