前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C#实现文件数据库

C#实现文件数据库

作者头像
跟着阿笨一起玩NET
发布2018-09-19 16:49:11
5760
发布2018-09-19 16:49:11
举报

本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载。

如果你需要一个简单的磁盘文件索引数据库,这篇文章可以帮助你。

文件数据库描述:

  • 每个文档对象保存为一个独立文件,例如一篇博客。
  • 文件内容序列化支持XML或JSON。
  • 支持基本的CRUD操作。

文件数据库抽象类实现

代码语言:javascript
复制
  1   /// <summary>
  2   /// 文件数据库,这是一个抽象类。
  3   /// </summary>
  4   public abstract class FileDatabase
  5   {
  6     #region Fields
  7 
  8     /// <summary>
  9     /// 文件数据库操作锁
 10     /// </summary>
 11     protected static readonly object operationLock = new object();
 12     private static HashSet<char> invalidFileNameChars;
 13 
 14     static FileDatabase()
 15     {
 16       invalidFileNameChars = new HashSet<char>() { '\0', ' ', '.', '$', '/', '\\' };
 17       foreach (var c in Path.GetInvalidPathChars()) { invalidFileNameChars.Add(c); }
 18       foreach (var c in Path.GetInvalidFileNameChars()) { invalidFileNameChars.Add(c); }
 19     }
 20 
 21     /// <summary>
 22     /// 文件数据库
 23     /// </summary>
 24     /// <param name="directory">数据库文件所在目录</param>
 25     protected FileDatabase(string directory)
 26     {
 27       Directory = directory;
 28     }
 29 
 30     #endregion
 31 
 32     #region Properties
 33 
 34     /// <summary>
 35     /// 数据库文件所在目录
 36     /// </summary>
 37     public virtual string Directory { get; private set; }
 38 
 39     /// <summary>
 40     /// 是否输出缩进
 41     /// </summary>
 42     public virtual bool OutputIndent { get; set; }
 43 
 44     /// <summary>
 45     /// 文件扩展名
 46     /// </summary>
 47     public virtual string FileExtension { get; set; }
 48 
 49     #endregion
 50 
 51     #region Public Methods
 52 
 53     /// <summary>
 54     /// 保存文档
 55     /// </summary>
 56     /// <typeparam name="TDocument">文档类型</typeparam>
 57     /// <param name="document">文档对象</param>
 58     /// <returns>文档ID</returns>
 59     public virtual string Save<TDocument>(TDocument document)
 60     {
 61       return Save<TDocument>(ObjectId.NewObjectId().ToString(), document);
 62     }
 63 
 64     /// <summary>
 65     /// 保存文档
 66     /// </summary>
 67     /// <typeparam name="TDocument">文档类型</typeparam>
 68     /// <param name="id">文档ID</param>
 69     /// <param name="document">文档对象</param>
 70     /// <returns>文档ID</returns>
 71     public virtual string Save<TDocument>(string id, TDocument document)
 72     {
 73       if (string.IsNullOrEmpty(id))
 74         throw new ArgumentNullException("id");
 75 
 76       if (document == null)
 77         throw new ArgumentNullException("document");
 78 
 79       Delete<TDocument>(id);
 80 
 81       try
 82       {
 83         string fileName = GenerateFileFullPath<TDocument>(id);
 84         string output = Serialize(document);
 85 
 86         lock (operationLock)
 87         {
 88           System.IO.FileInfo info = new System.IO.FileInfo(fileName);
 89           System.IO.Directory.CreateDirectory(info.Directory.FullName);
 90           System.IO.File.WriteAllText(fileName, output);
 91         }
 92       }
 93       catch (Exception ex)
 94       {
 95         throw new FileDatabaseException(
 96           string.Format(CultureInfo.InvariantCulture, 
 97           "Save document failed with id [{0}].", id), ex);
 98       }
 99 
100       return id;
101     }
102 
103     /// <summary>
104     /// 根据文档ID查找文档
105     /// </summary>
106     /// <typeparam name="TDocument">文档类型</typeparam>
107     /// <param name="id">文档ID</param>
108     /// <returns>文档对象</returns>
109     public virtual TDocument FindOneById<TDocument>(string id)
110     {
111       if (string.IsNullOrEmpty(id))
112         throw new ArgumentNullException("id");
113 
114       try
115       {
116         string fileName = GenerateFileFullPath<TDocument>(id);
117         if (File.Exists(fileName))
118         {
119           string fileData = File.ReadAllText(fileName);
120           return Deserialize<TDocument>(fileData);
121         }
122 
123         return default(TDocument);
124       }
125       catch (Exception ex)
126       {
127         throw new FileDatabaseException(
128           string.Format(CultureInfo.InvariantCulture, 
129           "Find document by id [{0}] failed.", id), ex);
130       }
131     }
132 
133     /// <summary>
134     /// 查找指定类型的所有文档
135     /// </summary>
136     /// <typeparam name="TDocument">文档类型</typeparam>
137     /// <returns>文档对象序列</returns>
138     public virtual IEnumerable<TDocument> FindAll<TDocument>()
139     {
140       try
141       {
142         string[] files = System.IO.Directory.GetFiles(
143           GenerateFilePath<TDocument>(), 
144           "*." + FileExtension, 
145           SearchOption.TopDirectoryOnly);
146 
147         List<TDocument> list = new List<TDocument>();
148         foreach (string fileName in files)
149         {
150           string fileData = File.ReadAllText(fileName);
151           TDocument document = Deserialize<TDocument>(fileData);
152           if (document != null)
153           {
154             list.Add(document);
155           }
156         }
157 
158         return list;
159       }
160       catch (Exception ex)
161       {
162         throw new FileDatabaseException(
163           "Find all documents failed.", ex);
164       }
165     }
166 
167     /// <summary>
168     /// 根据指定文档ID删除文档
169     /// </summary>
170     /// <typeparam name="TDocument">文档类型</typeparam>
171     /// <param name="id">文档ID</param>
172     public virtual void Delete<TDocument>(string id)
173     {
174       if (string.IsNullOrEmpty(id))
175         throw new ArgumentNullException("id");
176 
177       try
178       {
179         string fileName = GenerateFileFullPath<TDocument>(id);
180         if (File.Exists(fileName))
181         {
182           lock (operationLock)
183           {
184             File.Delete(fileName);
185           }
186         }
187       }
188       catch (Exception ex)
189       {
190         throw new FileDatabaseException(
191           string.Format(CultureInfo.InvariantCulture, 
192           "Delete document by id [{0}] failed.", id), ex);
193       }
194     }
195 
196     /// <summary>
197     /// 删除所有指定类型的文档
198     /// </summary>
199     /// <typeparam name="TDocument">文档类型</typeparam>
200     public virtual void DeleteAll<TDocument>()
201     {
202       try
203       {
204         string[] files = System.IO.Directory.GetFiles(
205           GenerateFilePath<TDocument>(), "*." + FileExtension, 
206           SearchOption.TopDirectoryOnly);
207 
208         foreach (string fileName in files)
209         {
210           lock (operationLock)
211           {
212             File.Delete(fileName);
213           }
214         }
215       }
216       catch (Exception ex)
217       {
218         throw new FileDatabaseException(
219           "Delete all documents failed.", ex);
220       }
221     }
222 
223     /// <summary>
224     /// 获取指定类型文档的数量
225     /// </summary>
226     /// <typeparam name="TDocument">文档类型</typeparam>
227     /// <returns>文档的数量</returns>
228     public virtual int Count<TDocument>()
229     {
230       try
231       {
232         string[] files = System.IO.Directory.GetFiles(
233           GenerateFilePath<TDocument>(), 
234           "*." + FileExtension, SearchOption.TopDirectoryOnly);
235         if (files != null)
236         {
237           return files.Length;
238         }
239         else
240         {
241           return 0;
242         }
243       }
244       catch (Exception ex)
245       {
246         throw new FileDatabaseException(
247           "Count all documents failed.", ex);
248       }
249     }
250 
251     #endregion
252 
253     #region Protected Methods
254 
255     /// <summary>
256     /// 生成文件全路径
257     /// </summary>
258     /// <typeparam name="TDocument">文档类型</typeparam>
259     /// <param name="id">文档ID</param>
260     /// <returns>文件路径</returns>
261     protected virtual string GenerateFileFullPath<TDocument>(string id)
262     {
263       return Path.Combine(GenerateFilePath<TDocument>(), 
264         GenerateFileName<TDocument>(id));
265     }
266 
267     /// <summary>
268     /// 生成文件路径
269     /// </summary>
270     /// <typeparam name="TDocument">文档类型</typeparam>
271     /// <returns>文件路径</returns>
272     protected virtual string GenerateFilePath<TDocument>()
273     {
274       return Path.Combine(this.Directory, typeof(TDocument).Name);
275     }
276 
277     /// <summary>
278     /// 生成文件名
279     /// </summary>
280     /// <typeparam name="TDocument">文档类型</typeparam>
281     /// <param name="id">文档ID</param>
282     /// <returns>文件名</returns>
283     protected virtual string GenerateFileName<TDocument>(string id)
284     {
285       if (string.IsNullOrEmpty(id))
286         throw new ArgumentNullException("id");
287 
288       foreach (char c in id)
289       {
290         if (invalidFileNameChars.Contains(c))
291         {
292           throw new FileDatabaseException(
293             string.Format(CultureInfo.InvariantCulture, 
294             "The character '{0}' is not a valid file name identifier.", c));
295         }
296       }
297 
298       return string.Format(CultureInfo.InvariantCulture, "{0}.{1}", id, FileExtension);
299     }
300 
301     /// <summary>
302     /// 将指定的文档对象序列化至字符串
303     /// </summary>
304     /// <param name="value">指定的文档对象</param>
305     /// <returns>文档对象序列化后的字符串</returns>
306     protected abstract string Serialize(object value);
307 
308     /// <summary>
309     /// 将字符串反序列化成文档对象
310     /// </summary>
311     /// <typeparam name="TDocument">文档类型</typeparam>
312     /// <param name="data">字符串</param>
313     /// <returns>文档对象</returns>
314     protected abstract TDocument Deserialize<TDocument>(string data);
315 
316     #endregion
317   }

XML文件数据库实现

代码语言:javascript
复制
 1   /// <summary>
 2   /// XML文件数据库
 3   /// </summary>
 4   public class XmlDatabase : FileDatabase
 5   {
 6     /// <summary>
 7     /// XML文件数据库
 8     /// </summary>
 9     /// <param name="directory">数据库文件所在目录</param>
10     public XmlDatabase(string directory)
11       : base(directory)
12     {
13       FileExtension = @"xml";
14     }
15 
16     /// <summary>
17     /// 将指定的文档对象序列化至字符串
18     /// </summary>
19     /// <param name="value">指定的文档对象</param>
20     /// <returns>
21     /// 文档对象序列化后的字符串
22     /// </returns>
23     protected override string Serialize(object value)
24     {
25       if (value == null)
26         throw new ArgumentNullException("value");
27 
28       using (StringWriterWithEncoding sw = new StringWriterWithEncoding(Encoding.UTF8))
29       {
30         XmlSerializer serializer = new XmlSerializer(value.GetType());
31         serializer.Serialize(sw, value);
32         return sw.ToString();
33       }
34     }
35 
36     /// <summary>
37     /// 将字符串反序列化成文档对象
38     /// </summary>
39     /// <typeparam name="TDocument">文档类型</typeparam>
40     /// <param name="data">字符串</param>
41     /// <returns>
42     /// 文档对象
43     /// </returns>
44     protected override TDocument Deserialize<TDocument>(string data)
45     {
46       if (string.IsNullOrEmpty(data))
47         throw new ArgumentNullException("data");
48 
49       using (StringReader sr = new StringReader(data))
50       {
51         XmlSerializer serializer = new XmlSerializer(typeof(TDocument));
52         return (TDocument)serializer.Deserialize(sr);
53       }
54     }
55   }

JSON文件数据库实现

代码语言:javascript
复制
 1   /// <summary>
 2   /// JSON文件数据库
 3   /// </summary>
 4   public class JsonDatabase : FileDatabase
 5   {
 6     /// <summary>
 7     /// JSON文件数据库
 8     /// </summary>
 9     /// <param name="directory">数据库文件所在目录</param>
10     public JsonDatabase(string directory)
11       : base(directory)
12     {
13       FileExtension = @"json";
14     }
15 
16     /// <summary>
17     /// 将指定的文档对象序列化至字符串
18     /// </summary>
19     /// <param name="value">指定的文档对象</param>
20     /// <returns>
21     /// 文档对象序列化后的字符串
22     /// </returns>
23     protected override string Serialize(object value)
24     {
25       if (value == null)
26         throw new ArgumentNullException("value");
27 
28       return JsonConvert.SerializeObject(value, OutputIndent);
29     }
30 
31     /// <summary>
32     /// 将字符串反序列化成文档对象
33     /// </summary>
34     /// <typeparam name="TDocument">文档类型</typeparam>
35     /// <param name="data">字符串</param>
36     /// <returns>
37     /// 文档对象
38     /// </returns>
39     protected override TDocument Deserialize<TDocument>(string data)
40     {
41       if (string.IsNullOrEmpty(data))
42         throw new ArgumentNullException("data");
43 
44       return JsonConvert.DeserializeObject<TDocument>(data);
45     }
46   }

Test Double

代码语言:javascript
复制
 1   [Serializable]
 2   public class Cat
 3   {
 4     public Cat()
 5     {
 6       Id = ObjectId.NewObjectId().ToString();
 7     }
 8 
 9     public Cat(string id)
10     {
11       Id = id;
12     }
13 
14     public string Name { get; set; }
15     public int Legs { get; set; }
16 
17     public string Id { get; set; }
18 
19     public override string ToString()
20     {
21       return string.Format("DocumentId={0}, Name={1}, Legs={2}", Id, Name, Legs);
22     }
23   }

使用举例

代码语言:javascript
复制
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       TestJsonDatabase();
 6       TestXmlDatabase();
 7 
 8       Console.ReadKey();
 9     }
10 
11     private static void TestJsonDatabase()
12     {
13       JsonDatabase db = new JsonDatabase(@"C:\tmp");
14       db.OutputIndent = true;
15 
16       Cat origin = new Cat() { Name = "Garfield", Legs = 4 };
17       db.Save<Cat>(origin);
18 
19       db.Save<Cat>(origin.Id, origin);
20       db.Delete<Cat>(origin.Id);
21     }
22 
23     private static void TestXmlDatabase()
24     {
25       XmlDatabase db = new XmlDatabase(@"C:\tmp");
26       db.OutputIndent = true;
27 
28       Cat origin = new Cat() { Name = "Garfield", Legs = 4 };
29       db.Save<Cat>(origin);
30 
31       db.Save<Cat>(origin.Id, origin);
32       db.Delete<Cat>(origin.Id);
33     }
34   }

本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2013-04-24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文件数据库抽象类实现
  • XML文件数据库实现
  • JSON文件数据库实现
  • Test Double
  • 使用举例
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档