基于MongoDB.Driver的扩展

  由于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映射关系到TDto上。

  排序

     /// 
        /// 排序方法
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        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写法。

  

  让我知道你们有更好的想法!

 

你可能感兴趣的:(基于MongoDB.Driver的扩展)