MongoDB在實際項目中的使用
MongoDB簡介
MongoDB是近些年來流行起來的NoSql的代表,和傳統數據庫最大的區別是支持文檔型數據庫。
當然,現在的一些數據庫通過自定義復合類型,可變長數組等手段也可以模擬文檔型數據庫。
例如在PostgreSQL中,以下是一個復合類型的例子
CREATE TYPE complex AS ( r double precision, i double precision ); CREATE TYPE inventory_item AS ( name text, supplier_id integer, price numeric );
數組的定義如下
array[1,2] --一維數組 array[[1,2],[3,5]] --二維數組
當然,MongoDB生來就是文檔型數據庫,自然在應用層面對數據操作非常友好。
- 使用了一套聚合框架來進行專業的聚合操作,和SQL語言相比,可以支持更加細致的操作
- 可以使用JavaScript編寫MapReduce函數進行數據統計操作,在分布式框架下適合處理大數據
當然,你也可以將MongoDB當做普通的關系型數據庫那樣使用。但是這樣就無法定義View(如果要使用View這樣的功能,還是老老實實將MongoDB當做文檔型數據庫來使用吧)
在 http://codesnippet.info/ 建站過程中,有些基礎數據是簡單的關系型數據,有些是緩存用文檔型數據。
MongoDB的管理工具
這里我就推薦自己開發的工具MongoCola了。
MongoCola項目Github地址
這個項目從2011年到現在已經斷斷續續維持了5年了,從MongoDB1.0到MongoDB3.2,這個工具和MongoDB一起成長起來的。將近200個Star,最近又有兩個兩個朋友貢獻了代碼(現在使用C#開發Winform的人真的不多了),讓我感到很欣慰。
期間進行了一次比較大的重構(由於自己對於軟件設計的理解的提高,以及從盲目的追求快速添加功能到追求整個項目代碼的合理,才下決心進行了一次傷筋動骨的重構,當然現在再看,這次重構很重要,但是代碼仍然還是有問題的,絕非完美。)
在開發 www.codesnippet.info 的過程中,整個MONGODB數據庫的查看都使用了這個工具,同時在使用中發現了一些BUG,也進行了一些改善。當然我現在也不敢保證BUG全部都清除干凈了。如果發現BUG,請和我聯系。
原本打算使用Mono進行跨平台的,但是Mono對於Winform的支持並不好,所以雖然這個工具可以在Mac的OSX上運行,但是最好還是老老實實在Windows下運行比較好。發一張工具的界面圖片,在OSX上截WIndows遠程桌面的圖。
C#驅動程序的再包裝
雖然官方的C#已經和不錯了,雖然MongoDB原生支持ORM的。文檔型對象就是OOP對象了。
但是資深碼農都會自己再寫一些操作數據庫的Helper方法。為自己定制的一套從EntityBase到ORM的方法。
(關於時區的設定,其實可以在系列化設定中完成)
using System; using MongoDB.Bson.Serialization.Attributes; namespace InfraStructure.DataBase { /// <summary> /// 實體 /// 沒有物理刪除,所以自增SN是安全的 /// </summary> public abstract class EntityBase { /// <summary> /// 創建時間 /// </summary> [BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime CreateDateTime; /// <summary> /// 創建者 /// [這里可以是用戶名,亦可是賬號] /// </summary> public string CreateUser; /// <summary> /// 刪除標記 /// </summary> public bool IsDel; /// <summary> /// 序列號 /// </summary> [BsonId] public string Sn; /// <summary> /// 更新時間 /// </summary> [BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime UpdateDateTime; /// <summary> /// 更新者 /// </summary> public string UpdateUser; /// <summary> /// 獲得表名稱 /// </summary> /// <returns></returns> public abstract string GetCollectionName(); /// <summary> /// 獲得主鍵前綴 /// </summary> /// <returns></returns> public abstract string GetPrefix(); /// <summary> /// 序列號格式 /// </summary> public const string SnFormat = "D8"; } }
ORM的增刪改也無非就是將驅動的數據庫操作重新定制了一下而已。
具體代碼可以在本網站的開源項目中找到
數據庫操作輔助類
MongoDB的序列化設定 (干貨)
- 場景一:由於類定義變更,某個字段不需要了,但是如果不進行設定的話,發序列化會出錯,某個數據庫字段沒有配置的實體字段(IgnoreExtraElementsConvention)
- 場景二:在序列化的時候,為了節省空間,希望字段為空的時候,不進行序列化。(IgnoreIfNullConvention)
-
場景三:日期型的數據,序列化的時候,希望可以指定時區(RegisterSerializer)
//http://mongodb.github.io/mongo-csharp-driver/1.10/serialization/ var pack = new ConventionPack(); pack.Add(new IgnoreExtraElementsConvention(true)); pack.Add(new IgnoreIfNullConvention(true)); ConventionRegistry.Register("CustomElementsConvention", pack, t => { return true; }); //DateTime Localize BsonSerializer.RegisterSerializer(typeof(DateTime), new DateTimeSerializer(DateTimeKind.Local)); return true;
當然,你也可以對某個日期型字段單獨指定時區,或者將某個字段定義為主鍵。詳細請參考上文提到的 [BsonId] 和 [BsonDateTimeOptions(Kind = DateTimeKind.Local)] 特性。
FileStorage
MongoDB雖然可以使用FileSystem,但是由於是內存型數據庫,可能會大量消耗內存資源。
這里貼一下FileSystemHelper,但是不建議大型項目在沒有足夠資源的情況下使用!!
using System; using System.Collections.Generic; using System.Web; using MongoDB.Driver.GridFS; using System.IO; using MongoDB.Driver; using System.Drawing.Imaging; using System.Drawing; namespace InfraStructure.Storage { public static class MongoStorage { /// <summary> /// 服務器 /// </summary> private static MongoServer _innerServer; /// <summary> /// 鏈接字符串 /// </summary> private static readonly string Connectionstring = @"mongodb://localhost:"; /// <summary> /// 初始化MongoDB /// </summary> /// <param name="dbList">除去Logger以外</param> /// <param name="defaultDbName"></param> /// <param name="port"></param> /// <returns></returns> public static bool Init(string port = "28030") { try { _innerServer = new MongoClient(Connectionstring + port).GetServer(); _innerServer.Connect(); return true; } catch (Exception) { return false; } } /// <summary> /// 保存文件 /// </summary> /// <param name="file"></param> /// <param name="ownerId"></param> /// <param name="databaseType"></param> /// <returns></returns> public static string InsertFile(HttpPostedFileBase file, string ownerId, string databaseType) { var mongofilename = ownerId + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + file.FileName; var innerFileServer = _innerServer.GetDatabase(databaseType); var gfs = innerFileServer.GetGridFS(new MongoGridFSSettings()); gfs.Upload(file.InputStream, mongofilename); return mongofilename; } /// <summary> /// 保存文件 /// </summary> /// <param name="file"></param> /// <param name="ownerId"></param> /// <param name="databaseType"></param> /// <returns></returns> public static string InsertFile(HttpPostedFile file, string ownerId, string databaseType) { return InsertFile(new HttpPostedFileWrapper(file) as HttpPostedFileBase, ownerId, databaseType); } /// <summary> /// /// </summary> /// <param name="file"></param> /// <param name="fileName"></param> /// <param name="ownerId"></param> /// <param name="databaseType"></param> /// <returns></returns> public static string InsertFile(Stream file, string fileName, string ownerId, string databaseType) { var mongofilename = ownerId + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + "_" + fileName; var innerFileServer = _innerServer.GetDatabase(databaseType); var gfs = innerFileServer.GetGridFS(new MongoGridFSSettings()); gfs.Upload(file, mongofilename); return mongofilename; } /// <summary> /// 保存流為固定文件名 /// </summary> /// <param name="file"></param> /// <param name="fixedFilename"></param> /// <param name="databaseType"></param> public static void InsertStreamWithFixFileName(Stream file, string fixedFilename, string databaseType) { var innerFileServer = _innerServer.GetDatabase(databaseType); var gfs = innerFileServer.GetGridFS(new MongoGridFSSettings()); gfs.Upload(file, fixedFilename); } /// <summary> /// 獲得文件 /// </summary> /// <param name="stream"></param> /// <param name="filename"></param> public