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限制其他限制

posted @ 2019-08-07 12:45  万箭穿心,习惯就好。  阅读(442)  评论(0编辑  收藏  举报