我们旧系统是基于Mongodb 1.10的驱动,为了一些更方便的功能(为了宇宙的和平),我们决定将驱动升级到2.0。先来看下2.0 驱动有哪些新特性。
该版本的驱动为了以比较自然的方式支持异步特性进行了重写,大部分api 返回的是 Task
或者 Task<T>
,于是我们这里可以愉快的使用async 和await 。这个年代,没有async的API 都不好意思出来打招呼。当然,该版本的驱动还提供了兼容老版本的Sync API 。不过,所有新的应用这里都建议使用新的API。
IMongoClient, IMongoDatabase, IMongoCollection
var names = await db.GetCollection<Person>("people") .Find(x => x.FirstName == "Jack") .SortBy(x => x.Age) .Project(x => x.FirstName + " " + x.LastName) .ToListAsync();
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();
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。
var settings = new MongoClientSettings { ClusterConfigurator = cb => { var textWriter = TextWriter.Synchronized(new StreamWriter("mylogfile.txt")); cb.AddListener(new LogListener(textWriter)); } };
这个是居家旅游必备的好东西。
var settings = new MongoClientSettings { ClusterConfigurator = cb => { cb.UsePeformanceCounters("MyApplicationName"); } };
这个用处不是太大。
2.0版本和1.x 版本还是有不少差别的,虽然在编码的过程中尽量做到了兼容,但是如果要做驱动版本升级,还是有不少需要注意的点。
不再支持.NET 3.5 和 .NET 4.0。
改进了BSON 序列化的基础,所有实现了自定义序列化器的代码会受影响。这是因为我们必须要支持dynamic类型的序列化和提升内存管理的性能,所以这里做了点小的修改。
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());
我们这里有部分老的代码无法直接改成async 的模式, 因而有了如下一个混合版本的dao。老的代码少做修改,新的代码基本走到async 的模式。
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; } .................................... }
保留老的Collection 和新的NewCollection。
public class Dao<TEntity> : IDisposable where TEntity : BaseEntity
{
protected IMongoCollection
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); }
IMongoDatabase IMongoClient
等都没有Close方法,而且没有继承 IDispose
接口,这意味着你不需要显示的关闭数据库操作的连接。这个和以前ado的连接使用方式不同。可以直接使用单例模式,不必频繁的去实例化该对象,也不用代码显式的去打开和关闭数据库连接,驱动内部已经帮我们做好了连接池管理。
找钢网搜索引擎高级工程师——钱露晗