• 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; }
        }
    

    如果不加该配置, 序列化的时候可能会抛出 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();
    

    同理获得一个Document 也得new 一个BsonDocument

    // old API
    var document = collection.FindOne();
    
    // new API
    var document = await collection.Find(new BsonDocument()).FirstOrDefaultAsync();
    

    还有Counting

    // old API
    var count = collection.Count();
    
    // new API
    var count = await collection.CountAsync(new BsonDocument());
    

    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;
        }
    
        ....................................
    }
    

    4.4.2 新老版本混合使用的中间方案

    保留老的Collection 和新的NewCollection。

    public class Dao<TEntity> : IDisposable where TEntity : BaseEntity
    
    {
        protected IMongoCollection<TEntity> NewCollection { get; private set; }
    
        protected MongoCollection<TEntity> Collection { get; private set; }
    
        ......
    }
    

    初始化连接也得有两份,这里使用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);
        }
    

    同时暴露异步和同步的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);
        }
    

    4.4.3 不需要手动管理数据库连接对象

    IMongoDatabase IMongoClient 等都没有Close方法,而且没有继承IDispose接口,这意味着你不需要显示的关闭数据库操作的连接。这个和以前ado的连接使用方式不同。可以直接使用单例模式,不必频繁的去实例化该对象,也不用代码显式的去打开和关闭数据库连接,驱动内部已经帮我们做好了连接池管理。

    参考:

    作者

    找钢网搜索引擎高级工程师——钱露晗

  • 相关阅读:
    关于如何使`(a === 1 && a === 2 && a === 3)`返回`true`问题的思考
    选择适合的类型判断方式
    this的指向问题
    MarkDown基础语法记录
    快速掌握vuex状态管理
    自己如何手动实现一个isNaN的方法
    移动端webapp使用flex布局解决底部导航被手机键盘顶起
    swiper文字内容超出一屏的时候如何实现区域滚动不翻页
    css3动画如何实现停止以后停留在最后一帧动画
    检测任意数据类型
  • 原文地址:https://www.cnblogs.com/izhaogang/p/MongoDB.html
Copyright © 2020-2023  润新知