驱动:Official .NET driver for MongoDB
版本:2.5.0
mongodb内部是用Bson格式存储的,与json大致类似但有区别,因此它也原生支持json串语法格式进行操作,
在C#版本驱动中就有BsonDucument类来处理json串,
所以大多驱动接口的泛型参数TDocument类型都可以用BsonDucument,即直接用BsonDucument来处理json和bson进行增删改查。
但是我不喜欢在C#语言还去写json,总觉得这样做以后比较难维护,所以尽量用C#语法本身去操作。
得益于驱动提供了BsonSerializer类,能够将C#中的大多数自定义class对象进行序列化与反序列化。
所以TDocument泛型参数不受限于BsonDucument,可以是任何自定义的类对象,
它内部可以将自定义的类对象序列化成BsonDucument,也可以反序列化,于是就可以在自定义对象层面操作mongodb了。
先简单做个封装类:
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using MongoDB.Bson.Serialization.Attributes;
public class MongoDbHelper
{
private readonly IMongoDatabase _database;
private MongoDbHelper(string connectionString, string databaseName)
{
var client = new MongoClient(connectionString);
_database = client.GetDatabase(databaseName);
}
public Task Close()
{
return Task.CompletedTask;
}
public async Task EnsureIndexes<TC,TD>(IEnumerable<string> keys)
{
var collection = _database.GetCollection<TD>(typeof(TC).Name);
await collection.Indexes.CreateManyAsync(keys.Select(key => new CreateIndexModel<TD>($"{{{key}:1}}")));
}
public Task<long> CountAsync<T>(string grainType, Expression<Func<T, bool>> whereFilter)
{
var collection = _database.GetCollection<T>(grainType);
return collection.CountAsync(whereFilter == null ? FilterDefinition<T>.Empty :
Builders<T>.Filter.Where(whereFilter));
}
public Task WriteData<T>(string collectionName, IEnumerable<T> data)
{
var collection = _database.GetCollection<BsonDocument>(collectionName);
collection.InsertManyAsync(data.Select(d => d.ToBsonDocument())); //对象可以直接转Bson
return Task.CompletedTask;
}
//TC用来指明数据库的Collection名,相当于sql表名;
//TD类型用来指明文档序列化对象,相当于sql记录所对应的对象
public Task WriteData<TC,TD>(IEnumerable<TD> data)
{
return WriteData<TD>(typeof(TC).Name, data);
}
//T类型用来指明文档序列化对象,相当于sql记录所对应的对象
public async Task<List<T>> Query<T>(string collectionName, Expression<Func<T, bool>> whereFilter)
{
try
{
var collection = _database.GetCollection<T>(collectionName);
var filterDefinition = whereFilter == null
? FilterDefinition<T>.Empty
: Builders<T>.Filter.Where(whereFilter);
var projection = Builders<T>.Projection.Exclude("_id");
var findoptions = new FindOptions<T>() { Projection = projection };
var cursor = await collection.FindAsync(filterDefinition, findoptions);
return cursor.ToEnumerable().ToList();
}
catch (Exception e)
{
Console.WriteLine(e);
}
return null;
}
public Task<List<TD>> Query<TC, TD>(Expression<Func<TD, bool>> whereFilter)
{
return Query(typeof(TC).Name, whereFilter);
}
private async Task<List<TO>> AggregateQuery<TI, TO>(string collectionName, IEnumerable<IPipelineStageDefinition> pipelineStage)
{
try
{
var pipeline = new PipelineStagePipelineDefinition<TI, TO>(pipelineStage);
var collection = _database.GetCollection<TI>(collectionName);
var result = await collection.AggregateAsync(pipeline);
return result.ToEnumerable().ToList();
}
catch (Exception e)
{
Console.WriteLine(e);
}
return null;
}
public Task<List<TO>> AggregateQuery<TC, TI, TO>(IEnumerable<IPipelineStageDefinition> pipelineStage)
{
return AggregateQuery<TI,TO>(typeof(TC).Name, pipelineStage);
}
/// <summary>
///
/// </summary>
/// <typeparam name="TC">collection名称类</typeparam>
/// <typeparam name="TI">输入文档类</typeparam>
/// <typeparam name="TG">group by字段类型</typeparam>
/// <typeparam name="TO">输出类型</typeparam>
/// <param name="match"></param>
/// <param name="groupby"></param>
/// <param name="groupValue"></param>
/// <returns></returns>
public async Task<List<TO>> AggregateQuery<TC,TI,TG,TO>(Expression<Func<TI, bool>> match,
Expression<Func<TI, TG>> groupby,
Expression<Func<IGrouping<TG, TI>, TO>> groupValue)
{
var pipelineStageDefinitions = new IPipelineStageDefinition[]
{
PipelineStageDefinitionBuilder.Match(match),
PipelineStageDefinitionBuilder.Group(
groupby,
groupValue)
};
return await AggregateQuery<TC, TI, TO>(pipelineStageDefinitions);
}
private async Task<List<T>> AggregateQuery<T>(string collectionName, IEnumerable<IPipelineStageDefinition> pipelineStage)
{
try
{
var pipeline = new PipelineStagePipelineDefinition<BsonDocument, BsonDocument>(pipelineStage);
var collection = _database.GetCollection<BsonDocument>(collectionName);
var result = await collection.AggregateAsync(pipeline);
return result.ToEnumerable().Select(b => BsonSerializer.Deserialize<T>(b)).ToList();
}
catch (Exception e)
{
Console.WriteLine(e);
}
return null;
}
public Task<List<TO>> AggregateQuery<TC,TO>(IEnumerable<string> pipelineStageJson)
{
return AggregateQuery<TO> (typeof(TC).Name,pipelineStageJson.Select(
json => new JsonPipelineStageDefinition<BsonDocument,BsonDocument>(json)
as IPipelineStageDefinition).ToList());
}
}
自定义一个类:
public class Recharge
{
public long UserId { get; set; }
public ulong Amount { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime Time { get; set; } = DateTime.Now;
}
先打开个数据库连接:
//连接串和数据库名参数按自己的实际情况填
var helper = new MongoDbHelper("XXXX","YYYY");
写数据:
//写入多条数据
await helper.WriteData<Recharge, Recharge>(new[]
{
new Recharge() {UserId = 1, Amount = 100},
new Recharge() {UserId = 1, Amount = 200},
new Recharge() {UserId = 2, Amount = 500},
new Recharge() {UserId = 2, Amount = 1000},
new Recharge() {UserId = 3, Amount = 800},
new Recharge() {UserId = 4, Amount = 100},
});
查询数据:
//按id筛选
var list1 = await helper.Query<Recharge, Recharge>(recharge=>recharge.UserId == 1);
//按时间筛选
var list2 = await helper.Query<Recharge, Recharge>(
recharge => recharge.Time >= DateTime.MinValue &&
recharge.Time <= DateTime.Now);
//按金额筛选
var list3 = await helper.Query<Recharge, Recharge>(
recharge => recharge.Amount >= 500);
注:开始新手的我以为查询过程是:先将bson文档反序列化成C#对象,再调用C#的lambda进行筛选。如果直接这样,效率肯定很低,相当于总是要全盘扫描。而实际上是驱动内部把lambda函数解析成了Expression表达式,再解析翻译为了原生json串查询语法,相较于原本的json语法操作,只是牺牲了点翻译时间。
统计查询:
//先定义一个统计类对象
public class RechargeStat
{
public long UserId { get; set; }
public ulong Amount { get; set; }
}
//统计所有人的总充值
var stats = await helper.AggregateQuery<Recharge, Recharge, long, RechargeStat>(
recharge=>true, //直接返回true,即不筛选
recharge=>recharge.UserId,//按UserId统计,相当于sql的group by UserId
group =>new RechargeStat()
{
UserId = group.Key,
Amount = (ulong)group.Sum(v=>(double)v.Amount)
}
);