Mongodb .NET Driver 2.0 升級指南

jopen 9年前發布 | 68K 次閱讀 MongoDB NoSQL數據庫

我們舊系統是基于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的連接使用方式不同。可以直接使用單例模式,不必頻繁的去實例化該對象,也不用代碼顯式的去打開和關閉數據庫連接,驅動內部已經幫我們做好了連接池管理。

參考:

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!