Mongodb .NET Driver 2.0 升級指南
我們舊系統是基于Mongodb 1.10的驅動,為了一些更方便的功能(為了宇宙的和平),我們決定將驅動升級到2.0。先來看下2.0 驅動有哪些新特性。
1、Async
該版本的驅動為了以比較自然的方式支持異步特性進行了重寫,大部分api 返回的是Task或者Task<T>,于是我們這里可以愉快的使用async 和await 。這個年代,沒有async的API 都不好意思出來打招呼。當然,該版本的驅動還提供了兼容老版本的Sync API 。不過,所有新的應用這里都建議使用新的API。
2、新的 API
2.1 使用了面向接口的風格,更有利于測試
IMongoClient, IMongoDatabase, IMongoCollection
2.2 Find API 支持所有的表達式樹包括project
var names = await db.GetCollection<Person>("people") .Find(x => x.FirstName == "Jack") .SortBy(x => x.Age) .Project(x => x.FirstName + " " + x.LastName) .ToListAsync();
2.3 Aggregation 支持大部分表達式樹
var totalAgeByLastName = await db.GetCollection<Person>("people") .Aggregate() .Match(x => x.FirstName == "Jack") .GroupBy(x => x.LastName, g => new { _id = g.Key, TotalAge = g.Sum(x => x.Age)}) .ToListAsync();
2.4 支持dynamic
var person = new ExpandoObject(); person.FirstName = "Jane"; person.Age = 12; person.PetNames = new List<dynamic> { "Sherlock", "Watson" } await db.GetCollection<dynamic>("people").InsertOneAsync(person);
這里我們系統中使用到了dynamic 的屬性字段,如果用老版本的驅動就得手動實現BsonDocument的序列化和反序列化。新版本內置了ExpandoObjectSerializer 。
public abstract class BaseItem { public ObjectId _id { get; set; }public string AppNo { get; set; } public string Ip { get; set; } public DateTime CreateTime { get; set; } [BsonSerializer(typeof(ExpandoObjectSerializer))] public ExpandoObject Content { get; set; } }</pre><br />
如果不加該配置, 序列化的時候可能會拋出 stackoverflow 的Exception。
2.5 一些額外的實驗性功能
2.5.1 全局性的日志監聽功能
var settings = new MongoClientSettings { ClusterConfigurator = cb => { var textWriter = TextWriter.Synchronized(new StreamWriter("mylogfile.txt")); cb.AddListener(new LogListener(textWriter)); } };
這個是居家旅游必備的好東西。
2.5.2 性能計數器
var settings = new MongoClientSettings { ClusterConfigurator = cb => { cb.UsePeformanceCounters("MyApplicationName"); } };這個用處不是太大。
3、兼容性
3.1 驅動和服務器的兼容性
![]()
3.2 驅動的.NET 語言兼容性
![]()
4、舊版本升級指南
2.0版本和1.x 版本還是有不少差別的,雖然在編碼的過程中盡量做到了兼容,但是如果要做驅動版本升級,還是有不少需要注意的點。
4.1 系統需求
不再支持.NET 3.5 和 .NET 4.0。
4.2 BSON
改進了BSON 序列化的基礎,所有實現了自定義序列化器的代碼會受影響。這是因為我們必須要支持dynamic類型的序列化和提升內存管理的性能,所以這里做了點小的修改。
4.3 Driver
MongoConnectionStringBuilder 類被移除。請使用mongodb 連接串或者MongoUrlBuilder代替。
MongoServer 標記為deprecated。所以所有調用了MongoClient.GetServer() 的代碼將會獲得一個warning。
獲得所有Documents
// old API var list = collection.FindAll().ToList();// new API var list = await collection.Find(new BsonDocument()).ToListAsync();</pre>
同理獲得一個Document 也得new 一個BsonDocument// old API var document = collection.FindOne();// new API var document = await collection.Find(new BsonDocument()).FirstOrDefaultAsync();</pre>
還有Counting// old API var count = collection.Count();// new API var count = await collection.CountAsync(new BsonDocument());</pre>
4.4 Dao 改造
我們這里有部分老的代碼無法直接改成async 的模式, 因而有了如下一個混合版本的dao。老的代碼少做修改,新的代碼基本走到async 的模式。
4.4.1 老版本的dao
public class Dao<TEntity> where TEntity:class {#region Properties protected MongoCollection<TEntity> Collection { get; private set; } #endregion #region Constructors public Dao() { var db = DataBaseManager.Database; Collection = db.GetCollection<TEntity>(typeof(TEntity).Name.ToLower()); } #endregion #region Public Methods public void Add(TEntity entity) { this.Collection.Insert(entity); } public void AddBatch(IEnumerable<TEntity> entities) { this.Collection.InsertBatch(entities); } public void Update(TEntity entity) { this.Collection.Save(entity); } public void Update(IMongoQuery query, IMongoUpdate update) { this.Collection.Update(query, update); } public void Delete(string id) { ObjectId objId; if (ObjectId.TryParse(id, out objId)) { throw new Exception("objectid格式不正確!"); } Collection.Remove(Query.EQ("_id", objId)); } public void Delete(IMongoQuery query) { Collection.Remove(query); } public TEntity FindOne(string id) { ObjectId objId; if (ObjectId.TryParse(id, out objId)) { throw new Exception("objectid格式不正確!"); } return Collection.Find(Query.EQ("_id", objId)).FirstOrDefault(); } public IEnumerable<TEntity> Find(IMongoQuery query) { var results = Collection.Find(query); return results; } ....................................
}</pre>
4.4.2 新老版本混合使用的中間方案
保留老的Collection 和新的NewCollection。
public class Dao<TEntity> : IDisposable where TEntity : BaseEntity{ protected IMongoCollection NewCollection { get; private set; }
protected MongoCollection<TEntity> Collection { get; private set; } ......
}</pre>
初始化連接也得有兩份,這里使用pragma warning disable 618禁止warning.
private static IMongoDatabase GetDatabase(string key) { var connectString = ConfigurationManager.ConnectionStrings[key].ConnectionString; var mongoUrl = new MongoUrl(connectString);var client = new MongoClient(connectString); return client.GetDatabase(mongoUrl.DatabaseName); } internal static MongoDatabase GetLegacyDatabase(string key) { var connectString = ConfigurationManager.ConnectionStrings[key].ConnectionString; var mongoUrl = new MongoUrl(connectString); var client = new MongoClient(connectString);
pragma warning disable 618
var server = client.GetServer();
pragma warning restore 618
return server.GetDatabase(mongoUrl.DatabaseName); }</pre><br />
同時暴露異步和同步的api
public async Task InsertOneAsync(TEntity entity) { await NewCollection.InsertOneAsync(entity); }public async Task InsertManyAsync(IEnumerable<TEntity> entities) { await NewCollection.InsertManyAsync(entities); } public async Task ReplaceOneAsync(TEntity entity) { await NewCollection.ReplaceOneAsync(a => a.Id == entity.Id, entity); } ............................ public long Add(TEntity entity) { return this.Collection.Insert(entity).DocumentsAffected; } public IQueryable<TEntity> GetQuery() { return this.Collection.AsQueryable(); } public IEnumerable<WriteConcernResult> AddBatch(IEnumerable<TEntity> entities) { return this.Collection.InsertBatch(entities); }</pre><br />
4.4.3 不需要手動管理數據庫連接對象
IMongoDatabase IMongoClient等都沒有Close方法,而且沒有繼承IDispose接口,這意味著你不需要顯示的關閉數據庫操作的連接。這個和以前ado的連接使用方式不同。可以直接使用單例模式,不必頻繁的去實例化該對象,也不用代碼顯式的去打開和關閉數據庫連接,驅動內部已經幫我們做好了連接池管理。
參考: