前言
最近閑的時間有點多,所以還是寫博客吧。
有人說Mongo 2.0的寫法難以把控,好多地方不知道咋用,所以堅持用1.0(不願意去嘗試2.0),我感覺不可理解。所以寫篇博客比較下。
Mongo C#驅動1.0到2.0設計方面的差別非常大。
正文
先說1.0吧,更像是Mongo 各功能的直譯,所以寫法與mongo原生查詢修改等比較類似,易上手。但是設計上確實存在很多問題。簡單說幾點:
a.在query的構建方面,雖然有問題,但是勉強能接受
1 var modelCursor = collection.Find( 2 Query.And(Query.Matches("Name", "test"),Query.EQ("Age",10),Query.In("id",new BsonArray(){"123","456","sda"}))); 3 var modelCursor1 = collection.Find( 4 Query.And(Query<TestData>.Matches(t => t.Name, "test"), Query<TestData>.EQ(t=>t.Age, 10), Query<TestData>.In(t=>t._id, new BsonArray() { "123", "456", "sda" })));
第一種方式代碼簡單,但是硬字符串比較多,萬一改個字段,維護難度大
第二種方式代碼維護性好,但是代碼真是煩瑣,每個query都要來個泛型約定,冗余太多了
b.大量linq方法與數據庫中執行構建查詢的方法混在一起,這點是可以改進的。否則可能出現一下問題:
在查詢返回類型上MongoCursor<TDefaultDocument>。繼承IEnumerable<T>,Find時只是構建查詢而已,只有調用GetEnumerator()才回去數據庫查詢數據。也就是ToList()或者foreach的時候才去查詢。這一點設計的沒錯跟ef一致的
執行方式,看看源碼截圖:
這樣看起來沒問題,但是在使用時,如下代碼:
這是段常用的分頁代碼,我天真的以為他會將cursor.Skip().Take()生成查詢去數據庫中執行再把200條結果返回給我。但是實際情況不是這樣的
其實已經把所有數據都拿出來了,只是在客戶端使用了linq的Skip去跳過而已。
正確的用法是:
要使用cursor的Set開頭的方法才是構建查詢的。
如果你學ef那也學徹底點啊,不信你看ef查詢時的Skip:
人家把Skip Take都“重寫”了好吧,根本沒使用IEnumerable<T>的Skip。這一點想說明的,就是導致了大量的linq客戶端執行的代碼與Mongo服務端執行的代碼混雜的問題
c.另外分組查詢是設計非常不好的。比如:
請看GroupArgs的注釋:
知道我寫這么多注釋,為啥嗎,我怕過兩天我也不知道咋用的了。更別說讓其他同事用了。一個分組查詢居然還要在c#中寫原始的js代碼來實現。所以驅動在這里的實現只是半成品的。
對比着再說說2.0吧,首先與時俱進大量采用了異步編程。然后對lamda表達式與強類型的支持都做了改進。
a.首先查詢全部是lamda表達式了,這次算是把查詢這塊徹底本地化了。不用再去記住Mongo查詢原生的語法了,門檻很低了。如:
b.重寫了查詢返回值類型,叫什么FindFluent。翻譯過來就是可鏈式調用的東東,看看源碼:
果然都是返回的this便於鏈式調用,再看看里面的方法:
2.0不再繼承IEnumerable接口,里面的方法全部是自己實現的了,比如:
findFluent.Skip(10).Limit(10).SortBy(t => t.Age);使用起來順手多了,而且都是到數據庫端執行的.
就連取集合的First方法,也是經過服務端處理的,不信你看:
你再看看Single方法:
查詢的時候都做了Limit處理,…………………………但是會不會突然心頭一緊。怎么Single的時候find.Limit(2)啊,太奇怪了。不過聰明的小伙伴,想一會兒應該知道咋回事了,哈哈!
再看看分組查詢優化,我就不說了,把c#里寫js代碼的部分直接搞掉了。使用lamda表達式的方式實現了,如下:
var dataGroup = collection.Aggregate().Group(t => t.Age, g => new { _id = g.Key, TotalAge = g.Sum(x => x.Age) });
需要注意的是2.0都是異步編程,熟悉下用法就行了,這也是.net4.5比較大的改變:把異步編程變得簡單。
先寫這么多了,那里說的不對的地方大家多多指出。另外身邊有誰還堅持用1.0的,一定要嘗試着去說服他……額……