基于SqlSugar的开发框架循序渐进介绍(27)-- 基于MongoDB的数据库操作整合

科技资讯 投稿 6500 0 评论

基于SqlSugar的开发框架循序渐进介绍(27)-- 基于MongoDB的数据库操作整合

1、MongDB的简单介绍

MongoDB是一款由C++编写的高性能、开源、无模式的常用非关系型数据库产品,是非关系数据库当中功能最丰富、最像关系数据库的数据库。它扩展了关系型数据库的众多功能,例如:辅助索引、范围查询、排序等。 

MongoDB 最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。并且MongoDB-4.2版本开始已经支持分布式事务功能。

    1)MongoDB中的 database 有着和我们熟知的"数据库"一样的概念 (对 Oracle 来说就是 schema。一个 MongoDB 实例中,可以有零个或多个数据库,每个都作为一个高等容器,用于存储数据。

collections (集合。集合和传统意义上的 table 基本一致,可以简单的把两者看成是一样的东西。

documents (文档组成。同样,一个文档可以看成是一 row

fields (字段组成。,对应的就是关系数据库的 columns

Indexes (索引在 MongoDB 中扮演着和它们在 RDBMS 中一样的角色,都是为了提高查询的效率。

Cursors (游标和上面的五个概念都不一样,但是它非常重要,并且经常被忽视,其中最重要的你要理解的一点是,游标是当你问 MongoDB 拿数据的时候,它会给你返回一个结果集的指针而不是真正的数据,这个指针我们叫它游标,我们可以拿游标做我们想做的任何事情,比如说计数或者跨行之类的,而无需把真正的数据拖下来,在真正的数据上操作。

BSON格式

Bson可以做为网络数据交换的一种存储形式,这个有点类似于Google的Protocol Buffer,但是Bson是一种schema-less的存储形式,它的优点是灵活性高,但它的缺点是空间利用率不是很理想,Bson有三个特点:轻量性、可遍历性、高效性,

MongDB数据库本身支持多种开发语言的驱动,MongoDB有官方的驱动如下:

    4字节:UNIX时间戳
  • 3字节:表示运行MongoDB的机器
  • 2字节:表示生成此_id的进程
  • 3字节:由一个随机数开始的计数器生成的值

 

2、基于MongoDB数据库的封装处理

    /// <summary>
    /// 基于MongoDB的实体类基类
    /// </summary>
    public class BaseMongoEntity : Entity<string>
    {
        [BsonId]
        [BsonRepresentation(BsonType.ObjectId]
        public override string Id { get; set; }
    }

相应的,我们根据常规数据库的基类接口名称,在处理MongoDB数据库的操作接口的时候,名称保持一致性。

其他接口定义类似的处理即可。

    /// <summary>
    /// MongoDB基础仓储实现
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public abstract class BaseMongoService<TEntity, TGetListInput> : IBaseMongoService<TEntity, TGetListInput> 
        where TEntity : class, IEntity<string>, new(
        where TGetListInput : IPagedAndSortedResultRequest
    {
        protected readonly IMongoDBContext mongoContext = NullMongoDBContext.Instance;//空实现
        protected IMongoCollection<TEntity> collection;           //强类型对象集合
        protected IMongoCollection<BsonDocument> bsonCollection; //弱类型集合BsonDocument集合
        /// <summary>
        /// 当前Api用户信息
        /// </summary>
        public IApiUserSession CurrentApiUser { get; set; } = NullApiUserSession.Instance;//空实现

        /// <summary>
        /// 构造函数
        /// </summary>
        protected BaseMongoService(
        {
            //如果SerivcePovider已经设置值,则获得注入的接口对象
            if (ServiceLocator.SerivcePovider != null
            {
                CurrentApiUser = ServiceLocator.GetService<IApiUserSession>(;
                mongoContext = ServiceLocator.GetService<IMongoDBContext>(;
                collection = mongoContext.GetCollection<TEntity>(typeof(TEntity.Name;//强类型对象集合
                bsonCollection = mongoContext.GetCollection<BsonDocument>(typeof(TEntity.Name;//弱类型集合BsonDocument集合
            }
        }
        /// <summary>
        /// 获取所有记录
        /// </summary>
        /// <returns></returns>
        public virtual async Task<ListResultDto<TEntity>> GetAllAsync(
        {
            var all = await collection.FindAsync(Builders<TEntity>.Filter.Empty;
            var list = await all.ToListAsync(;
            return new ListResultDto<TEntity>(
            {
                Items = list
            };
        }

我们通过构建对应的强类型Collection和弱类型Collection,来操作实体类和BsonDocument的相关操作的。其中的上下文对象,参考随笔《NoSQL – MongoDB Repository Implementation in .NET Core with Unit Testing example》进行的处理。

/// <summary> /// MongoDB 上下文对象 /// </summary> public class MongoDBContext : IMongoDBContext { private IMongoDatabase _db { get; set; } private MongoClient _mongoClient { get; set; } public IClientSessionHandle Session { get; set; } public MongoDBContext(IOptions<Mongosettings> configuration { _mongoClient = new MongoClient(configuration.Value.Connection; _db = _mongoClient.GetDatabase(configuration.Value.DatabaseName; } /// <summary> /// 获取强类型集合对象 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <param name="name"></param> /// <returns></returns> public IMongoCollection<T> GetCollection<T>(string name where T : class, new( { return _db.GetCollection<T>(name; } } public interface IMongoDBContext { IMongoCollection<T> GetCollection<T>(string name where T : class, new(; }

通过IOptions 方式我们注入对应的MongoDB数据库配置信息,在appsettings.json中添加根节点内容。

"MongoSettings": { "Connection": "mongodb://localhost:27017/", //MongoDB连接字符串 "DatabaseName": "iqidi" //MongoDB数据库名称 },

我们在启动Web API的时候,在Program.cs 代码中配置好就可以了。

//MongoDB配置 builder.Services.Configure<Mongosettings>(builder.Configuration.GetSection("MongoSettings";

默认初始化的IMongoDBContext是一个空接口,我们可以在Web API启动的时候,指定一个具体的实现就可以了

//添加IMongoContext实现类 builder.Services.AddSingleton<IMongoDBContext, MongoDBContext>(;

对于基类接口,分页查询获取对应列表数据,是常规的处理方式,默认需要排序、分页,返回对应的数据结构,如下代码所示。

/// <summary> /// 根据条件获取列表 /// </summary> /// <param name="input">分页查询条件</param> /// <returns></returns> public virtual async Task<PagedResultDto<TEntity>> GetListAsync(TGetListInput input { var query = CreateFilteredQueryAsync(input; var totalCount = await query.CountAsync(; //排序处理 query = ApplySorting(query, input; //分页处理 query = ApplyPaging(query, input; //获取列表 var list = await query.ToListAsync(; return new PagedResultDto<TEntity>( totalCount, list ; }

其中PagedResultDto 是我们SqlSugar开发框架参照ABP框架定义一个数据结构,包含一个TotalCount数量和一个Items的对象集合。而其中 CreateFilteredQueryAsync 是定义的一个可供业务子类重写的函数,用来处理具体的查询条件。在基类BaseMongoService中只是提供一个默认的可查询对象。

/// <summary> /// 留给子类实现过滤条件的处理 /// </summary> /// <returns></returns> protected virtual IMongoQueryable<TEntity> CreateFilteredQueryAsync(TGetListInput input { return collection.AsQueryable(; }

例如,对于一个具体的业务对象操作类,CustomerService的定义如下所示,并且具体化查询的条件处理,如下代码所示。

namespace SugarProject.Core.MongoDB { /// <summary> /// 基于MongoDB数据库的应用层服务接口实现 /// </summary> public class CustomerService : BaseMongoService<CustomerInfo, CustomerPagedDto>, ICustomerService { /// <summary> /// 构造函数 /// </summary> public CustomerService( { } /// <summary> /// 自定义条件处理 /// </summary> /// <param name="input">查询条件Dto</param> /// <returns></returns> protected override IMongoQueryable<CustomerInfo> CreateFilteredQueryAsync(CustomerPagedDto input { var query = base.CreateFilteredQueryAsync(input; query = query .Where(t=> !input.ExcludeId.IsNullOrWhiteSpace( && t.Id != input.ExcludeId //不包含排除ID .Where(t=> !input.Name.IsNullOrWhiteSpace( && t.Name.Contains(input.Name //如需要精确匹配则用Equals //年龄区间查询 .Where(t=> input.AgeStart.HasValue && t.Age >= input.AgeStart.Value .Where(t => input.AgeEnd.HasValue && t.Age <= input.AgeEnd.Value //创建日期区间查询 .Where(t => input.CreateTimeStart.HasValue && t.CreateTime >= input.CreateTimeStart.Value .Where(t => input.CreateTimeEnd.HasValue && t.CreateTime <= input.CreateTimeEnd.Value ; return query; }

这个处理方式类似于常规关系型数据库的处理方式,就是对条件的判断处理。而具体的业务对象模型,和常规框架的实体类很类似。

/// <summary> /// 客户信息 /// 继承自BaseMongoEntity,拥有Id主键属性 /// </summary> public class CustomerInfo : BaseMongoEntity { /// <summary> /// 默认构造函数(需要初始化属性的在此处理) /// </summary> public CustomerInfo( { this.CreateTime = System.DateTime.Now; } #region Property Members /// <summary> /// 姓名 /// </summary> public virtual string Name { get; set; } /// <summary> /// 年龄 /// </summary> public virtual int Age { get; set; } /// <summary> /// 创建人 /// </summary> public virtual string Creator { get; set; } /// <summary> /// 创建时间 /// </summary> public virtual DateTime CreateTime { get; set; } #endregion }

对于插入和更新操作等常规操作,我们调用普通的Collection操作处理就可以了

/// <summary> /// 创建对象 /// </summary> /// <param name="input">实体对象</param> /// <returns></returns> public virtual async Task InsertAsync(TEntity input { SetObjectIdIfEmpty(input;//如果Id为空,设置为ObjectId的值 await collection.InsertOneAsync(input; } /// <summary> /// 更新记录 /// </summary> public virtual async Task<bool> UpdateAsync(TEntity input { SetObjectIdIfEmpty(input;//如果Id为空,设置为ObjectId的值 //await _dbSet.ReplaceOneAsync(Builders<TEntity>.Filter.Eq("_id", input.Id, input; //要修改的字段 var list = new List<UpdateDefinition<TEntity>>(; foreach (var item in input.GetType(.GetProperties( { if (item.Name.ToLower( == "id" continue; list.Add(Builders<TEntity>.Update.Set(item.Name, item.GetValue(input; } var updatefilter = Builders<TEntity>.Update.Combine(list; var update = await collection.UpdateOneAsync(Builders<TEntity>.Filter.Eq("_id", input.Id, updatefilter; var result = update != null && update.ModifiedCount > 0; return result; }

更新操作,有一种整个替换更新,还有一个是部分更新,它们两者是有区别的。如果对于部分字段的更新,那么操作如下所示,主要是利用UpdateDefinition对象来指定需要更新那些字段属性及值等信息。

/// <summary> /// 封装处理更新的操作(部分字段更新 /// </summary> /// <example> /// var update = Builders<UserInfo>.Update.Set(s => s.Name, newName; /// </example> public virtual async Task<bool> UpdateAsync(string id, UpdateDefinition<TEntity> update { var result = await collection.UpdateOneAsync(s => s.Id == id, update, new UpdateOptions( { IsUpsert = true }; return result != null && result.ModifiedCount > 0; }

根据MongoDB数据库的特性,我们尽量细化对数据库操作的基类接口,定义所需的接口函数即可。

 

而对于 MongoDB的Web API控制器,我们为了方便开发,也设计了同类型的Web API 控制器基类。

最后我们启动Swagger进行测试对应的接口即可,实际还可以整合在UI中进行测试处理。我们安装MongoDB数据库的管理工具后,可以在MongoDBCompass 中进行查询对应数据库的数据。

/// <summary> /// 客户信息的控制器对象(基于MongoDB,基于BaseApiController,需要自定义接口处理 /// </summary> [ApiController] [Route("api/MongoCustomer"] public class MongoCustomerController : BaseApiController { private ICustomerService _service; /// <summary> /// 构造函数,并注入基础接口对象 /// </summary> /// <param name="service"></param> public MongoCustomerController(ICustomerService service { this._service = service; } /// <summary> /// 获取所有记录 /// </summary> [HttpGet] [Route("all"] public virtual async Task<ListResultDto<CustomerInfo>> GetAllAsync( { //检查用户是否有权限,否则抛出MyDenyAccessException异常 base.CheckAuthorized(AuthorizeKey.ListKey; return await _service.GetAllAsync(; }

    /// <summary>
    /// 客户信息的控制器对象(基于MongoDB,基于MongoBaseController,具有常规CRUD操作接口
    /// </summary>
    [ApiController]
    [Route("api/MongoCustomer2"]
    public class MongoCustomer2Controller : MongoBaseController<CustomerInfo, CustomerPagedDto>
    {
        /// <summary>
        /// 构造函数,并注入基础接口对象
        /// </summary>
        /// <param name="service"></param>
        public MongoCustomer2Controller(ICustomerService service : base(service
        {
        }
    }

早几年前曾经也介绍过该数据库的相关使用,随笔如下所示,有需要也可以了解下。

基于C#的MongoDB数据库开发应用(4)--Redis的安装及使用 伍华聪 2016-01-12 
基于C#的MongoDB数据库开发应用(3)--MongoDB数据库的C#开发之异步接口 伍华聪 2016-01-12 
基于C#的MongoDB数据库开发应用(2)--MongoDB数据库的C#开发 伍华聪 2016-01-06
基于C#的MongoDB数据库开发应用(1)--MongoDB数据库的基础知识和使用 伍华聪 2016-01-05
 

编程笔记 » 基于SqlSugar的开发框架循序渐进介绍(27)-- 基于MongoDB的数据库操作整合

赞同 (35) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽