目录
1.添加包
2. 连接配置
2.1.链接字符串
2.2.连接类
3.仓储配置
3.1.仓储实现
3.2.仓储接口
4.获取配置和注册
4.1.添加配置获取方法
4.2.注册
5.常规使用案例
5.1实体
5.2.实现
5.3.接口
5.4.控制器
NET Core 应用程序中使用 MongoDB 有许多好处,尤其是在处理大规模数据、需要高灵活性和扩展性的场景下。
1.添加包
添加 MongoDB.Driver 包。
2. 连接配置
2.1.连接字符串
dbsettings.json文件添加 MongoDB连接配置
//MongoDB配置
"MongoDBSettings": {"MongoConnStr": "mongodb://root:密码@iP地址:Mongo端口","DatabaseName": "数据库名称"
},
2.2.连接类
namespace Frame3_DataRepository.MongoRepository
{/// <summary>/// Mongo配置获取/// </summary>public class MongoOptions{/// <summary>/// 连接地址/// </summary>public string MongoConnStr { get; set; }/// <summary>/// 连接数据库/// </summary>public string DatabaseName { get; set; }}
}
案例如下
3.仓储配置
3.1.仓储实现
为MongoDB添加仓储方便使用。
using MongoDB.Bson;
using MongoDB.Driver;
using System.Linq.Expressions;namespace Frame3_DataRepository.MongoRepository
{/// <summary>/// 泛型MongoDB仓储实现类,实现IMongoRepository<T>接口,约束T必须是class类型/// </summary>/// <typeparam name="T"></typeparam>public class MongoRepository<T> : IMongoRepository<T> where T : class{// 私有字段,存储MongoDB集合对象private readonly IMongoCollection<T> _collection;/// <summary>/// 构造函数,通过依赖注入初始化/// </summary>/// <param name="database">Mongo数据库实例</param>/// <param name="collectionName">集合名称(可选,默认为类型名称)</param>public MongoRepository(IMongoDatabase database, string collectionName = null){// 获取或创建集合:如果collectionName为null则使用类型名称作为集合名_collection = database.GetCollection<T>(collectionName ?? typeof(T).Name);}#region 基本CRUD操作/// <summary>/// 插入单个文档/// </summary>/// <param name="document">要插入的文档</param>/// <returns>受影响的行数(1表示成功,0表示失败)</returns>public async Task<int> InsertAsync(T document){try{// 异步插入单个文档await _collection.InsertOneAsync(document);return 1; // 插入单个文档总是返回1表示成功}catch{return 0; // 插入失败返回0}}/// <summary>/// 批量插入文档/// </summary>/// <param name="documents">要插入的文档集合</param>/// <returns>实际插入的文档数量</returns>public async Task<int> InsertAsync(IEnumerable<T> documents){try{// 转换为列表以避免多次枚举var docsList = documents.ToList();// 如果集合为空则直接返回0if (!docsList.Any()){return 0;}// 异步批量插入文档await _collection.InsertManyAsync(docsList);// 返回实际插入的文档数量return docsList.Count;}catch{return 0; // 插入失败返回0}}/// <summary>/// 根据ID获取文档/// </summary>/// <param name="id">文档ID字符串</param>/// <returns>找到的文档或默认值</returns>public async Task<T> GetByIdAsync(string id){try{// 构建ID过滤器(MongoDB默认使用"_id"字段)var filter = Builders<T>.Filter.Eq("_id", ObjectId.Parse(id));// 异步查找并返回第一个匹配的文档return await _collection.Find(filter).FirstOrDefaultAsync();}catch{return default; // 出错返回默认值}}/// <summary>/// 获取所有文档/// </summary>/// <returns>所有文档列表或默认值</returns>public async Task<List<T>> GetAllAsync(){try{// 使用空过滤器获取所有文档return await _collection.Find(_ => true).ToListAsync();}catch{return default; // 出错返回默认值}}/// <summary>/// 条件查询/// </summary>/// <param name="filter">LINQ表达式过滤器</param>/// <returns>匹配条件的文档集合</returns>public async Task<IEnumerable<T>> FindByConditionAsync(Expression<Func<T, bool>> filter){try{// 使用表达式过滤器查询文档return await _collection.Find(filter).ToListAsync();}catch{return default; // 出错返回默认值}}/// <summary>/// 更新单个文档/// </summary>/// <param name="filter">查询过滤器</param>/// <param name="update">更新定义</param>/// <returns>实际修改的文档数量</returns>public async Task<int> UpdateAsync(Expression<Func<T, bool>> filter, UpdateDefinition<T> update){try{// 异步更新单个文档var result = await _collection.UpdateOneAsync(filter, update);// 返回实际修改的文档数量return (int)result.ModifiedCount;}catch{return 0; // 出错返回0}}/// <summary>/// 替换整个文档/// </summary>/// <param name="filter">查询过滤器</param>/// <param name="replacement">替换文档</param>/// <returns>实际修改的文档数量</returns>public async Task<int> UpdateAsync(Expression<Func<T, bool>> filter, T replacement){try{// 异步替换整个文档var result = await _collection.ReplaceOneAsync(filter, replacement);// 返回实际修改的文档数量return (int)result.ModifiedCount;}catch{return 0; // 出错返回0}}/// <summary>/// 删除单个文档/// </summary>/// <param name="filter">查询过滤器</param>/// <returns>实际删除的文档数量</returns>public async Task<int> DeleteAsync(Expression<Func<T, bool>> filter){try{// 异步删除单个文档var result = await _collection.DeleteOneAsync(filter);// 返回实际删除的文档数量return (int)result.DeletedCount;}catch{return 0; // 出错返回0}}/// <summary>/// 根据ID删除文档/// </summary>/// <param name="id">文档ID</param>/// <returns>实际删除的文档数量</returns>public async Task<int> DeleteByIdAsync(string id){try{// 构建ID过滤器var filter = Builders<T>.Filter.Eq("_id", ObjectId.Parse(id));// 异步删除匹配的文档var result = await _collection.DeleteOneAsync(filter);// 返回实际删除的文档数量return (int)result.DeletedCount;}catch{return 0; // 出错返回0}}/// <summary>/// 批量删除文档/// </summary>/// <param name="filter">查询过滤器</param>/// <returns>实际删除的文档数量</returns>public async Task<int> DeleteManyAsync(Expression<Func<T, bool>> filter){try{// 异步删除多个文档var result = await _collection.DeleteManyAsync(filter);// 返回实际删除的文档数量return (int)result.DeletedCount;}catch{return 0; // 出错返回0}}#endregion#region 高级操作/// <summary>/// 分页查询/// </summary>/// <param name="filter">查询过滤器</param>/// <param name="pageNumber">页码</param>/// <param name="pageSize">每页大小</param>/// <param name="sort">排序定义(可选)</param>/// <returns>包含数据和总数的元组</returns>public async Task<(List<T> Data, int Total)> PaginateAsync(Expression<Func<T, bool>> filter, int pageNumber, int pageSize, SortDefinition<T> sort = null){try{// 获取匹配条件的文档总数var count = (int)await _collection.CountDocumentsAsync(filter);// 构建基础查询var query = _collection.Find(filter);// 如果提供了排序条件,则应用排序if (sort != null){query = query.Sort(sort);}// 执行分页查询:跳过前面的记录,限制返回数量var data = await query.Skip((pageNumber - 1) * pageSize).Limit(pageSize).ToListAsync();// 返回数据和总数return (data, count);}catch{return (null, 0); // 出错返回空数据和0总数}}/// <summary>/// 聚合查询/// </summary>/// <typeparam name="TResult">结果类型</typeparam>/// <param name="pipeline">聚合管道定义</param>/// <returns>聚合结果集合</returns>public async Task<IEnumerable<TResult>> AggregateAsync<TResult>(PipelineDefinition<T, TResult> pipeline){// 异步执行聚合管道并返回结果列表return await _collection.Aggregate(pipeline).ToListAsync();}/// <summary>/// 创建索引/// </summary>/// <param name="keys">索引键定义</param>/// <param name="options">索引选项(可选)</param>/// <returns>创建的索引名称</returns>public async Task<string> CreateIndexAsync(IndexKeysDefinition<T> keys, CreateIndexOptions options = null){// 异步创建索引并返回索引名称return await _collection.Indexes.CreateOneAsync(new CreateIndexModel<T>(keys, options));}/// <summary>/// 批量操作/// </summary>/// <param name="requests">批量写操作模型集合</param>/// <returns>批量操作结果</returns>public async Task<BulkWriteResult<T>> BulkWriteAsync(IEnumerable<WriteModel<T>> requests){// 异步执行批量写操作return await _collection.BulkWriteAsync(requests);}/// <summary>/// 文档计数/// </summary>/// <param name="filter">查询过滤器(可选)</param>/// <returns>匹配条件的文档数量</returns>public async Task<long> CountAsync(Expression<Func<T, bool>> filter = null){// 根据是否提供过滤器返回相应计数return filter == null? await _collection.CountDocumentsAsync(_ => true) // 无过滤器时计算所有文档: await _collection.CountDocumentsAsync(filter); // 有过滤器时计算匹配文档}#endregion#region 事务支持/// <summary>/// 执行事务操作/// </summary>/// <param name="actions">要在事务中执行的操作</param>public async Task ExecuteInTransactionAsync(Func<IClientSessionHandle, IMongoCollection<T>, Task> actions){// 创建会话using var session = await _collection.Database.Client.StartSessionAsync();// 开始事务session.StartTransaction();try{// 执行用户定义的操作await actions(session, _collection);// 提交事务await session.CommitTransactionAsync();}catch{// 出错时中止事务await session.AbortTransactionAsync();throw; // 重新抛出异常}}#endregion}
}
案例如下
3.2.仓储接口
方便使用仓储给所有实现加接口
using MongoDB.Driver;
using System.Linq.Expressions;namespace Frame3_DataRepository.MongoRepository
{public interface IMongoRepository<T> where T : class{/// <summary>/// 插入文档/// </summary>/// <param name="document"></param>/// <returns></returns>Task<int> InsertAsync(T document);/// <summary>/// 批量插入文档/// </summary>Task<int> InsertAsync(IEnumerable<T> documents);/// <summary>/// 根据ID获取文档/// </summary>Task<T> GetByIdAsync(string id);/// <summary>/// 获取所有文档/// </summary>Task<List<T>> GetAllAsync();/// <summary>/// 条件查询/// </summary>Task<IEnumerable<T>> FindByConditionAsync(Expression<Func<T, bool>> filter);/// <summary>/// 更新单个文档/// </summary>Task<int> UpdateAsync(Expression<Func<T, bool>> filter, UpdateDefinition<T> update);/// <summary>/// 替换整个文档/// </summary>Task<int> UpdateAsync(Expression<Func<T, bool>> filter, T replacement);/// <summary>/// 删除单个文档/// </summary>Task<int> DeleteAsync(Expression<Func<T, bool>> filter);/// <summary>/// 根据ID删除文档/// </summary>Task<int> DeleteByIdAsync(string id);/// <summary>/// 批量删除文档/// </summary>Task<int> DeleteManyAsync(Expression<Func<T, bool>> filter);/// <summary>/// 分页查询/// </summary>Task<(List<T> Data, int Total)> PaginateAsync(Expression<Func<T, bool>> filter, int pageNumber, int pageSize, SortDefinition<T> sort = null);/// <summary>/// 聚合查询/// </summary>Task<IEnumerable<TResult>> AggregateAsync<TResult>(PipelineDefinition<T, TResult> pipeline);/// <summary>/// 创建索引/// </summary>Task<string> CreateIndexAsync(IndexKeysDefinition<T> keys, CreateIndexOptions options = null);/// <summary>/// 批量操作/// </summary>Task<BulkWriteResult<T>> BulkWriteAsync(IEnumerable<WriteModel<T>> requests);/// <summary>/// 文档计数/// </summary>Task<long> CountAsync(Expression<Func<T, bool>> filter = null);/// <summary>/// 执行事务操作/// </summary>Task ExecuteInTransactionAsync(Func<IClientSessionHandle, IMongoCollection<T>, Task> actions);}
}
案例如下
4.获取配置和注册
获取连接字符串、注册Mongo实例和仓储
4.1.添加配置获取方法
using Microsoft.Extensions.Configuration;namespace Frame4_LibraryCore.BaseConfig
{/// <summary>/// 全局配置/// </summary>public static class Config{/// <summary>/// 从指定的 JSON 配置文件中读取配置,并反序列化为指定类型/// </summary>/// <typeparam name="T">目标配置类型(如 RedisSettings、DatabaseSettings 等)</typeparam>/// <param name="fileName">JSON 配置文件名(如 "appsettings.json")</param>/// <param name="sessions">配置节点名称(如 "RedisSettings")</param>/// <returns>返回绑定后的强类型配置对象</returns>public static T GetSetting<T>(string fileName, string sessions){//创建 ConfigurationBuilder 实例,用于构建配置var builder = new ConfigurationBuilder()//设置配置文件的基础路径为当前程序运行目录.SetBasePath(Directory.GetCurrentDirectory())//添加 JSON 文件作为配置源://- fileName: 指定要加载的 JSON 文件//- optional: false 表示文件必须存在,否则抛出异常//- reloadOnChange: true 表示文件修改时自动重新加载.AddJsonFile(fileName, optional: false, reloadOnChange: true);//构建配置对象(IConfigurationRoot)IConfigurationRoot config = builder.Build();//获取指定配置节点(sessions),并将其反序列化为类型 Tvar conn = config.GetSection(sessions).Get<T>();//返回反序列化后的配置对象return conn;}}
}
案例如下
4.2.注册
添加注册方法方便程序注册
using Frame4_LibraryCore.BaseConfig;
using Microsoft.Extensions.DependencyInjection;
using MongoDB.Driver;namespace Frame3_DataRepository.MongoRepository
{/// <summary>/// MongoDB仓储相关服务/// </summary>public static class MongoExtension{/// <summary>/// 扩展方法:向IServiceCollection添加MongoDB仓储相关服务/// </summary>/// <param name="services">服务集合</param>/// <returns>配置后的服务集合</returns>public static IServiceCollection AddMongoRepository(this IServiceCollection services){// 从配置文件"dbsettings.json"的"MongoDBSettings"节点获取MongoDB配置选项var mongoOptions = Config.GetSetting<MongoOptions>("dbsettings.json", "MongoDBSettings");// 注册IMongoClient为单例服务(因为MongoClient是线程安全的,适合单例)services.AddSingleton<IMongoClient>(sp => new MongoClient(mongoOptions.MongoConnStr));// 注册IMongoDatabase为作用域服务(每个请求一个实例)services.AddScoped(sp =>{// 从服务容器获取已注册的IMongoClient实例var client = sp.GetRequiredService<IMongoClient>();// 使用配置中的数据库名称获取数据库实例return client.GetDatabase(mongoOptions.DatabaseName);});// 注册泛型仓储接口和实现(IMongoRepository<>和MongoRepository<>)services.AddScoped(typeof(IMongoRepository<>), typeof(MongoRepository<>));// 返回配置好的服务集合以支持链式调用return services;}}
}
案例如下
新增好注册方法后即可在 Program 或 Startup 中注册。
//注册MongoDB服务
builder.Services.AddMongoRepository();
案例如下
5.常规使用案例
下面是 实体、实现、接口和控制器的使用案例
5.1实体
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;namespace Frame2_DataModel.MongoEntity.Product
{/// <summary>/// 产品表-Mongo/// </summary>public class ProductEntity{[BsonId] // 标记这是 MongoDB 的 _id 字段[BsonRepresentation(BsonType.ObjectId)] // 表示该字段使用 ObjectId 类型public string Id { get; set; }/// <summary>/// 产品名称/// </summary>public string ProductName { get; set; }/// <summary>/// 价格/// </summary>public decimal Price { get; set; }/// <summary>/// 库存/// </summary>public int Stock { get; set; }}
}
案例如下
5.2.实现
using Frame_Service.IService.Product;
using Frame1_Service.IService.Product;
using Frame2_DataModel.MongoEntity.Product;
using Frame3_DataRepository.MongoRepository;
using Frame6_LibraryUtility;namespace Frame1_Service.Service.Product
{/// <summary>/// MongoDB测试实现/// </summary>public class ProductSvr : BaseService, IProductSvr{/// <summary>/// 产品-Mongo/// </summary>private readonly IMongoRepository<ProductEntity> _productSvr;/// <summary>/// 构造函数/// </summary>public ProductSvr(IMongoRepository<ProductEntity> productSvr){_productSvr = productSvr;}#region 查询数据/// <summary>/// 获取所有产品/// </summary>/// <returns></returns>public async Task<ResultModel<List<ProductEntity>>> GetProductAll(){var result = new ResultModel<List<ProductEntity>>() { Data = null };var list = await _productSvr.GetAllAsync();result.Code = ResultCodeEnum.Success;result.Msg = "获取成功";result.Data = list;return result;}/// <summary>/// 根据Id获取产品/// </summary>/// <param name="id"></param>/// <returns></returns>public async Task<ResultModel<ProductEntity>> GetByIdProduct(string id){var result = new ResultModel<ProductEntity>() { Data = null };var list = await _productSvr.GetByIdAsync(id);result.Msg = "获取成功";result.Data = list;return result;}/// <summary>/// 获取所有产品-分页/// </summary>/// <param name="model"></param>/// <returns></returns>public async Task<ResultPageModel<List<ProductEntity>>> GetProductPageList(PageList model){var result = new ResultPageModel<List<ProductEntity>>() { Data = null };var (products, total) = await _productSvr.PaginateAsync(filter: p => true, pageNumber: model.PageIndex, pageSize: model.PageSize);result.TotalCount = total;result.PageIndex = model.PageIndex;result.PageSize = model.PageSize;result.Data = products;result.Msg = "获取成功";return result;}#endregion#region 修改数据/// <summary>/// 保存产品/// </summary>/// <param name="model"></param>/// <returns></returns>public async Task<ResultModel<int>> SaveProduct(ProductEntity model){var result = new ResultModel<int>() { Data = 0 };#region 数据校验if (model.IsEmpty()){result.Code = ResultCodeEnum.Error;result.Msg = "传参错误";return result;}#endregionint row = 0;if (model.Id.IsEmpty()){row = await _productSvr.InsertAsync(model);}else{row = await _productSvr.UpdateAsync(a => a.Id.Equals(model.Id), model);}if (row > 0){result.Code = ResultCodeEnum.Success;result.Msg = "操作成功";result.Data = row;}return result;}/// <summary>/// 根据Id删除产品/// </summary>/// <param name="id"></param>/// <returns></returns>public async Task<ResultModel<int>> RemoveProduct(string id){var result = new ResultModel<int>() { Data = 0 };int row = await _productSvr.DeleteByIdAsync(id);if (row > 0){result.Code = ResultCodeEnum.Success;result.Msg = "操作成功";result.Data = row;}return result;}#endregion}
}
案例如下
5.3.接口
using Frame2_DataModel.MongoEntity.Product;
using Frame6_LibraryUtility;namespace Frame1_Service.IService.Product
{/// <summary>/// MongoDB测试接口/// </summary>public interface IProductSvr{/// <summary>/// 获取所有产品/// </summary>/// <returns></returns>Task<ResultModel<List<ProductEntity>>> GetProductAll();/// <summary>/// 根据Id获取产品/// </summary>/// <param name="id"></param>/// <returns></returns>Task<ResultModel<ProductEntity>> GetByIdProduct(string id);/// <summary>/// 获取所有产品-分页/// </summary>/// <param name="model"></param>/// <returns></returns>Task<ResultPageModel<List<ProductEntity>>> GetProductPageList(PageList model);/// <summary>/// 保存产品/// </summary>/// <param name="model"></param>/// <returns></returns>Task<ResultModel<int>> SaveProduct(ProductEntity model);/// <summary>/// 根据Id删除产品/// </summary>/// <param name="id"></param>/// <returns></returns>Task<ResultModel<int>> RemoveProduct(string id);}
}
案例如下
5.4.控制器
using Frame1_Service.IService.Product;
using Frame2_DataModel.MongoEntity.Product;
using Frame4_LibraryCore.BaseConfig;
using Frame6_LibraryUtility;
using Microsoft.AspNetCore.Mvc;namespace DemoAPI.Controllers
{/// <summary>/// MongoDB测试控制器/// </summary>//[Authorize]// 保护整个控制器[Route("api/[controller]/[action]")]//标记路由地址规格[ApiController] // 标记该类为 API 控制器,启用一些默认的行为,如模型绑定、输入验证等[ApiExplorerSettings(GroupName = nameof(ApiVersionInfo.V1))]//设置控制器的API版本public class ProductController : BaseController{/// <summary>/// 产品/// </summary>private readonly IProductSvr _IProductSvr;/// <summary>/// 构造/// </summary>/// <param name="productSvr"></param>public ProductController(IProductSvr productSvr){_IProductSvr = productSvr;}/// <summary>/// 获取所有产品/// </summary>/// <returns></returns>[HttpGet]public async Task<ResultModel<List<ProductEntity>>> GetProductAll() => await _IProductSvr.GetProductAll();/// <summary>/// 根据Id获取产品/// </summary>/// <param name="id"></param>/// <returns></returns>[HttpGet]public async Task<ResultModel<ProductEntity>> GetByIdProduct(string id) => await _IProductSvr.GetByIdProduct(id);/// <summary>/// 获取所有产品-分页/// </summary>/// <param name="model"></param>/// <returns></returns>[HttpPost]public async Task<ResultPageModel<List<ProductEntity>>> GetProductPageList(PageList model) => await _IProductSvr.GetProductPageList(model);/// <summary>/// 保存产品/// </summary>/// <param name="model"></param>/// <returns></returns>[HttpPost]public async Task<ResultModel<int>> SaveProduct(ProductEntity model) => await _IProductSvr.SaveProduct(model);/// <summary>/// 根据Id删除产品/// </summary>/// <param name="id"></param>/// <returns></returns>[HttpGet]public async Task<ResultModel<int>> RemoveProduct(string id) => await _IProductSvr.RemoveProduct(id);}
}
案例如下