zoukankan      html  css  js  c++  java
  • 基于MongoDB.Driver的扩展

      由于MongoDB.Driver中的Find方法也支持表达式写法,结合【通用查询设计思想】这篇文章中的查询思想,个人基于MongoDB扩展了一些常用的方法。

      首先我们从常用的查询开始,由于MongoDB.Driver支持类似于AutoMapper返回的指定属性(Project<TDto>方法),所以这里都是基于泛型的扩展

      查询

          /// <summary>
            /// 同步查询指定条件的数据,并且返回指定类型TDto
            /// </summary>
            /// <typeparam name="TEntity">查询实体</typeparam>
            /// <typeparam name="TDto">返回类型</typeparam>
            /// <param name="source"></param>
            /// <param name="query"></param>
            public static IFindFluent<TEntity, TDto> FindSync<TEntity, TDto>(this IMongoCollection<TEntity> source, IQuery<TEntity> query)
                where TEntity : class
            {
                var projection = GetTDtoReturnProperties<TEntity, TDto>();
    
                var expression = query?.GenerateExpression();
                if (null == expression)
                {
                    var emptyExpression = Builders<TEntity>.Filter.Empty;
                    return source.Find(emptyExpression).Project<TDto>(projection);
                }
    
                return source.Find(expression).Project<TDto>(projection);
            }
    
           /// <summary>
            /// 获取指定的返回列
            /// </summary>
            /// <typeparam name="TEntity"></typeparam>
            /// <typeparam name="TDto"></typeparam>
            /// <returns></returns>
            private static ProjectionDefinition<TEntity, TDto> GetTDtoReturnProperties<TEntity, TDto>()
                where TEntity : class
            {
                var returnType = typeof(TDto);
    
                var fieldList = new List<ProjectionDefinition<TEntity>>();
                foreach (var property in returnType.GetProperties())
                {
                    fieldList.Add(Builders<TEntity>.Projection.Include(property.Name));
                }
    
                return Builders<TEntity>.Projection.Combine(fieldList);
            }

      这里主要是利用了IQuery接口中的GenerateExpression方法,如果前端传来了查询参数,则拼装返回我们的表达式,如果没有,默认返回一个空的Filter,再通过Project<TDto>映射关系到TDto上。

      排序

         /// <summary>
            /// 排序方法
            /// </summary>
            /// <typeparam name="TEntity"></typeparam>
            /// <typeparam name="TDto"></typeparam>
            /// <param name="source"></param>
            /// <param name="sortInfo"></param>
            /// <returns></returns>
            public static IFindFluent<TEntity, TDto> Sort<TEntity, TDto>(this IFindFluent<TEntity, TDto> source, ISortInfo sortInfo)
                where TEntity : class
            {
                var sort = Builders<TEntity>.Sort;
                SortDefinition<TEntity> 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稍微不一样,大家结合实际情况来更改业务即可。

      分页

        /// <summary>
            /// 查询指定条件的数据
            /// </summary>
            /// <typeparam name="TEntity">查询的实体</typeparam>
            /// <typeparam name="TDto">返回的类型</typeparam>
            /// <param name="source"></param>
            /// <param name="query">查询条件</param>
            /// <param name="page">分页信息</param>
            public static async Task<PageResult<TDto>> ToPageResultAsync<TEntity, TDto>(this IMongoCollection<TEntity> source, IQuery<TEntity> query, IPageInfo page)
                where TEntity : class
            {
                var pageIndex = Math.Max(0, page.PageIndex);
                var pageSize = Math.Max(1, page.PageSize);
    
                var cursor = source.FindSync<TEntity, TDto>(query);
    
                var pageResult = new PageResult<TDto>
                {
                    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
    
            /// <summary>  
            /// 查询一个集合中的所有数据 其集合的名称为T的名称  
            /// </summary>  
            /// <typeparam name="TEntity">该集合数据的所属类型</typeparam>
            /// <typeparam name="TDto">返回类型</typeparam>
            /// <returns>返回一个Result<TDto />
            /// </returns>  
            public static async Task<Result<TDto>> QueryAsync<TEntity, TDto>(IQuery<TEntity> query, string collectionName = "")
                where TEntity : class
            {
                //检查是否存在该文档
                var existed = await CollectionExists(Db, string.IsNullOrWhiteSpace(collectionName) ? DbName : collectionName);
                if (existed)
                {
                    var collection = Db.GetCollection<TEntity>(string.IsNullOrWhiteSpace(collectionName) ? DbName : collectionName);
                    var result = collection.FindSync<TEntity, TDto>(query);
                    var listResult = await result.ToListAsync();
                    return Result.FromData(listResult.FirstOrDefault());
                }
                else
                {
                    return Result.FromCode<TDto>(ResultCode.NoSuchCollection);
                }
            }
    
            /// <summary>  
            /// 查询一个集合中的所有数据 其集合的名称为T的名称  
            /// </summary>  
            /// <typeparam name="TEntity">该集合数据的所属类型</typeparam>
            /// <typeparam name="TDto">返回数据类型</typeparam>
            /// <returns>返回一个List列表</returns>  
            public static async Task<Result<List<TDto>>> QueryListAsync<TEntity, TDto>(IQuery<TEntity> query, string collectionName = "")
                where TEntity : class
            {
                var collection = Db.GetCollection<TEntity>(string.IsNullOrWhiteSpace(collectionName) ? DbName : collectionName);
                var result = collection.FindSync<TEntity, TDto>(query);
                var listResult = await result.ToListAsync();
                return Result.FromData(listResult);
            }
    
            /// <summary>
            /// 分页方法
            /// </summary>
            /// <typeparam name="TEntity"></typeparam>
            /// <typeparam name="TDto"></typeparam>
            /// <param name="collectionName">指定文档名称</param>
            /// <param name="query"></param>
            /// <returns></returns>
            public static async Task<PageResult<TDto>> QueryPageResultAsync<TEntity, TDto>(PageQuery<TEntity> query, string collectionName = "")
                where TEntity : class
            {
                //检查是否存在该文档
                var existed = await CollectionExists(Db, string.IsNullOrWhiteSpace(collectionName) ? DbName : collectionName);
                if (existed)
                {
                    var collection = Db.GetCollection<TEntity>(string.IsNullOrWhiteSpace(collectionName) ? DbName : collectionName);
                    return await collection.ToPageResultAsync<TEntity, TDto>(query, query);
                }
                else
                {
                    return new PageResult<TDto>
                    {
                        Code = ResultCode.NoSuchCollection
                    };
                }
            }
    
            #endregion
    
            #region Private Methods
    
            /// <summary>
            /// 检查是否存在该文档
            /// </summary>
            /// <param name="database">指定的数据库</param>
            /// <param name="collectionName">文档名称</param>
            /// <returns></returns>
            private static async Task<bool> CollectionExists(IMongoDatabase database, string collectionName)
            {
                var options = new ListCollectionsOptions
                {
                    Filter = Builders<BsonDocument>.Filter.Eq("name", collectionName)
                };
    
                return await database.ListCollections(options).AnyAsync();
            }
            #endregion
        }

      有了这个帮助类,我们可以看一下应用层的具体应用

          /// <summary>
            /// TEntity的分页查询
            /// </summary>
            /// <typeparam name="TEntity">查询实体</typeparam>
            /// <typeparam name="TDto">返回结果</typeparam>
            /// <param name="query">查询条件</param>
            /// <returns></returns>
            public async Task<PageResult<TDto>> GetPagedLogsAsync<TEntity, TDto>(PageQuery<TEntity> query)
                where TEntity : class
            {
                return await MongoHelper.QueryPageResultAsync<TEntity, TDto>(query, typeof(TEntity).Name);
            }
    
            /// <summary>
            /// TEntity的查询
            /// </summary>
            /// <typeparam name="TEntity">查询实体</typeparam>
            /// <typeparam name="TDto">返回结果</typeparam>
            /// <param name="query">查询条件</param>
            /// <returns></returns>
            public async Task<Result<TDto>> GetSpecifyLogAsync<TEntity, TDto>(IQuery<TEntity> query)
                where TEntity : class
            {
                return await MongoHelper.QueryAsync<TEntity, TDto>(query, typeof(TEntity).Name);
            }

      看一下我们Controller的调用

          /// <summary>
            /// 获取指定条件的日志分页列表
            /// </summary>
            /// <param name="query">查询参数</param>
            /// <returns></returns>
            [HttpPost]
            public async Task<PageResult<LogResult>> SearchPagedLogsAsync(LogPageQuery query)
            {
                return await _logBll.GetPagedLogsAsync<Log, LogResult>(query);
            }
    
            /// <summary>
            /// 获取指定条件的日志
            /// </summary>
            /// <param name="id">查询参数</param>
            /// <returns></returns>
            public async Task<Result<LogResult>> GetLogByIdAsync(string id)
            {
                if (string.IsNullOrWhiteSpace(id))
                    return Result.FromCode<LogResult>(ResultCode.Fail);
                return await _logBll.GetSpecifyLogAsync<Log, LogResult>(new Query<Log>(m => m.Id == new ObjectId(id)));
            }

       我这里的例子是为了符合单一职责的设计原则,所以指定了GetLogByIdAsync这样的单一接口,如果大家喜欢单个方法满足更多功能,可以参照【通用查询设计思想】文章中的Controller写法。

      

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

    如果你觉得该文章还不错且有所收获,请右下角推荐一下,也可留下您的问题或建议,让我们共同进步。 原创博客请在转载时保留原文链接或者在文章开头加上本人博客地址!
  • 相关阅读:
    ehcache 的 配置文件: ehcache.xml的认识
    Hibernate的二级缓存(SessionFaction的外置缓存)-----Helloword
    QBC检索和本地SQL检索
    HQL的检索方式
    HQL的第一个程序
    Ubuntu Error: No module named 'apt_pkg' 怎么办?
    Linux 后台运行python .sh等程序,以及查看和关闭后台运行程序操作
    ubuntu install redis/mongo 以及 监控安装
    Mac 上的 redis
    Mac 解决硬盘插入不能写的问题
  • 原文地址:https://www.cnblogs.com/lex-wu/p/10395136.html
Copyright © 2011-2022 走看看