由于MongoDB.Driver中的Find方法也支持表达式写法,结合【通用查询设计思想】这篇文章
中的查询思想,个人基于MongoDB扩展了一些常用的方法。
首先我们从常用的查询开始,由于MongoDB.Driver支持类似于AutoMapper返回的指定属性(Project
查询
////// 同步查询指定条件的数据,并且返回指定类型TDto /// /// 查询实体 /// 返回类型 /// /// public static IFindFluent FindSync (this IMongoCollection source, IQuery query) where TEntity : class { var projection = GetTDtoReturnProperties (); var expression = query?.GenerateExpression(); if (null == expression) { var emptyExpression = Builders .Filter.Empty; return source.Find(emptyExpression).Project (projection); } return source.Find(expression).Project (projection); } /// /// 获取指定的返回列 /// /// /// /// private static ProjectionDefinition GetTDtoReturnProperties () where TEntity : class { var returnType = typeof(TDto); var fieldList = new List >(); foreach (var property in returnType.GetProperties()) { fieldList.Add(Builders .Projection.Include(property.Name)); } return Builders .Projection.Combine(fieldList); }
这里主要是利用了IQuery接口中的GenerateExpression方法,如果前端传来了查询参数,则拼装返回我们的表达式,如果没有,默认返回一个空的Filter,再通过Project
排序
////// 排序方法 /// /// /// /// /// /// public static IFindFluent Sort (this IFindFluent source, ISortInfo sortInfo) where TEntity : class { var sort = Builders .Sort; SortDefinition sortDefinition = null; if (sortInfo != null) { if (!string.IsNullOrWhiteSpace(sortInfo.Order) && !string.IsNullOrWhiteSpace(sortInfo.Field)) { if (sortInfo.Order.Contains("asc")) sortDefinition = sort.Ascending(sortInfo.Field); if (sortInfo.Order.Contains("desc")) sortDefinition = sort.Descending(sortInfo.Field); } } return source.Sort(sortDefinition); }
这里前端是使用了LayUI的表格,所以API中的参数是Order和Field,我们也可以结合例如JQuery的DataTable或者其他框架的表格,只是参数SortInfo稍微不一样,大家结合实际情况来更改业务即可。
分页
////// 查询指定条件的数据 /// /// 查询的实体 /// 返回的类型 /// /// 查询条件 /// 分页信息 public static async Task > ToPageResultAsync (this IMongoCollection source, IQuery query, IPageInfo page) where TEntity : class { var pageIndex = Math.Max(0, page.PageIndex); var pageSize = Math.Max(1, page.PageSize); var cursor = source.FindSync (query); var pageResult = new PageResult { PageIndex = pageIndex, PageSize = pageSize, TotalCount = (int)await cursor.CountDocumentsAsync(), Data = await cursor.Skip(pageSize * (pageIndex - 1)).Limit(pageSize).Sort(page).ToListAsync() }; return pageResult; }
增和删暂时不写了,官方API也提供了批处理的接口,都可以直接用的。
来看一下我们这几个扩展方法的应用(基于aspnet core)
public class MongoHelper { private static readonly string DbName = ConfigHelper.GetSetting("MongoDb").ToString(); private static readonly string ConnStr = ConfigHelper.GetSetting("MongoDbConnStr").ToString(); private static IMongoDatabase Db { get; } private static readonly object LockHelper = new object(); #region cotr static MongoHelper() { if (Db == null) { lock (LockHelper) { if (Db == null) { var client = new MongoClient(ConnStr); Db = client.GetDatabase(DbName); } } } } #endregion #region query ////// 查询一个集合中的所有数据 其集合的名称为T的名称 /// /// 该集合数据的所属类型 /// 返回类型 /// 返回一个Result /// public static async Task > QueryAsync (IQuery query, string collectionName = "") where TEntity : class { //检查是否存在该文档 var existed = await CollectionExists(Db, string.IsNullOrWhiteSpace(collectionName) ? DbName : collectionName); if (existed) { var collection = Db.GetCollection (string.IsNullOrWhiteSpace(collectionName) ? DbName : collectionName); var result = collection.FindSync (query); var listResult = await result.ToListAsync(); return Result.FromData(listResult.FirstOrDefault()); } else { return Result.FromCode (ResultCode.NoSuchCollection); } } /// /// 查询一个集合中的所有数据 其集合的名称为T的名称 /// /// 该集合数据的所属类型 /// 返回数据类型 /// 返回一个List列表 public static async Task >> QueryListAsync (IQuery query, string collectionName = "") where TEntity : class { var collection = Db.GetCollection (string.IsNullOrWhiteSpace(collectionName) ? DbName : collectionName); var result = collection.FindSync (query); var listResult = await result.ToListAsync(); return Result.FromData(listResult); } /// /// 分页方法 /// /// /// /// 指定文档名称 /// /// public static async Task > QueryPageResultAsync (PageQuery query, string collectionName = "") where TEntity : class { //检查是否存在该文档 var existed = await CollectionExists(Db, string.IsNullOrWhiteSpace(collectionName) ? DbName : collectionName); if (existed) { var collection = Db.GetCollection (string.IsNullOrWhiteSpace(collectionName) ? DbName : collectionName); return await collection.ToPageResultAsync (query, query); } else { return new PageResult { Code = ResultCode.NoSuchCollection }; } } #endregion #region Private Methods /// /// 检查是否存在该文档 /// /// 指定的数据库 /// 文档名称 /// private static async Task<bool> CollectionExists(IMongoDatabase database, string collectionName) { var options = new ListCollectionsOptions { Filter = Builders .Filter.Eq("name", collectionName) }; return await database.ListCollections(options).AnyAsync(); } #endregion }
有了这个帮助类,我们可以看一下应用层的具体应用
////// TEntity的分页查询 /// /// 查询实体 /// 返回结果 /// 查询条件 /// public async Task > GetPagedLogsAsync (PageQuery query) where TEntity : class { return await MongoHelper.QueryPageResultAsync (query, typeof(TEntity).Name); } /// /// TEntity的查询 /// /// 查询实体 /// 返回结果 /// 查询条件 /// public async Task > GetSpecifyLogAsync (IQuery query) where TEntity : class { return await MongoHelper.QueryAsync (query, typeof(TEntity).Name); }
看一下我们Controller的调用
////// 获取指定条件的日志分页列表 /// /// 查询参数 /// [HttpPost] public async Task > SearchPagedLogsAsync(LogPageQuery query) { return await _logBll.GetPagedLogsAsync (query); } /// /// 获取指定条件的日志 /// /// 查询参数 /// public async Task > GetLogByIdAsync(string id) { if (string.IsNullOrWhiteSpace(id)) return Result.FromCode (ResultCode.Fail); return await _logBll.GetSpecifyLogAsync (new Query (m => m.Id == new ObjectId(id))); }
我这里的例子是为了符合单一职责的设计原则,所以指定了GetLogByIdAsync这样的单一接口,如果大家喜欢单个方法满足更多功能,可以参照【通用查询设计思想】文章中的Controller写法。
让我知道你们有更好的想法!