本文檔基於C#官方驅動1.8版本。數據庫
本節C#驅動教程談論C#類到BSON對象的序列化和反序列化。序列化是映射一個對象到可保存到MongoDB庫中BSON對象的過程,反序列化由BSON文檔重建對象的逆過程。所以,序列化過程一般被稱爲「對象映射」數組
序列化使用BSON庫處理。BSON庫擁有一個可擴展的序列化結構,因此你能夠控制你的序列化方式。BSON庫提供的默認的序列化其能夠知足你大部分的需求,若是你須要特殊處理,你能夠對其進行擴展。函數
默認序列化器經過「類映射」來處理。類映射是定義類和BSON文檔對象間映射的一種結構,它包含一系列參與序列化的字段或屬性而且爲每個定義了所需的序列化參數(例如,BSON元素名,表明選項等)。spa
默認的序列化器也內建了對.NET數據類型(原始類型、數組、集合、字典等)的支持。code
序列化一個類對象以前,該類映射必須存在,能夠手動建立類映射也能夠簡單的經過自動映射來建立。你能夠在類自動映射的過程當中經過使用序列化相關特性或者初始化代碼的方式施加一些控制。對象
在你的初始化代碼中建立類映射:blog
BsonClassMap.RegisterClassMap<MyClass>();
在此MyClass 會被自動映射或者註冊,固然你可讓你的類經過序列化器自動映射。教程
若是你想控制建立的類映射,你能夠在一個lambda表達式中提供您本身的初始化代碼:事件
BsonClassMap.RegisterClassMap<MyClass)(cm => { cm.MapProperty(c => c.SomeProperty); cm.MapProperty(c => c.AnotherProperty); });
當lambda表達式執行「CM」(簡稱類映射),參數傳遞一個空類映射供您填充。在本例子中兩個屬性經過調用MapProperty 方法被添加進去,傳進MapProperty 的參數是它們自己。使用lambda表達式,而不是僅僅使用一個字符串參數的屬性名稱的優勢是IntelliSense和編譯時檢查,確保你正確的拼寫屬性名稱。ip
另外,也可使用自動映射,而後覆寫一些結果。稍後咱們將會看到這方面的例子。
注意類映射必須只能被註冊一次(若是你試着屢次註冊同一個類,會拋出異常)。
一般狀況,你能夠在只執行一次的代碼路徑中調用RegisterClassMap (Main 方法,Application_Start 事件等),若是你在執行次數多於一次的代碼路徑中調用RegisterClassMap 方法,你也能夠經過調用IsClassMapRegistered 來檢查該類是否被註冊過:
if (!BsonClassMap.IsClassMapRegistered(typeof(MyClass))) { // 爲MyClass註冊類映射 }
Creator Maps
默認狀況下,類一定包括一個無參的構造函數來用於類的實例化,而後配置一個具備和映射屬性相關的參數的構造函數是能夠的,有兩種方法能夠實現:
使用一個表達式,你能夠按照下面方式經過驅動使用一個creator map
public class Person { public string FirstName { get; private set; } public string LastName { get; private set; } public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } } BsonClassMap.RegisterClassMap<Person>(cm => { cm.AutoMap(); cm.MapCreator(p => new Person(p.FirstName, p.LastName)); });
解析表達式樹使構造函數第一個參數和FirstName 相關,使構造函數第二個參數和LastName 屬性相關。還有其餘更復雜的方式處理這種狀況,當須要時,能夠研究探索。
經過特性:
public class Person { public string FirstName { get; set; } public string LastName { get; set; } [BsonConstructor] public Person(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } }
當不止一個構造函數是,咱們經過「知足大多數參數」這一策略來肯定哪一個是最好的匹配,例如:
public class Person { public string FirstName { get; set; } public string LastName { get; set; } public DateTime? BirthDate { get; set; } [BsonConstructor] public Person(string firstName, string lastName) { // snip... } [BsonConstructor] public Person(string firstName, string lastName, DateTime birthDate) { // snip... } }
若是數據庫中文檔對象有一個BirthDate字段,咱們會選擇使用包含3個參數的構造函數,由於它更具體。
除了上面代碼和屬性的形式,映射一建立者能夠經過約定(約束)來處理。
約定(約束)
自動進行類映射有許多須要考慮的方面,例如
. 例如
這些問題的答案就表明一組約定。對於每個約定都有一個默認的慣例,它是最有可能被您使用的一個,在必要時也能夠從新個別約定(甚至寫你本身的)。
若是你想使用和默認約定不一樣的本身的約定,能夠很簡單的建立一個ConventionPack 的實例,添加你想使用的約定進去,而後註冊(換句話說,當你使用具體約定的時候通知序列化器)。例如:
var myConventions = new ConventionPack(); pack.Add(new CamelCaseElementNameConvention()); ConventionRegistry.Register( "My Custom Conventions", pack, t => t.FullName.StartsWith("MyNamespace."));
第三個參數是用來定義什麼時候使用這個約定包的過濾器方法。在這種狀況下,也就是指若是任何類的全名以MyNamespace開頭的話應該使用myConventions約定。
由上面咱們已經瞭解到,除了預約的約定(約束),你能夠自定義本身的約定(約束)。有4個可讓咱們建立和註冊自定義約定的類,運行在不一樣(階段)級別。
運行鍼對類映射階段.
運行鍼對在IClassMapConvention 階段發現映射的每名成員
運行鍼對在IClassMapConvention 階段發現映射的CreatorMap
運行鍼對類映射階段.
約定在他們註冊的每一個階段順序運行,默認的約定會先註冊,這就容許任何用戶註冊的約定覆蓋掉默認的約定。因此某些值可能獲得應用和覆寫,這就要求用戶確保註冊順序的正確性。
注意:
若是一個IPostProcessingConvention 的自定義實現的註冊早於一個IClassMapConvention的自定義實現,那麼IClassMapConvention 先運行,由於它是運行類階段是早於後處理階段的。
待續。。。。
下篇介紹:
Field or Property Level Serialization Options