MongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(4)-Mongo数据仓储和工作单元模式封装

前言

         上一章我们把系统所需要的MongoDB集合设计好了,这一章我们的主要任务是使用.NET Core应用程序连接MongoDB并且封装MongoDB数据仓储和工作单元模式,因为本章内容涵盖的有点多关于仓储和工作单元的使用就放到下一章节中讲解了。仓储模式(Repository )带来的好处是一套代码可以适用于多个类,把常用的CRUD通用方法抽象出来通过接口形式集中管理,从而解除业务逻辑层与数据访问层之间的耦合,使业务逻辑层在存储、访问数据库时无须关心数据的来源及存储方式。工作单元模式(UnitOfWork)它是用来维护一个由已经被业务修改(如增加、删除和更新等)的业务对象组成的列表,跨多个请求的业务,统一管理事务,统一提交从而保障事物一致性的作用。

MongoDB从入门到实战的相关教程

MongoDB从入门到实战之MongoDB简介👉

MongoDB从入门到实战之MongoDB快速入门👉

MongoDB从入门到实战之Docker快速安装MongoDB👉

MongoDB从入门到实战之MongoDB工作常用操作命令👉

MongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(1)-后端项目框架搭建👉

MongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(2)-Swagger框架集成👉

MongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(3)-系统数据集合设计👉

MongoDB从入门到实战之.NET Core使用MongoDB开发ToDoList系统(4)-MongoDB数据仓储和工作单元模式封装👉

YyFlight.ToDoList项目源码地址

欢迎各位看官老爷review,有帮助的别忘了给我个Star哦💖!!!

GitHub地址:GitHub - YSGStudyHards/YyFlight.ToDoList: 【.NET8 MongoDB 待办清单系统】.NET8 MongoDB从入门到实战基础教程,该项目后端使用的是.NET8、前端页面使用Blazor、使用MongoDB存储数据,更多相关内容大家可以看目录中的MongoDB从入门到实战的相关教程。该系列教程可作为.NET Core入门项目进行学习,感兴趣的小伙伴可以关注博主和我一起学习共同进步。

MongoRepository地址:https://github.com/YSGStudyHards/YyFlight.ToDoList/tree/main/Repository/Repository

MongoDB事务使用前提说明

参阅MongoDB的事务

说明:

MongoDB单机服务器不支持事务【使用MongoDB事务会报错:Standalone servers do not support transactions】,只有在集群情况下才支持事务,因为博主接下来都是在单机环境下操作,所以无法来演示Mongo事务操作,但是方法都已经是封装好了的,大家可以自己搭建集群实操。

原因:

MongoDB在使用分布式事务时需要进行多节点之间的协调和通信,而单机环境下无法实现这样的分布式协调和通信机制。但是,在MongoDB部署为一个集群(cluster)后,将多个计算机连接为一个整体,通过协调和通信机制实现了分布式事务的正常使用。从数据一致性和可靠性的角度来看,在分布式系统中实现事务处理是至关重要的。而在单机环境下不支持事务,只有在集群情况下才支持事务的设计方式是为了保证数据一致性和可靠性,并且也符合分布式系统的设计思想。

MongoDB.Driver驱动安装

1、直接命令自动安装

Install-Package MongoDB.Driver

2、搜索Nuget手动安装

MongoSettings数据库连接配置

前往appsettings.json文件中配置Mongo数据库信息:

  "MongoSettings": {
    "Connection": "mongodb://root:123456@local:27017/yyflight_todolist?authSource=admin", //MongoDB连接字符串
    "DatabaseName": "yyflight_todolist" //MongoDB数据库名称
  }

定义单例的MongoClient

基于MongoDB的最佳实践对于MongoClient最好设置为单例注入,因为在MongoDB.Driver中MongoClient已经被设计为线程安全可以被多线程共享,这样可还以避免反复实例化MongoClient带来的开销,避免在极端情况下的性能低下。

我们这里设计一个MongoConnection类,用于包裹这个MongoClient,然后将其以单例模式注入IoC容器中。

定义IMongoConnection接口

    public interface IMongoConnection
    {
        public MongoClient MongoDBClient { get; set; }
        public IMongoDatabase DatabaseName { get; set; }
    }

定义MongoConnection类

    public class MongoConnection : IMongoConnection
    {
        //基于MongoDB的最佳实践对于MongoClient最好设置为单例注入,因为在MongoDB.Driver中MongoClient已经被设计为线程安全可以被多线程共享,这样可还以避免反复实例化MongoClient带来的开销,避免在极端情况下的性能低下。
        //我们这里设计一个MongoConnection类,用于包裹这个MongoClient,然后将其以单例模式注入IoC容器中。
        public MongoClient MongoDBClient { get; set; }

        public IMongoDatabase DatabaseName { get; set; }

        public MongoConnection(IConfiguration configuration)
        {
            MongoDBClient = new MongoClient(configuration["MongoSettings:Connection"]);
            DatabaseName = MongoDBClient.GetDatabase(configuration["MongoSettings:DatabaseName"]);
        }
    }

定义Mongo DBContext上下文

现在我们将定义MongoDB DBContext上下文类,具体到一个业务对象或需要被持久化的对象,这个上下文类将封装数据库的连接和集合。
该类应负责建立与所需数据库的连接,在建立连接后,该类将在内存中或按请求持有数据库上下文(基于API管道中配置的生命周期管理。)

定义IMongoContext接口 

    public interface IMongoContext : IDisposable
    {
        /// <summary>
        /// 添加命令操作
        /// </summary>
        /// <param name="func">委托 方法接受一个 Func<IClientSessionHandle, Task> 委托作为参数,该委托表示一个需要 IClientSessionHandle 对象作为参数并返回一个异步任务的方法</param>
        /// <returns></returns>
        Task AddCommandAsync(Func<IClientSessionHandle, Task> func);

        /// <summary>
        /// 提交更改并返回受影响的行数
        /// TODO:MongoDB单机服务器不支持事务【使用MongoDB事务会报错:Standalone servers do not support transactions】,只有在集群情况下才支持事务
        /// 原因:MongoDB在使用分布式事务时需要进行多节点之间的协调和通信,而单机环境下无法实现这样的分布式协调和通信机制。但是,在MongoDB部署为一个集群(cluster)后,将多个计算机连接为一个整体,通过协调和通信机制实现了分布式事务的正常使用。从数据一致性和可靠性的角度来看,在分布式系统中实现事务处理是至关重要的。而在单机环境下不支持事务,只有在集群情况下才支持事务的设计方式是为了保证数据一致性和可靠性,并且也符合分布式系统的设计思想。
        /// </summary>
        /// <param name="session">MongoDB 会话(session)对象</param>
        /// <returns></returns>
        Task<int> SaveChangesAsync(IClientSessionHandle session);

        /// <summary>
        /// 初始化Mongodb会话对象session
        /// </summary>
        /// <returns></returns>
        Task<IClientSessionHandle> StartSessionAsync();

        /// <summary>
        /// 获取集合数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        IMongoCollection<T> GetCollection<T>(string name);
    }

定义MongoContext类

    public class MongoContext : IMongoContext
    {
        private readonly IMongoDatabase _databaseName;
        private readonly MongoClient _mongoClient;
        //这里将 _commands 中的每个元素都定义为一个 Func<IClientSessionHandle, Task> 委托,此委托表示一个需要 IClientSessionHandle 对象作为参数并返回一个异步任务的方法
        //每个委托都表示一个MongoDB 会话(session)对象和要执行的命令
        private readonly List<Func<IClientSessionHandle, Task>> _commands = new List<Func<IClientSessionHandle, Task>>();

        public MongoContext(IMongoConnection mongoConnection)
        {
            _mongoClient = mongoConnection.MongoDBClient;
            _databaseName = mongoConnection.DatabaseName;
        }

        /// <summary>
        /// 添加命令操作
        /// </summary>
        /// <param name="func">方法接受一个 Func<IClientSessionHandle, Task> 委托作为参数,该委托表示一个需要 IClientSessionHandle 对象作为参数并返回一个异步任务的方法</param>
        /// <returns></returns>
        public async Task AddCommandAsync(Func<IClientSessionHandle, Task> func)
        {
            _commands.Add(func);
            await Task.CompletedTask;
        }

        /// <summary>
        /// 提交更改并返回受影响的行数
        /// TODO:MongoDB单机服务器不支持事务【使用MongoDB事务会报错:Standalone servers do not support transactions】,只有在集群情况下才支持事务
        /// 原因:MongoDB在使用分布式事务时需要进行多节点之间的协调和通信,而单机环境下无法实现这样的分布式协调和通信机制。但是,在MongoDB部署为一个集群(cluster)后,将多个计算机连接为一个整体,通过协调和通信机制实现了分布式事务的正常使用。从数据一致性和可靠性的角度来看,在分布式系统中实现事务处理是至关重要的。而在单机环境下不支持事务,只有在集群情况下才支持事务的设计方式是为了保证数据一致性和可靠性,并且也符合分布式系统的设计思想。
        /// </summary>
        /// <param name="session">MongoDB 会话(session)对象</param>
        /// <returns></returns>
        public async Task<int> SaveChangesAsync(IClientSessionHandle session)
        {
            try
            {
                session.StartTransaction();//开始事务

                foreach (var command in _commands)
                {
                    await command(session);
                    //语句实现了对事务中所有操作的异步执行,并等待它们完成。如果没有错误发生,程序会继续执行session.CommitTransactionAsync();方法,将之前进行的所有更改一起提交到MongoDB服务器上,从而实现事务提交。
                }

                await session.CommitTransactionAsync();//提交事务
                return _commands.Count;
            }
            catch (Exception ex)
            {
                await session.AbortTransactionAsync();//回滚事务
                return 0;
            }
            finally
            {
                _commands.Clear();//清空_commands列表中的元素
            }
        }

        /// <summary>
        /// 初始化Mongodb会话对象session
        /// </summary>
        /// <returns></returns>
        public async Task<IClientSessionHandle> StartSessionAsync()
        {
            var session = await _mongoClient.StartSessionAsync();
            return session;
        }

        /// <summary>
        /// 获取MongoDB集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="name">集合名称</param>
        /// <returns></returns>
        public IMongoCollection<T> GetCollection<T>(string name)
        {
            return _databaseName.GetCollection<T>(name);
        }

        /// <summary>
        /// 释放上下文
        /// </summary>
        public void Dispose()
        {
            GC.SuppressFinalize(this);
        }
    }

定义通用泛型Repository

Repository(仓储)是DDD(领域驱动设计)中的经典思想,可以归纳为介于实际业务层(领域层)和数据访问层之间的层,能让领域层能在感觉不到数据访问层的情况下,完成与数据库的交互和以往的DAO(数据访问)层相比,Repository层的设计理念更偏向于面向对象,而淡化直接对数据表进行的CRUD操作。

定义IMongoRepository接口

定义一个泛型Repository通用接口,抽象常用的增加,删除,修改,查询等操作方法。

    public interface IMongoRepository<T> where T : class, new()
    {
        #region 事务操作示例

        /// <summary>
        /// 事务添加数据
        /// </summary>
        /// <param name="session">MongoDB 会话(session)对象</param>
        /// <param name="objData">添加数据</param>
        /// <returns></returns>
        Task AddTransactionsAsync(IClientSessionHandle session, T objData);

        /// <summary>
        /// 事务数据删除
        /// </summary>
        /// <param name="session">MongoDB 会话(session)对象</param>
        /// <param name="id">objectId</param>
        /// <returns></returns>
        Task DeleteTransactionsAsync(IClientSessionHandle session, string id);

        /// <summary>
        /// 事务异步局部更新(仅更新一条记录)
        /// </summary>
        /// <param name="session">MongoDB 会话(session)对象</param>
        /// <param name="filter">过滤器</param>
        /// <param name="update">更新条件</param>
        /// <returns></returns>
        Task UpdateTransactionsAsync(IClientSessionHandle session, FilterDefinition<T> filter, UpdateDefinition<T> update);

        #endregion

        #region 添加相关操作

        /// <summary>
        /// 添加数据
        /// </summary>
        /// <param name="objData">添加数据</param>
        /// <returns></returns>
        Task AddAsync(T objData);

        /// <summary>
        /// 批量插入
        /// </summary>
        /// <param name="objDatas">实体集合</param>
        /// <returns></returns>
        Task InsertManyAsync(List<T> objDatas);

        #endregion

        #region 删除相关操作

        /// <summary>
        /// 数据删除
        /// </summary>
        /// <param name="id">objectId</param>
        /// <returns></returns>
        Task DeleteAsync(string id);

        /// <summary>
        /// 异步删除多条数据
        /// </summary>
        /// <param name="filter">删除的条件</param>
        /// <returns></returns>
        Task<DeleteResult> DeleteManyAsync(FilterDefinition<T> filter);

        #endregion

        #region 修改相关操作

        /// <summary>
        /// 指定对象异步修改一条数据
        /// </summary>
        /// <param name="obj">要修改的对象</param>
        /// <param name="id">修改条件</param>
        /// <returns></returns>
        Task UpdateAsync(T obj, string id);

        /// <summary>
        /// 局部更新(仅更新一条记录)
        /// <para><![CDATA[expression 参数示例:x => x.Id == 1 && x.Age > 18 && x.Gender == 0]]></para>
        /// <para><![CDATA[entity 参数示例:y => new T{ RealName = "Ray", Gender = 1}]]></para>
        /// </summary>
        /// <param name="expression">筛选条件</param>
        /// <param name="entity">更新条件</param>
        /// <returns></returns>
        Task UpdateAsync(Expression<Func<T, bool>> expression, Expression<Action<T>> entity);

        /// <summary>
        /// 异步局部更新(仅更新一条记录)
        /// </summary>
        /// <param name="filter">过滤器</param>
        /// <param name="update">更新条件</param>
        /// <returns></returns>
        Task UpdateAsync(FilterDefinition<T> filter, UpdateDefinition<T> update);

        /// <summary>
        /// 异步局部更新(仅更新多条记录)
        /// </summary>
        /// <param name="expression">筛选条件</param>
        /// <param name="update">更新条件</param>
        /// <returns></returns>
        Task UpdateManyAsync(Expression<Func<T, bool>> expression, UpdateDefinition<T> update);

        /// <summary>
        /// 异步批量修改数据
        /// </summary>
        /// <param name="dic">要修改的字段</param>
        /// <param name="filter">更新条件</param>
        /// <returns></returns>
        Task<UpdateResult> UpdateManayAsync(Dictionary<string, string> dic, FilterDefinition<T> filter);

        #endregion

        #region 查询统计相关操作

        /// <summary>
        /// 通过ID主键获取数据
        /// </summary>
        /// <param name="id">objectId</param>
        /// <returns></returns>
        Task<T> GetByIdAsync(string id);
        /// <summary>
        /// 获取所有数据
        /// </summary>
        /// <returns></returns>
        Task<IEnumerable<T>> GetAllAsync();

        /// <summary>
        /// 获取记录数
        /// </summary>
        /// <param name="expression">筛选条件</param>
        /// <returns></returns>
        Task<long> CountAsync(Expression<Func<T, bool>> expression);

        /// <summary>
        /// 获取记录数
        /// </summary>
        /// <param name="filter">过滤器</param>
        /// <returns></returns>
        Task<long> CountAsync(FilterDefinition<T> filter);

        /// <summary>
        /// 判断是否存在
        /// </summary>
        /// <param name="predicate">条件</param>
        /// <returns></returns>
        Task<bool> ExistsAsync(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 异步查询集合
        /// </summary>
        /// <param name="filter">查询条件</param>
        /// <param name="field">要查询的字段,不写时查询全部</param>
        /// <param name="sort">要排序的字段</param>
        /// <returns></returns>
        Task<List<T>> FindListAsync(FilterDefinition<T> filter, string[]? field = null, SortDefinition<T>? sort = null);

        /// <summary>
        /// 异步分页查询集合
        /// </summary>
        /// <param name="filter">查询条件</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">页容量</param>
        /// <param name="field">要查询的字段,不写时查询全部</param>
        /// <param name="sort">要排序的字段</param>
        /// <returns></returns>
        Task<List<T>> FindListByPageAsync(FilterDefinition<T> filter, int pageIndex, int pageSize, string[]? field = null, SortDefinition<T>? sort = null);

        #endregion
    }

实现泛型MongoBaseRepository基类

    public class MongoBaseRepository<T> : IMongoRepository<T> where T : class, new()
    {
        protected readonly IMongoContext _context;
        protected readonly IMongoCollection<T> _dbSet;
        private readonly string _collectionName;

        protected MongoBaseRepository(IMongoContext context)
        {
            _context = context;
            _collectionName = typeof(T).GetAttributeValue((TableAttribute m) => m.Name) ?? typeof(T).Name;
            _dbSet = _context.GetCollection<T>(_collectionName);
        }

        #region 事务操作示例

        /// <summary>
        /// 事务添加数据
        /// </summary>
        /// <param name="session">MongoDB 会话(session)对象</param>
        /// <param name="objData">添加数据</param>
        /// <returns></returns>
        public async Task AddTransactionsAsync(IClientSessionHandle session, T objData)
        {
            await _context.AddCommandAsync(async (session) => await _dbSet.InsertOneAsync(objData));
        }

        /// <summary>
        /// 事务数据删除
        /// </summary>
        /// <param name="session">MongoDB 会话(session)对象</param>
        /// <param name="id">objectId</param>
        /// <returns></returns>
        public async Task DeleteTransactionsAsync(IClientSessionHandle session, string id)
        {
            await _context.AddCommandAsync((session) => _dbSet.DeleteOneAsync(Builders<T>.Filter.Eq(" _id ", id)));
        }

        /// <summary>
        /// 事务异步局部更新(仅更新一条记录)
        /// </summary>
        /// <param name="session">MongoDB 会话(session)对象</param>
        /// <param name="filter">过滤器</param>
        /// <param name="update">更新条件</param>
        /// <returns></returns>
        public async Task UpdateTransactionsAsync(IClientSessionHandle session, FilterDefinition<T> filter, UpdateDefinition<T> update)
        {
            await _context.AddCommandAsync((session) => _dbSet.UpdateOneAsync(filter, update));
        }

        #endregion

        #region 添加相关操作

        /// <summary>
        /// 添加数据
        /// </summary>
        /// <param name="objData">添加数据</param>
        /// <returns></returns>
        public async Task AddAsync(T objData)
        {
            await _dbSet.InsertOneAsync(objData);
        }

        /// <summary>
        /// 批量插入
        /// </summary>
        /// <param name="objDatas">实体集合</param>
        /// <returns></returns>
        public async Task InsertManyAsync(List<T> objDatas)
        {
            await _dbSet.InsertManyAsync(objDatas);
        }

        #endregion

        #region 删除相关操作

        /// <summary>
        /// 数据删除
        /// </summary>
        /// <param name="id">objectId</param>
        /// <returns></returns>
        public async Task DeleteAsync(string id)
        {
            await _dbSet.DeleteOneAsync(Builders<T>.Filter.Eq("_id", new ObjectId(id)));
        }

        /// <summary>
        /// 异步删除多条数据
        /// </summary>
        /// <param name="filter">删除的条件</param>
        /// <returns></returns>
        public async Task<DeleteResult> DeleteManyAsync(FilterDefinition<T> filter)
        {
            return await _dbSet.DeleteManyAsync(filter);
        }

        #endregion

        #region 修改相关操作

        /// <summary>
        /// 指定对象异步修改一条数据
        /// </summary>
        /// <param name="obj">要修改的对象</param>
        /// <param name="id">修改条件</param>
        /// <returns></returns>
        public async Task UpdateAsync(T obj, string id)
        {
            //修改条件
            FilterDefinition<T> filter = Builders<T>.Filter.Eq("_id", new ObjectId(id));
            //要修改的字段
            var list = new List<UpdateDefinition<T>>();
            foreach (var item in obj.GetType().GetProperties())
            {
                if (item.Name.ToLower() == "id") continue;
                list.Add(Builders<T>.Update.Set(item.Name, item.GetValue(obj)));
            }
            var updatefilter = Builders<T>.Update.Combine(list);
            await _dbSet.UpdateOneAsync(filter, updatefilter);
        }

        /// <summary>
        /// 局部更新(仅更新一条记录)
        /// <para><![CDATA[expression 参数示例:x => x.Id == 1 && x.Age > 18 && x.Gender == 0]]></para>
        /// <para><![CDATA[entity 参数示例:y => new T{ RealName = "Ray", Gender = 1}]]></para>
        /// </summary>
        /// <param name="expression">筛选条件</param>
        /// <param name="entity">更新条件</param>
        /// <returns></returns>
        public async Task UpdateAsync(Expression<Func<T, bool>> expression, Expression<Action<T>> entity)
        {
            var fieldList = new List<UpdateDefinition<T>>();

            if (entity.Body is MemberInitExpression param)
            {
                foreach (var item in param.Bindings)
                {
                    var propertyName = item.Member.Name;
                    object propertyValue = null;

                    if (item is not MemberAssignment memberAssignment) continue;

                    if (memberAssignment.Expression.NodeType == ExpressionType.Constant)
                    {
                        if (memberAssignment.Expression is ConstantExpression constantExpression)
                            propertyValue = constantExpression.Value;
                    }
                    else
                    {
                        propertyValue = Expression.Lambda(memberAssignment.Expression, null).Compile().DynamicInvoke();
                    }

                    if (propertyName != "_id") //实体键_id不允许更新
                    {
                        fieldList.Add(Builders<T>.Update.Set(propertyName, propertyValue));
                    }
                }
            }

            await _dbSet.UpdateOneAsync(expression, Builders<T>.Update.Combine(fieldList));
        }

        /// <summary>
        /// 异步局部更新(仅更新一条记录)
        /// </summary>
        /// <param name="filter">过滤器</param>
        /// <param name="update">更新条件</param>
        /// <returns></returns>
        public async Task UpdateAsync(FilterDefinition<T> filter, UpdateDefinition<T> update)
        {
            await _dbSet.UpdateOneAsync(filter, update);
        }

        /// <summary>
        /// 异步局部更新(仅更新多条记录)
        /// </summary>
        /// <param name="expression">筛选条件</param>
        /// <param name="update">更新条件</param>
        /// <returns></returns>
        public async Task UpdateManyAsync(Expression<Func<T, bool>> expression, UpdateDefinition<T> update)
        {
            await _dbSet.UpdateManyAsync(expression, update);
        }

        /// <summary>
        /// 异步批量修改数据
        /// </summary>
        /// <param name="dic">要修改的字段</param>
        /// <param name="filter">更新条件</param>
        /// <returns></returns>
        public async Task<UpdateResult> UpdateManayAsync(Dictionary<string, string> dic, FilterDefinition<T> filter)
        {
            T t = new T();
            //要修改的字段
            var list = new List<UpdateDefinition<T>>();
            foreach (var item in t.GetType().GetProperties())
            {
                if (!dic.ContainsKey(item.Name)) continue;
                var value = dic[item.Name];
                list.Add(Builders<T>.Update.Set(item.Name, value));
            }
            var updatefilter = Builders<T>.Update.Combine(list);
            return await _dbSet.UpdateManyAsync(filter, updatefilter);
        }

        #endregion

        #region 查询统计相关操作

        /// <summary>
        /// 通过ID主键获取数据
        /// </summary>
        /// <param name="id">objectId</param>
        /// <returns></returns>
        public async Task<T> GetByIdAsync(string id)
        {
            var queryData = await _dbSet.FindAsync(Builders<T>.Filter.Eq("_id", new ObjectId(id)));
            return queryData.FirstOrDefault();
        }

        /// <summary>
        /// 获取所有数据
        /// </summary>
        /// <returns></returns>
        public async Task<IEnumerable<T>> GetAllAsync()
        {
            var queryAllData = await _dbSet.FindAsync(Builders<T>.Filter.Empty);
            return queryAllData.ToList();
        }

        /// <summary>
        /// 获取记录数
        /// </summary>
        /// <param name="expression">筛选条件</param>
        /// <returns></returns>
        public async Task<long> CountAsync(Expression<Func<T, bool>> expression)
        {
            return await _dbSet.CountDocumentsAsync(expression);
        }

        /// <summary>
        /// 获取记录数
        /// </summary>
        /// <param name="filter">过滤器</param>
        /// <returns></returns>
        public async Task<long> CountAsync(FilterDefinition<T> filter)
        {
            return await _dbSet.CountDocumentsAsync(filter);
        }

        /// <summary>
        /// 判断是否存在
        /// </summary>
        /// <param name="predicate">条件</param>
        /// <returns></returns>
        public async Task<bool> ExistsAsync(Expression<Func<T, bool>> predicate)
        {
            return await Task.FromResult(_dbSet.AsQueryable().Any(predicate));
        }

        /// <summary>
        /// 异步查询集合
        /// </summary>
        /// <param name="filter">查询条件</param>
        /// <param name="field">要查询的字段,不写时查询全部</param>
        /// <param name="sort">要排序的字段</param>
        /// <returns></returns>
        public async Task<List<T>> FindListAsync(FilterDefinition<T> filter, string[]? field = null, SortDefinition<T>? sort = null)
        {
            //不指定查询字段
            if (field == null || field.Length == 0)
            {
                if (sort == null) return await _dbSet.Find(filter).ToListAsync();
                return await _dbSet.Find(filter).Sort(sort).ToListAsync();
            }

            //指定查询字段
            var fieldList = new List<ProjectionDefinition<T>>();
            for (int i = 0; i < field.Length; i++)
            {
                fieldList.Add(Builders<T>.Projection.Include(field[i].ToString()));
            }
            var projection = Builders<T>.Projection.Combine(fieldList);
            fieldList?.Clear();

            //不排序
            if (sort == null) return await _dbSet.Find(filter).Project<T>(projection).ToListAsync();

            //排序查询
            return await _dbSet.Find(filter).Sort(sort).Project<T>(projection).ToListAsync();
        }

        /// <summary>
        /// 异步分页查询集合
        /// </summary>
        /// <param name="filter">查询条件</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">页容量</param>
        /// <param name="field">要查询的字段,不写时查询全部</param>
        /// <param name="sort">要排序的字段</param>
        /// <returns></returns>
        public async Task<List<T>> FindListByPageAsync(FilterDefinition<T> filter, int pageIndex, int pageSize, string[]? field = null, SortDefinition<T>? sort = null)
        {
            //不指定查询字段
            if (field == null || field.Length == 0)
            {
                if (sort == null) return await _dbSet.Find(filter).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
                //进行排序
                return await _dbSet.Find(filter).Sort(sort).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
            }

            //指定查询字段
            var fieldList = new List<ProjectionDefinition<T>>();
            for (int i = 0; i < field.Length; i++)
            {
                fieldList.Add(Builders<T>.Projection.Include(field[i].ToString()));
            }
            var projection = Builders<T>.Projection.Combine(fieldList);
            fieldList?.Clear();

            //不排序
            if (sort == null) return await _dbSet.Find(filter).Project<T>(projection).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();

            //排序查询
            return await _dbSet.Find(filter).Sort(sort).Project<T>(projection).Skip((pageIndex - 1) * pageSize).Limit(pageSize).ToListAsync();
        }

        #endregion
    }

工作单元模式

工作单元模式是“维护一个被业务事务影响的对象列表,协调变化的写入和并发问题的解决”。具体来说,在C#工作单元模式中,我们通过UnitOfWork对象来管理多个Repository对象,同时UnitOfWork还提供了对事务的支持。对于一组需要用到多个Repository的业务操作,我们可以在UnitOfWork中创建一个事务,并将多个Repository操作放在同一个事务中处理,以保证数据的一致性。当所有Repository操作完成后,再通过UnitOfWork提交事务或者回滚事务。

定义IUnitOfWork接口

    /// <summary>
    /// 工作单元接口
    /// </summary>
    public interface IUnitOfWork : IDisposable
    {
        /// <summary>
        /// 提交保存更改
        /// </summary>
        /// <param name="session">MongoDB 会话(session)对象</param>
        /// <returns></returns>
        Task<bool> Commit(IClientSessionHandle session);

        /// <summary>
        /// 初始化MongoDB会话对象session
        /// </summary>
        /// <returns></returns>
        Task<IClientSessionHandle> InitTransaction();
    }

定义UnitOfWork类

    /// <summary>
    /// 工作单元类
    /// </summary>
    public class UnitOfWork : IUnitOfWork
    {
        private readonly IMongoContext _context;

        public UnitOfWork(IMongoContext context)
        {
            _context = context;
        }

        /// <summary>
        /// 提交保存更改
        /// </summary>
        /// <param name="session">MongoDB 会话(session)对象</param>
        /// <returns></returns>
        public async Task<bool> Commit(IClientSessionHandle session)
        {
            return await _context.SaveChangesAsync(session) > 0;
        }

        /// <summary>
        /// 初始化MongoDB会话对象session
        /// </summary>
        /// <returns></returns>
        public async Task<IClientSessionHandle> InitTransaction()
        {
            return await _context.StartSessionAsync();
        }

        public void Dispose()
        {
            _context.Dispose();
        }
    }

注册数据库基础操作和工作单元

//注册数据库基础操作和工作单元
builder.Services.AddSingleton<IMongoConnection, MongoConnection>();
builder.Services.AddScoped<IMongoContext, MongoContext>();
builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
builder.Services.AddScoped<IUserRepository, UserRepository>();

参考文章

NoSQL – MongoDB Repository Implementation in .NET Core with Unit Testing example

ASP.NET CORE – MONGODB REPOSITORY PATTERN & UNIT OF WORK

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/404775.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

(done) 什么是特征值和特征向量?如何求特征值的特征向量 ?如何判断一个矩阵能否相似对角化?

什么是齐次方程&#xff1f; https://blog.csdn.net/shimly123456/article/details/136198159 行列式和是否有解的关系&#xff1f; https://blog.csdn.net/shimly123456/article/details/136198215 特征值和特征向量 参考视频&#xff1a;https://www.bilibili.com/video/BV…

基于springboot+vue的在线宠物用品交易网站(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

uni-app 经验分享,从入门到离职(五)——由浅入深 uni-app 数据缓存

文章目录 &#x1f4cb;前言⏬关于专栏 &#x1f3af;什么是数据存储&#x1f9e9;数据存储——存储&#x1f4cc; uni.setStorage(OBJECT)&#x1f4cc; uni.setStorageSync(KEY,DATA) &#x1f9e9;数据存储——获取&#x1f4cc; uni.getStorage(OBJECT)&#x1f4cc; uni.g…

学会如何打印菱形

打印菱形 题目描述&#xff1a;解法思路&#xff1a;解法代码运行结果&#xff1a; 题目描述&#xff1a; 输入⼀个整数n&#xff0c;打印对应2*n-1行的菱形图案&#xff0c;比如&#xff0c;输入7&#xff0c;输出如下图案&#xff0c;图案总共13行 解法思路&#xff1a; …

企业计算机服务器中了crypt勒索病毒怎么办,crypt勒索病毒解密数据恢复

计算机服务器设备为企业的生产运营提供了极大便利&#xff0c;企业的重要核心数据大多都存储在计算机服务器中&#xff0c;保护企业计算机服务器免遭勒索病毒攻击&#xff0c;是一项艰巨的工作任务。但即便很多企业都做好的了安全运维工作&#xff0c;依旧免不了被勒索病毒攻击…

队列的基本操作——常见队列的对比分析(c语言完整代码包含注释)

目录 一、队列 1.1基本概念 1.2基本操作 1.3 队列分类 1.3.1带头队列 1.3.2不带头队列 1.3.3 循环带头队列 1.3.4 循环不带头队列 1.3.5 总结 二、代码实现 2.1带头队列 2.2不带头队列 2.3循环带头队列 2.4循环不带头队列 一、队列 1.1基本概念 队列&#xff08…

RAW 编程接口 TCP 简介

一、LWIP 中 中 RAW API 编程接口中与 TCP 相关的函数 二、LWIP TCP RAW API 函数 三、LwIP_Periodic_Handle函数 LwIP_Periodic_Handle 函数是一个必须被无限循环调用的 LwIP支持函数&#xff0c;一般在 main函数的无限循环中调用&#xff0c;主要功能是为 LwIP各个模块提供…

由于找不到d3dx9_43.dll无法继续执行的解决方法,5种有效的方法

丢失d3dx9_43.dll文件可能会引发一系列运行问题&#xff0c;具体表现在哪些方面呢&#xff1f;首先&#xff0c;它是DirectX 9.0c的一个重要动态链接库文件&#xff0c;对于许多基于此版本DirectX开发的老旧或经典PC游戏至关重要。一旦缺失&#xff0c;可能导致这些游戏无法启动…

element ui 安装 简易过程 已解决

我之所以将Element归类为Vue.js&#xff0c;其主要原因是Element是&#xff08;饿了么团队&#xff09;基于MVVM框架Vue开源出来的一套前端ui组件。我最爱的就是它的布局容器&#xff01;&#xff01;&#xff01; 下面进入正题&#xff1a; 1、Element的安装 首先你需要创建…

基于Java+Selenium的WebUI自动化测试框架(一)---页面元素定位器

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

糖尿病性视网膜病变(DR)的自动化检测和分期

糖尿病性视网膜病变&#xff08;DR&#xff09;的自动化检测和分期 提出背景DR的阶段及其特征 历年解法计算机视觉方法多分类方法 新的解法深度学习方法迁移学习大模型多模型集成全流程分析 总结特征1&#xff1a;图像分割特征2&#xff1a;疾病分级特征3&#xff1a;治疗建议生…

二进制中-1加上+1如果按照原码相加会存在什么问题?

问题描述&#xff1a;二进制中-1加上1如果按照原码相加会存在什么问题&#xff1f; 问题解答&#xff1a; -1加1等于-2&#xff0c;这明显是不对的。 因此引入反码的概念 然后再将计算后反码在取反码&#xff0c;得到-0&#xff0c;如下图所示。 -0不太精确&#xff0c;因此再…

美团面试:说说Java OOM的三大场景和解决方案?

美团面试&#xff1a;说说Java OOM的场景和解决方案&#xff1f; 尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的面试题&…

day05_方法

今日内容 流程控制关键字 break,continue方法 复习 1 循环的四要素 初始值控制条件循环内容迭代 2 for循环执行流程 for(初始值;控制条件;迭代){ 循环体; } 3 while和do-while什么区别 while先判断后执行dowhile是先执行再判断(先斩后奏) 4 手写代码,写出使用for循环输出1-10的…

区块链笔记(五)---德勤相关分析报告

web3.0 定义&#xff1a; 在《Insights into a Modern World》提出&#xff0c;“信息将由用户自己发布、保管、不可追溯且永远不会泄露&#xff0c;用户的任何行为将不需要任何中间机构来帮助传递”&#xff1b;用来指代一种区块链技术&#xff0c;可以基于“无须信任的交互…

微信小程序开发:通过wx.login()获取用户唯一标识openid和unionid

下面代码展示了 openid 的获取过程。 想获取 unionid 需要满足条件&#xff1a;小程序已绑定到微信开放平台账号下&#xff0c;不然只会返回 openid。 【相关文档】 微信小程序开发&#xff1a;appid 和 secret 的获取方法 wx.login({success (res) {if (res.code) {// 发起网…

【机器学习的基本术语和概念】

曾梦想执剑走天涯&#xff0c;我是程序猿【AK】 目录 简述概要知识图谱 简述概要 提示&#xff1a;简要描述文章内容&#xff0c;适合哪些人观看 知识图谱 样本&#xff08;Sample&#xff09;/实例&#xff08;Instance&#xff09;&#xff1a;在机器学习中&#xff0c;我…

Linux中的各类时间 与 find命令的常用参数

之前研究wal日志清理的副产物&#xff0c;wal日志名被修改后文件的哪个时间会变&#xff1f;应该如何删除&#xff1f;由此整理一下Linux中atime、mtime、ctime的区别&#xff0c;以及find的常见用法。 一、 Linux中的各类时间 1. 各类时间的定义 Linux中有三种用于文件时间戳…

你听说过柔性数组吗?

目录 1. 柔性数组的概念 2. 柔性数组的特点 3. 柔性数组的使用 4. 柔性数组的优势 5.完结散花 悟已往之不谏&#xff0c;知来者犹可追 创作不易&#xff0c;宝子们&#xff01;如果这篇文章对你们有帮助的话&#…

个人博客系列-项目部署-nginx(3)

使用Nginx uwsgi进行部署django项目 一. 检查项目是否可以运行 启动项目 python manage.py runserver 0.0.0.0:8099输入ip:8099 查看启动页面 出现上述页面表示运行成功 二. 安装uwsgi并配置 2.1 下载uwsgi pip install uwsgi新建文件test.py写入内容&#xff0c;测试一…