• MongoDB


    安装使用

    小插曲:项目属性-生成-高级,发现vs2013只支持到C#5.0
    C#6.0在vs2015引入,或在vs2013基础上从https://github.com/dotnet/roslyn下载roslync包

    MongoDB Client

    RoboMongo --> Robo 3T
    RoboMongo镜像地址:http://dl.mongodb.org/dl/win32/x86_64
    Robo 3T下载地址:https://robomongo.org/download
    可分别参考:使用教程1, 使用教程2

    .Net MongoDB

    驱动下载:https://github.com/mongodb/mongo-csharp-driver/releases,或直接在 nuget.net 中下载包
    MongoDB .Net文档:.NET MongoDB Driver 2.2 API注释
    版本兼容一览表:MongoDB C#/.NET Driver

    Robo 3T

    常用命令汇总

    db.getCollection('collectName').find({'UID':/^2020-03-25$/}) //正则匹配
    
    

    基础概念

    查询
    若类对象T中无_id字段,FindOneAs方法会抛异常:Element '_id' doesnot match any field of class T

    var conventionPack = new ConventionPack { new IgnoreExtraElementsConvention(true) };
    ConventionRegistry.Register("IgnoreExtraElements", conventionPack, type => true);
    

    查询指定字段

    FilterDefinitionBuilder<BsonDocument> builderFilter = Builders<BsonDocument>.Filter;
    ProjectionDefinitionBuilder<BsonDocument> builderProjection = Builders<BsonDocument>.Projection;
    ProjectionDefinition<BsonDocument> projection = builderProjection.Include("_fieldName1").Exclude("_fieldName2");
    var result = coll.Find<BsonDocument>(builderFilter.Empty).Project(projection).ToList();		  
    //Linq查询
    var result = from y in coll.AsQueryable() 
    			 select new { _fieldName1 = y["_fieldName1"], _fieldName2 = y["_fieldName2"] };
    var result = from y in coll.AsQueryable() group y 
    			 by y["_fieldName1"] into s 
    			 select new { _fieldName1 = s.Key, Count = s.Count() };
    

    将BsonDocument相关对象转换为具体类T对象:扩展方法

    public static IList<T> ToEntityList<T>(this IEnumerable<BsonDocument> bsons) {
        IList<T> list = new List<T>();
        foreach (var item in bsons) {
            list.Add(BsonSerializer.Deserialize<T>(item));
        }
        return list;
    }
    

    新增

    • insert
    • save

    insert可以一次性插入一个列表,不用遍历、效率高, save需要遍历列表、一个个插入。
    关于insert和save的区别:https://blog.csdn.net/xiaojin21cen/article/details/40480609
    注意,文中强调的是自有的主键_id。若插入数据中有自定义为Unique索引的字段,insert和save均会抛异常键重复。

    更新

    • update
    • replace

    删除

    此处提供一个简单的MongoDB工具类

    private static MongoServer server = MongoServer.Create("connectionString");
    private static MongoDatabase database {
    	get {
    		if (server == null) return null;
    		server.Connect(TimeSpan.FromSeconds(5));
    		if (!server.DatabaseExists(databaseName)) {
    			var databaseSettings = server.CreateDatabaseSettings(databaseName);
    			return server.GetDatabase(databaseSettings);
    		} else {
    			return server.GetDatabase(databaseName);
    		}
    	}
    }
    public static MongoCollection<BsonDocument> GetMongoCollection(string collectionName){
        if (server == null) return null;
        using (server.RequestStart(database)) {
           if (!database.CollectionExists(collectionName)) {
                database.CreateCollection(collectionName);
           }
           return database.GetCollection<BsonDocument>(collectionName);
        }
    }
    

    此处提供一个功能更丰富的工具类:参考1, 参考2, 参考3, 参考4, 参考5

    // 建议T名称与collection名称保持一致
    public class MongoUtil<T> where T : class {
    	private string _conn = string.Empty;
    	private string _dbName = string.Empty;
    	private string _collectName = String.Empty;
    
    	private MongoClient client;
    	private IMongoDatabase db;
    	private IMongoCollection<T> collection;
    
    	public MongoUtil(string connectionString, string dbName, string collectName) {
    		this._conn = connectionString;
    		this._dbName = dbName;
    		this._collectName = collectName;
    
    		this.client = new MongoClient(connectionString);
    		this.db = client.GetDatabase(dbName);
    		this.collection = db.GetCollection<T>(this._collectName);
    	}
    
    	public MongoUtil(string connectionString, string dbName) :
    		this(connectionString, dbName, typeof(T).Name) //获取类名
    	{ }
    
    	public void InsertOne(String collName, T data) {
    		this.collection.InsertOne(data);//InsertOneAsync
    	}
    	public void InsertBath(String collName, IEnumerable<T> datas) {
    		this.collection.InsertMany(datas);//InsertManyAsync
    	}
    
    	public void DeleteOneByKey(string key, object v) {
    		FilterDefinition<T> filter = Builders<T>.Filter.Eq(key, v);
    		this.collection.DeleteOne(filter, CancellationToken.None);//DeleteOneAsync
    	}
    	public void DeleteManyByKey(string key, object value) {
    		FilterDefinition<T> filter = Builders<T>.Filter.Eq(key, value);
    		collection.DeleteMany(filter);//DeleteManyAsync
    	}
    
    	public void UpdateByKey(string key, object value, T data) {
    		FilterDefinition<T> filter = Builders<T>.Filter.Eq(key, value);
    		UpdateOptions opts = new UpdateOptions() { IsUpsert = false };
    		ReplaceOneResult rlt = this.collection.ReplaceOne(filter, data, opts, CancellationToken.None);
    	}
    	public void Update(Expression<Func<T, Boolean>> filter, UpdateDefinition<T> update, Boolean upsert = false) {
    		var result = this.collection.UpdateMany(filter, update, new UpdateOptions { IsUpsert = upsert });
    	}
    	public void UpdateByKey(string k, object v, string key2, object value2) {
    		FilterDefinition<T> filter = Builders<T>.Filter.Eq(k, v);
    		var updateDefinition = Builders<T>.Update.Set(key2, value2);
    		var model = new UpdateOneModel<T>(filter, updateDefinition) { IsUpsert = false };
    
    		List<WriteModel<T>> requests = new List<WriteModel<T>>();
    		requests.Add(model);
    		BulkWriteResult<T> bulkWriteResult = this.collection.BulkWrite(requests);
    	}
    
    	public IFindFluent<T, T> FindByKey(string k, object v) {
    		FilterDefinition<T> filter = Builders<T>.Filter.Eq(k, v);
    		IFindFluent<T, T> findFluent = this.collection.Find(filter);
    		return findFluent;
    	}
    	public IFindFluent<T, T> FindRangeKeys(string k, string _start, string _end) {
    		var filterDefinition = Builders<T>.Filter.Gte(k, _start) & Builders<T>.Filter.Lt(k, _end);
    		var findFluent = collection.Find(filterDefinition);
    		return findFluent;
    	}
    	public IFindFluent<T, T> FindRangeKeys(string key, string[] uids) {
    		var filterDefinition = Builders<T>.Filter.In(key, uids);
    		return collection.Find(filterDefinition);
    	}
    	public IFindFluent<T, T> FindByUids(string key, string[] uids, int limit, int skip) {
    		var filterDefinition = Builders<T>.Filter.In(key, uids);
    		return collection.Find(filterDefinition).Limit(limit).Skip(skip).Sort(Builders<T>.Sort.Descending(key));
    	}
    	public IFindFluent<T, T> FindByRegex(string key, List<string> orValue) {
    		StringBuilder sb = new StringBuilder().Append("{'$or':[ ");
    		for (int i = 0; i < orValue.Count; i++) {
    			//i表示包括,最后一位加逗号不会报错
    			sb.Append("{'" + key + "':{$regex: '" + orValue[i] + "', $options:'i'}},"); 
    		}
    		sb.Append("]}").Append(".limit(" + 10 + ").skip(" + 0 + ")");
    
    		BsonDocument bsonDocumnet = BsonSerializer.Deserialize<BsonDocument>(sb.ToString());
    		return collection.Find(bsonDocumnet);
    	}
    }
    

    注意,MongoServer已废弃,推荐Mongo客户端MongoClient
    提供几个异步方式示例:参考

    public async Task<T> FindByBsonAsync(BsonDocument bson){
         return await this.collection.Find(new BsonDocument()).FirstOrDefaultAsync();
    }
    
    // 若返回的数据预期很大,建议采用以下异步的迭代的方法处理。
    await collection.Find(new BsonDocument()).ForEachAsync(d => Console.WriteLine(d));
    // 若用同步方法,可以采用ToEnumerable适配器方法
    var cursor = collection.Find(new BsonDocument()).ToCursor();
    foreach (var document in cursor.ToEnumerable()) { ... }
    

    提供一个分页查询示例

    public List<T> FindListByPage(FilterDefinition<T> filter, int pageIndex, int pageSize, out long count,
    	string[] field = null, SortDefinition<T> sort = null) {
    	try {
    		count = this.collection.CountDocuments(filter);
    		if (field == null || field.Length == 0) { //不指定查询字段
    			if (sort == null)
    				return this.collection.Find(filter).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToList();
    			return this.collection.Find(filter).Sort(sort).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToList();
    		} else { //指定查询字段
    			var _fieldLst = new List<ProjectionDefinition<T>>();
    			for (int i = 0; i < field.Length; i++) {
    				_fieldLst.Add(Builders<T>.Projection.Include(field[i].ToString()));
    			}
    			var projection = Builders<T>.Projection.Combine(_fieldLst);
    			_fieldLst?.Clear();
    
    			if (sort == null)
    				return this.collection.Find(filter).Project<T>(projection).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToList();
    			return this.collection.Find(filter).Project<T>(projection).Sort(sort).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToList();
    		}
    	} catch (Exception ex) { throw ex; }
    }
    
    

    MongoDB资料系列bulkWrite操作

    问题解决

    问题1:保存DateTime类型到Mongo后,日期比存入的日期要小
    原因:Mongo会将时间保存成UCT时间,即格林威治时间,比北京时间要晚8小时
    解决:在时间属性上加标签[BsonDateTimeOptions(Kind = DateTimeKind.Local)]或注册

    BsonSerializer.RegisterSerializer(typeof(DateTime),
    	new DateTimeSerializer(DateTimeSerializationOptions.LocalInstance));
    

    具体参见:http://www.voidcn.com/article/p-tanzovjm-bsx.html
    问题2:在.Net中写enum类型对象到MongoDB,会存储为Int32类型
    解决:方法1方法2
    但是,并不影响程序调用和解析。
    问题3:调用Save()方法,提示报错:

    No IdGenerator found.  在 MongoDB.Driver.MongoCollection.Save(Type nominalType, Object document, MongoInsertOptions options)
    在 MongoDB.Driver.MongoCollection.Save(Type nominalType, Object document)
    在 MongoDB.Driver.MongoCollection.Save[TNominalType](TNominalType document)
    在 CMB.ScheduleTask.MongoHelper.SaveOne[T](MongoServerSettings connString, String dbName, String collectionName, T entity)
    

    解决:entity中必须要有_id字段。另,MongoDB驱动新版(v2.7.0等)均已去掉Save()方法
    问题4:MongoDB(v3.4)连接初始化报错(MongoDB Driver v2.8.1)

    Multiple custom attributes of the same type found.    
    at System.Attribute.GetCustomAttribute(Assembly element, Type attributeType, Boolean inherit)
    at System.Runtime.InteropServices.RuntimeInformation.get_FrameworkDescription()  [4.3.0] 
    at System.Lazy`1.CreateValue() 
     --- End of stack trace from previous location where exception was thrown ---   
    at System.Lazy`1.get_Value()   
    at MongoDB.Driver.Core.Connections.ClientDocumentHelper.CreateClientDocument(String applicationName)   
    at MongoDB.Driver.Core.Connections.BinaryConnectionFactory..ctor(ConnectionSettings settings, IStreamFactory streamFactory, IEventSubscriber eventSubscriber)   
    at MongoDB.Driver.Core.Configuration.ClusterBuilder.CreateConnectionPoolFactory()   
    at MongoDB.Driver.Core.Configuration.ClusterBuilder.CreateServerFactory()   
    at MongoDB.Driver.Core.Configuration.ClusterBuilder.CreateClusterFactory()   
    at MongoDB.Driver.ClusterRegistry.CreateCluster(ClusterKey clusterKey)   
    at MongoDB.Driver.ClusterRegistry.GetOrCreateCluster(ClusterKey clusterKey)  
    at MongoDB.Driver.MongoClient..ctor(MongoClientSettings settings)
    at Tool.MongoDBHelper`1..ctor(String collectionName)
    

    解决:先降版本至2.7.0。可参考:由System.Runtime.InteropServices.RuntimeInformation.dll引发的MongoDB连接问题
    问题5:提示Size 27105946 is large than MaxDocumentSize 16777216
    因为bson文档大小最大限制为16MB,确保单个文档不会使用过多RAM,或在迁移期间不会占用过多的带宽。
    参见:MongoDB限制其他限制

  • 相关阅读:
    qiankun 报错:Target container with #container not existed while xxx mounting!
    promise加载队列实现
    promise 封装定时器
    关于promise
    节流防抖
    箭头函数特点
    this
    手写apply
    手写call
    手写bind函数
  • 原文地址:https://www.cnblogs.com/wjcx-sqh/p/11314615.html
Copyright © 2020-2023  润新知