.NET Core MongoDB数据仓储和工作单元模式封装

前言

         上一章我们把系统所需要的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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

开源数据可视化应用程序JSON Crack

什么是 JSON Crack &#xff1f; JSON Crack 是一款免费的开源数据可视化应用程序&#xff0c;能够将 JSON、YAML、XML、CSV 等数据格式可视化为交互式图表。凭借其直观且用户友好的界面&#xff0c;JSON Crack 可以轻松探索、分析和理解即使是最复杂的数据结构。无论您是从事大…

【Chrono Engine学习总结】5-sensor-5.2-导出lidar数据的方法与原理探究

由于Chrono的官方教程在一些细节方面解释的并不清楚&#xff0c;自己做了一些尝试&#xff0c;做学习总结。 1、Sensor数据生成流程回顾 Chrono里面&#xff0c;sensor的数据生成、可视化、以及保存&#xff0c;都需要单独进行设置才能实现。sensor数据的采集流程如下https:/…

17.3.1.6 自定义处理

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 模拟某款图像处理软件的处理&#xff0c;它只留下红色、绿色或者蓝色这样的单一颜色。 首先按照颜色划分了6个色系&#xff0c;分别…

P2P 应用

P2P 工作方式概述 在 P2P 工作方式下&#xff0c;所有的音频/视频文件都是在普通的互联网用户之间传输。 1 具有集中目录服务器的 P2P 工作方式 Napster 最早使用 P2P 技术&#xff0c;提供免费下载 MP3 音乐。 Napster 将所有音乐文件的索引信息都集中存放在 Napster 目录服…

《白话C++》第10章 STL和boost,Page67 std::auto_ptr

std::auto_ptr可以不经意间转移裸指针控制权 std::auto_ptr持有裸指针的控制权&#xff0c;却可以随随便便看似不经意地转移给另一个auto_ptr: #include <iostream> #include <memory>using namespace std;struct S {int a;void SetA(int a){this->a a;}~S()…

爱上JVM——常见问题(一):JVM组成

1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序的运行环境&#xff08;java二进制字节码的运行环境&#xff09; 好处&#xff1a; 一次编写&…

如何修复Microsoft Edge不能以全屏模式打开​?这里提供几个故障排除方法

随着越来越多的Windows 10用户将Edge设置为默认浏览器&#xff0c;各种错误和小故障层出不穷。例如&#xff0c;许多用户抱怨他们无法在全屏模式下启动Edge。如果你正在寻找解决方案来解决这个恼人的问题&#xff0c;请按照下面的故障排除步骤进行操作。 修复Microsoft Edge不…

最大N个数与最小N个数的和 - 华为OD统一考试(C卷)

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题解&#xff1a; Java / Python / C 题目描述 给定一个数组&#xff0c;编写一个函数来计算它的最大N个数与最小N个数的和。你需要对数组进行去重。 说明&#xff1a; 数组中数字范围[0, 1000] 最大N个数与…

ClickHouse--09--SQL DML 操作

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 SQL DML 操作1.Insert 插入2.update 更新3.delete 删除 SQL DML 操作 DML:Data Manipulation Language,数据操纵语言。 List itemClickHouse 中 DML 语言包含插入…

【JavaEE】_线程与多线程的创建

目录 1. 线程的概念 2. 创建与使用多线程 2.1 方式1&#xff1a;继承Thread类 2.2 方式2&#xff1a; 实现Runnable接口 2.3 以上两种创建线程方式的对比 3. 多线程的优势-增加运行速度 1. 线程的概念 进程的存在是由于系统的多任务执行需求&#xff0c;这也要求程序员进…

php基础学习之作用域和静态变量

作用域 变量&#xff08;常量&#xff09;能够被访问的区域&#xff0c;变量可以在常规代码中定义&#xff0c;也可以在函数内部定义 变量的作用域 在 PHP 中作用域严格来说分为两种&#xff0c;但是 PHP内部还定义一些在严格意义之外的一种&#xff0c;所以总共算三种—— 局部…

html从零开始9:javaScript简介,语句、标识符,变量,JavaScript引入到文件【搬代码】

javaScript简介 javaScript语句、标识符 变量 var num 10; var就是固定声明,num就是变量名&#xff0c;10就是变量&#xff1b;<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Comp…