文章出處

 

 

前言

  最近花了點時間玩了下MongoDB.Driver,進行封裝了工具庫,平常也會經常用到MongoDB,因此寫一篇文章梳理知識同時把自己的成果分享給大家。

  本篇會設計到Lambda表達式的解析,有興趣的同學也看看我之前寫的《表達式樹的解析》。

  文章最后會給出源碼下載地址。

MongoDB簡介

  MongoDB是一個基于分布式文件存儲的非關系型數據庫,相比于其他NoSql它支持復雜的查詢。

  文本是類似JSON的BSON格式,BSON是在JSON的基礎上進化:更快的遍歷、操作更簡易、更多的數據類型。因此MongoDB可以存儲比較復雜的數據類型,同樣也支持建立索引。

  MongoDB的概念有:

  • DataBase(庫)
  • Collections(集合),類似于關系型數據庫的表
  • Document(文檔),類似于關系型數據庫的一條數據

  

MongoDB優缺點

  • 優點

  1. 高效性,內置GridFS,從而達到海量數據存儲,并且滿足大數據集的快速范圍查詢。
  2. 高擴展性,分片使MongoDB的有更高的吞吐量,復制使MongoDB更高的可用性。
  3. BSON文檔,易于理解、查看,
  4. 免費
  • 缺點

  1. 不支持事務
  2. 不支持表關聯
  3. 不耗CPU卻耗內存
  4. 沒有成熟的管理工具

MongoDB使用場景

  擁有高效的存儲的特點,讓MongoDB用在操作日志記錄是非常流行的做法。

  隨著版本的升級提供更加強大的功能,產品逐漸成熟用在主業務也很多,例如電商行業的訂單系統與包裹跟蹤模塊,海量的主訂單與訂單明細,包裹的狀態變更信息。

  然而因為BSON文檔的存儲方式,使平常的開發的思維模式有所變更。舉個栗子,傳統用關系型數據庫,訂單模塊就會分主訂單表和訂單明細表,創建訂單就會用事務同時添加兩表的數據,查找訂單也會通過兩表關聯查詢出來。但是使用MongoDB,主訂單表與其明細,將會以一個完整的對象保存為文檔。

  也因為不支持事務、表關聯的原因,它更加適合用作于一個完整的業務模塊。

  

MongoDB安裝

  本來想寫的,相應的文章在園子太多了,借用一位仁兄的博文,傳送門

  MongoDB下載地址:https://www.mongodb.com/download-center#community

  管理工具:Robomongo,傳送門

MongoDB.Driver的使用

  

  創建一個控制臺,到Nuget下載MongoDB.Driver。寫入以下代碼:

 View Code

  第一個demo:添加數據就完成了。F12可以看到IMongoCollection這個接口,增刪改查都有,注意分One和Many。基礎的使用就不扯過多,在文章尾部的代碼已經提供增刪改查的封裝。

  增刪查的封裝相對簡單,但是MongoDB.Driver提供的update的稍微比較特殊。通過Builders<T>.Update.Set(_fieldname, value)更新指定字段名,有多個字段名需要修改,就要通過new UpdateDefinitionBuilder<T>().Combine(updateDefinitionList)去完成

  然而,這種方式并不適用于我們實際開發,因此需要對Update方法進行 實體更新封裝Lambda更新封裝

實體更新封裝

  通過ID作為過濾條件更新整個實體在實際工作中是常有的。既然通過ID作為條件,那么只能通過UpdateOneAsync進行約束更新一條數據。更新的字段可以通過反射實體對象進行遍歷屬性。下邊是實現代碼:

 View Code

 

Lambda表達式更新封裝

  曾經用過其他ORM都清楚Lambda表達式使用是非常頻繁的,MongoDB.Driver已經支持Lambda表達式的過濾條件,但沒支持部分字段更新,因此由我們自己來寫解析。下邊是現實代碼:

 View Code

 

表達式樹的解析

  對于Lambda表達式的封裝,我側重講一下。假如有一段這樣的更新代碼:  

復制代碼
new MongoDbService().Update<User>(a => a._id == "d99ce40d7a0b49768b74735b91f2aa75", a => new User
            {
                AddressList = new List<string>
                {
                    "number1",
                    "number2"
                },
                Age = 10,
                BirthDateTime = DateTime.Now,
                Name = "skychen",
                NumList = new List<int>
                {
                    1211,23344
                },
                Sex = Sex.Woman,
                Son = new User
                {
                    Name = "xiaochenpi",
                    Age = 1
                }
            });
復制代碼

  那么,我們可以調試監視看看(下圖),我們可以得出兩個重要信息:

  1.Expression<Func<T, T>>解析出來Body的NodeType是MemberInit

  2.Bindings里有需要修改的字段信息。

  再調試進去看看Bindings的第一項,我們又可以了解了幾個重要信息。

  1.Bindings里的元素是MemberAssignment類型。

  2.Member能取到Name屬性,也就是字段名

  3.Expression屬性,使用 Expression.Lambda,進行Compile().Invoke()就能得到我們需要的值。

  fileName和Value都能取到了,那么更新自然能解決了。

  上圖是源碼的部分核心代碼,奇怪的是,我并沒有在VisitMemberInit里進行遍歷Bindings后進行Update.Set,而是將item的Expression屬性再一次訪問。那是因為我需要針對不同的數據類型進行處理。例如:

  常量,我可以定義一個object value進行去接收,如果遇到枚舉我需要強轉成整型。

  集合與數組,假如草率的使用object類型,object value = Expression.Lambda<Func<object>>(node).Compile().Invoke(),那么更新到MongoDB里就會有bug,奇怪的_t,_v就會出現。以此我需要定義為IList才能解決這個問題。

  此外,工作中還會遇到金額或者數量自增的情況。Amount = a.Amount+9.9M,Count =a.Count-1。 MongoDB.Driver提供了Builders<T>.Update.Inc方法,因此重寫二元表達式進行封裝。

結束

  不知道有多少朋友直接拖到文章尾部直接下載源碼的。。。。。。

  如果對您有用,麻煩您推薦一下。

  此外還要感謝廣州賽酷比科技物流組的杜小非同志,率先做了我的小白鼠給我提出了可貴的BUG,不然我還真不敢放出源碼。

  如果有什么問題和建議,可以在下方評論,我會及時回復。

  雙手奉上源碼:https://github.com/SkyChenSky/Framework.MongoDB.git

 

文章列表


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

    IT工程師數位筆記本

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