MongoDB 与 EF Core 深度整合实战:打造结构清晰的 Web API 应用

06-01 1328阅读

题纲

    • MongoDB 字符串
      • 连接 URI
      • C# 连接字符串实例
      • 实现一个电影信息查询 demo
        • 创建项目
        • 创建实体
        • 实现 DbContext 上下文
        • 仓储实现
        • 服务实现
        • 控制器实现
        • 服务注册
        • 快照注入数据库连接配置
          • 1. 注册配置类
          • 2. 注入 `IOptionsSnapshot`
          • 3. 配置文件 appsettings.json 示例
          • 总结
            • 过去,C# 开发者可以使用 MongoDB.Driver(MongoDB 的 C# 驱动程序),但无法获得针对 EF Core 的第一方支持。

            • 现在,随着 MongoDB.EntityFrameworkCore(适用于 EF Core 的官方 MongoDB 提供程序) 的正式发布,开发者在使用 MongoDB 构建生产级工作负载时可以放心地使用 C# 和 EF Core。

              • .NET Nuget 包发布情况:

                github 项目地址,https://github.com/mongodb/mongo-efcore-provider

                MongoDB 字符串

                接下来介绍如何使用 MongoDB.Driver 连接到 MongoDB 实例或副本集部署。

                连接 URI

                连接 URI(也称为连接字符串)可告知驱动程序如何连接到 MongoDB 部署,以及连接后如何进行操作。

                标准连接字符串包括以下部分:

                字段说明
                mongodb://必需。将其标识为标准连接格式中字符串的前缀。
                username:password@可选。身份验证凭证。如果包含这些内容,客户端将根据 authSource 中指定的数据库对用户进行身份验证。
                host[:port]必需。运行 MongoDB 的主机和可选端口号。如果未包含端口号,则驱动程序将使用默认端口 27017。
                /defaultauthdb可选。如果连接字符串包含 username:password@ 身份验证档案但未指定 authSource 选项,则要使用的身份验证数据库。如果您不包含这一内容,客户端将根据 admin 数据库对用户进行身份验证。
                ?可选。将连接特定选项指定为 = 对的查询字符串。有关这些选项的完整说明,请参阅连接选项。

                连接选项 参考,https://www.mongodb.com/zh-cn/docs/drivers/csharp/current/fundamentals/connection/connection-options/#std-label-csharp-connection-options

                C# 连接字符串实例

                1. 连接字符串语法
                mongodb://:@:,:,:/?replicaSet=
                
                1. 参数说明
                • :MongoDB 的认证用户名(如果没有认证可省略)
                • :MongoDB 的用户密码(如果没有认证可省略)
                • , , :MongoDB 副本集的各个节点 IP 或主机名;
                • :MongoDB 实例的端口号,默认为 27017
                • :MongoDB 副本集的名称
                  1. 示例代码
                  var connectionString = "mongodb://admin:password@host1:27017,host2:27017,host3:27017/?replicaSet=myReplicaSet";
                  var client = new MongoClient(connectionString);
                  var database = client.GetDatabase("test");
                  
                  1. 其他常用选项

                  你还可以添加额外参数到连接字符串中,例如:

                  • ssl=true:启用 SSL 加密连接
                  • authSource=admin:指定认证数据库
                  • readPreference=secondaryPreferred:优先读取从节点

                    示例:

                    mongodb://admin:password@host1:27017,host2:27017,host3:27017/test?replicaSet=myReplicaSet&ssl=true&authSource=admin
                    

                    实现一个电影信息查询 demo

                    • 添加 nuget 包
                      dotnet add package MongoDB.EntityFrameworkCore --version 9.0.0
                      
                      • 当前包版本为 9.0.0

                        MongoDB 与 EF Core 深度整合实战:打造结构清晰的 Web API 应用

                        MongoDB EF Core 提供程序需要启用实体框架核心8或9。.NET 8或更高版本以及 MongoDB数据库服务器5.0或更高级别,最好是在 支持事务 的配置中。

                        创建项目

                        使用 .NET CLI 创建一个名为 MongoDbExample 的 Web API 项目,可以使用以下命令:

                        dotnet new webapi -n MongoDbExample
                        # 进入项目
                        cd MongoDbExample
                        dotnet run
                        

                        创建实体

                        创建两个实体类,分别模拟电影信息和电影商品,定义如下:

                        • Movie 电影信息
                          using MongoDB.Bson.Serialization.Attributes;
                          using MongoDB.Bson;
                          namespace MongoDbExample.Database.Collections;
                          public sealed class Movie
                          {
                              [BsonId]
                              [BsonElement("_id")]
                              public ObjectId Id { get; set; }
                              [BsonElement("title")]
                              public string Title { get; set; } = null!;
                              [BsonElement("rated")]
                              public string Rated { get; set; } = null!;
                              [BsonElement("plot")]
                              public string Plot { get; set; } = null!;
                          }
                          
                          • Product 电影商品
                            using MongoDB.Bson.Serialization.Attributes;
                            using MongoDB.Bson;
                            namespace MongoDbExample.Database.Collections;
                            public sealed class Product
                            {
                                [BsonId]
                                [BsonElement("_id")]
                                public ObjectId Id { get; set; } 
                                [BsonElement("name")]
                                public string? Name { get; set; }
                                [BsonElement("price")]
                                public decimal Price { get; set; }
                            }
                            

                            实现 DbContext 上下文

                            • CinemaAppDbContext
                              using Microsoft.EntityFrameworkCore;
                              using MongoDB.Driver;
                              using MongoDB.EntityFrameworkCore.Extensions;
                              using MongoDbExample.Database.Collections;
                              namespace MongoDbExample;
                              public sealed class CinemaAppDbContext(ILogger logger,
                                  DbContextOptions options) : DbContext(options)
                              {
                                  public DbSet Products { get; init; }
                                  public DbSet Movies { get; init; }
                                  public static CinemaAppDbContext Create(
                                      ILogger logger,
                                      IMongoDatabase database)
                                  {
                                      var options = new DbContextOptionsBuilder()
                                          .UseMongoDB(database.Client, database.DatabaseNamespace.DatabaseName)
                                          .Options;
                                      return new CinemaAppDbContext(logger, options);
                                  }
                                  public static IMongoDatabase GetDatabase(MongoClientSettings clientSettings, string name, MongoDatabaseSettings? dbSettings = null)
                                  {
                                      var client = new MongoClient(clientSettings);
                                      return dbSettings is null ? client.GetDatabase(name) : client.GetDatabase(name, dbSettings);
                                  }
                                  public static IMongoDatabase GetDatabase(string connectionString, string name, MongoDatabaseSettings? dbSettings = null)
                                  {
                                      var client = new MongoClient(connectionString);
                                      return dbSettings is null ? client.GetDatabase(name) : client.GetDatabase(name, dbSettings);
                                  }
                                  protected override void OnModelCreating(ModelBuilder modelBuilder)
                                  {
                                      base.OnModelCreating(modelBuilder);
                                      logger.LogInformation("Configuring entity mappings..."); 
                                      // 实体映射到集合
                                      modelBuilder.Entity().ToCollection("products");
                                      modelBuilder.Entity().ToCollection("movies");
                                  }
                              }
                              

                              仓储实现

                              创建仓储模式,实现上面实体的 crud 操作,实现如下:

                              • ICinemaAppRepository,定义仓储规范
                                using MongoDB.Bson;
                                using MongoDbExample.Database.Collections;
                                namespace MongoDbExample.Database.Repositorys;
                                public interface ICinemaAppRepository
                                {
                                    #region Movie
                                    IAsyncEnumerable GetMoviesAsync();
                                    Task GetMovieByIdAsync(ObjectId id);
                                    Task AddMovieAsync(Movie movie);
                                    Task UpdateMovieAsync(Movie movie);
                                    Task DeleteMovieAsync(ObjectId id);
                                    #endregion
                                    #region Product
                                    IAsyncEnumerable GetProductsAsync();
                                    Task GetProductByIdAsync(ObjectId id);
                                    Task AddProductAsync(Product product);
                                    Task UpdateProductAsync(Product product);
                                    Task DeleteProductAsync(ObjectId id);
                                    #endregion
                                }
                                
                                • CinemaAppRepository 仓储实现

                                  此处使用 partial class (部分类)模拟工程化结构,分别拆分为两个独立的文件 CinemaAppRepository.Movie.cs 和 CinemaAppRepository.Product.cs。

                                  using MongoDbExample.Database.Repositorys;
                                  namespace MongoDbExample.Repositorys;
                                  // 实现接口 ICinemaAppRepository
                                  public partial class CinemaAppRepository(ILogger logger, 
                                      CinemaAppDbContext dbContext) : ICinemaAppRepository
                                  {
                                      // CinemaAppRepository.Movie.cs
                                      // CinemaAppRepository.Product.cs
                                  }
                                  
                                  • CinemaAppRepository.Movie.cs
                                    using Microsoft.EntityFrameworkCore;
                                    using MongoDB.Bson;
                                    using MongoDbExample.Database.Collections;
                                    namespace MongoDbExample.Repositorys;
                                    public partial class CinemaAppRepository
                                    {
                                        #region IMovieRepository
                                        public async IAsyncEnumerable GetMoviesAsync()
                                        {
                                            var movies = await dbContext.Movies.ToListAsync();
                                            foreach (var movie in movies)
                                            {
                                                yield return movie;
                                            }
                                        }
                                        public Task GetMovieByIdAsync(ObjectId id) 
                                           => dbContext.Movies.FindAsync(id).AsTask();
                                        public async Task AddMovieAsync(Movie movie)
                                        {
                                            if (movie.Id == ObjectId.Empty)
                                            {
                                                movie.Id = ObjectId.GenerateNewId(); // 确保生成新的 ObjectId
                                            }
                                            
                                            await dbContext.Movies.AddAsync(movie);
                                            int rcount = await dbContext.SaveChangesAsync();
                                            return (rcount > 0, movie.Id);
                                        }
                                        public async Task UpdateMovieAsync(Movie movie)
                                        {
                                            dbContext.Movies.Update(movie);
                                            int rcount = await dbContext.SaveChangesAsync();
                                            return (rcount > 0, movie.Id);
                                        }
                                        public async Task DeleteMovieAsync(ObjectId id)
                                        {
                                            int rcount = 0;
                                            var movie = await dbContext.Movies.FindAsync(id);
                                            if (movie != null)
                                            {
                                                dbContext.Movies.Remove(movie);
                                                rcount = await dbContext.SaveChangesAsync();
                                            }
                                            return rcount > 0;
                                        }
                                        #endregion
                                    }
                                    
                                    • CinemaAppRepository.Product.cs
                                      using Microsoft.EntityFrameworkCore;
                                      using MongoDB.Bson;
                                      using MongoDbExample.Database.Collections;
                                      namespace MongoDbExample.Repositorys;
                                      public partial class CinemaAppRepository
                                      {
                                          #region Product
                                          public async IAsyncEnumerable GetProductsAsync()
                                          {
                                              var products = await dbContext.Products.ToListAsync();
                                              foreach (var product in products)
                                              {
                                                  yield return product;
                                              }
                                          }
                                          public async Task GetProductByIdAsync(ObjectId id)
                                          {
                                              return await dbContext.Products.FindAsync(id);
                                          }
                                          public async Task AddProductAsync(Product product)
                                          {
                                              if (product.Id == ObjectId.Empty)
                                              {
                                                  product.Id = ObjectId.GenerateNewId(); // 确保生成新的 ObjectId
                                              }
                                              await dbContext.Products.AddAsync(product);
                                              int rcount = await dbContext.SaveChangesAsync();
                                              return (rcount > 0, product.Id);
                                          }
                                          public async Task UpdateProductAsync(Product product)
                                          {
                                              dbContext.Products.Update(product);
                                              int rcount = await dbContext.SaveChangesAsync();
                                              return (rcount > 0, product.Id);
                                          }
                                          public async Task DeleteProductAsync(ObjectId id)
                                          {
                                              int rcount = 0;
                                              var product = await dbContext.Products.FindAsync(id);
                                              if (product != null)
                                              {
                                                  dbContext.Products.Remove(product);
                                                  rcount = await dbContext.SaveChangesAsync();
                                              }
                                              return rcount > 0;
                                          }
                                          #endregion
                                      }
                                      

                                      服务实现

                                      定义服务接口,分别实现如下:

                                      • IMovieService
                                        using MongoDB.Bson;
                                        using MongoDbExample.Database.Collections;
                                        namespace MongoDbExample.Services;
                                        public interface IMovieService
                                        {
                                            IAsyncEnumerable GetMoviesAsync();
                                            Task GetMovieByIdAsync(ObjectId id);
                                            Task CreateMovieAsync(Movie movie);
                                            Task UpdateMovieAsync(Movie movie);
                                            Task DeleteMovieAsync(ObjectId id);
                                        }
                                        
                                        • IProductService
                                          using MongoDB.Bson;
                                          using MongoDbExample.Database.Collections;
                                          namespace MongoDbExample.Services;
                                          public interface IProductService
                                          {
                                              IAsyncEnumerable GetProductsAsync();
                                              Task GetProductByIdAsync(ObjectId id);
                                              Task CreateProductAsync(Product product);
                                              Task UpdateProductAsync(Product product);
                                              Task DeleteProductAsync(ObjectId id);
                                          }
                                          

                                          实现接口规范,代码如下:

                                          • MovieService
                                            using MongoDB.Bson;
                                            using MongoDbExample.Database.Collections;
                                            using MongoDbExample.Database.Repositorys;
                                            namespace MongoDbExample.Services;
                                            public class MovieService(ICinemaAppRepository _repository) : IMovieService
                                            {
                                                #region Movie
                                                public IAsyncEnumerable GetMoviesAsync() => _repository.GetMoviesAsync();
                                                public async Task GetMovieByIdAsync(ObjectId id)
                                                {
                                                    var movie = await _repository.GetMovieByIdAsync(id);
                                                    if (movie == null) return (false, "Movie not found", null);
                                                    return (true, "Success", movie);
                                                }
                                                public async Task CreateMovieAsync(Movie movie)
                                                {
                                                    if (string.IsNullOrWhiteSpace(movie.Title))
                                                        return (false, "Movie title is required.", ObjectId.Empty);
                                                    var (isOk, id) = await _repository.AddMovieAsync(movie);
                                                    if (!isOk) return (false, "Failed to add movie.", ObjectId.Empty);
                                                    return (true, "Movie added successfully.", id);
                                                }
                                                public async Task UpdateMovieAsync(Movie movie)
                                                {
                                                    var existing = await _repository.GetMovieByIdAsync(movie.Id);
                                                    if (existing == null) return (false, "Movie not found.", ObjectId.Empty);
                                                    var (isOk, id) = await _repository.UpdateMovieAsync(movie);
                                                    if (!isOk) return (false, "Failed to update movie.", ObjectId.Empty);
                                                    return (true, "Movie updated successfully.", id);
                                                }
                                                public async Task DeleteMovieAsync(ObjectId id)
                                                {
                                                    var exists = await _repository.GetMovieByIdAsync(id);
                                                    if (exists == null) return (false, "Movie not found.");
                                                    var success = await _repository.DeleteMovieAsync(id);
                                                    if (!success) return (false, "Failed to delete movie.");
                                                    return (true, "Movie deleted successfully.");
                                                }
                                                #endregion
                                            }
                                            
                                            • ProductService
                                              using MongoDB.Bson;
                                              using MongoDbExample.Database.Collections;
                                              using MongoDbExample.Database.Repositorys;
                                              namespace MongoDbExample.Services;
                                              public class ProductService(ICinemaAppRepository repository) : IProductService
                                              {
                                                  #region Product
                                                  public IAsyncEnumerable GetProductsAsync() => repository.GetProductsAsync();
                                                  public async Task GetProductByIdAsync(ObjectId id)
                                                  {
                                                      var product = await repository.GetProductByIdAsync(id);
                                                      if (product == null) return (false, "Product not found", null);
                                                      return (true, "Success", product);
                                                  }
                                                  public async Task CreateProductAsync(Product product)
                                                  {
                                                      if (string.IsNullOrWhiteSpace(product.Name))
                                                          return (false, "Product name is required.", ObjectId.Empty);
                                                      var (isOk, id) = await repository.AddProductAsync(product);
                                                      if (!isOk) return (false, "Failed to add product.", ObjectId.Empty);
                                                      return (true, "Product added successfully.", id);
                                                  }
                                                  public async Task UpdateProductAsync(Product product)
                                                  {
                                                      var existing = await repository.GetProductByIdAsync(product.Id);
                                                      if (existing == null) return (false, "Product not found.", ObjectId.Empty);
                                                      var (isOk, id) = await repository.UpdateProductAsync(product);
                                                      if (!isOk) return (false, "Failed to update product.", ObjectId.Empty);
                                                      return (true, "Product updated successfully.", id);
                                                  }
                                                  public async Task DeleteProductAsync(ObjectId id)
                                                  {
                                                      var exists = await repository.GetProductByIdAsync(id);
                                                      if (exists == null) return (false, "Product not found.");
                                                      var success = await repository.DeleteProductAsync(id);
                                                      if (!success) return (false, "Failed to delete product.");
                                                      return (true, "Product deleted successfully.");
                                                  }
                                                  #endregion
                                              }
                                              

                                              控制器实现

                                              此处只给出 Products 接口实现(Movies类似)。

                                              using Microsoft.AspNetCore.Mvc;
                                              using MongoDB.Bson;
                                              using MongoDbExample.Database.Collections;
                                              using MongoDbExample.Services;
                                              namespace MongoDbExample.Controllers;
                                              [Route("api/[controller]")]
                                              [ApiController]
                                              public class ProductsController(IProductService productService) : ControllerBase
                                              {
                                                  [HttpGet]
                                                  public IAsyncEnumerable GetProducts() => productService.GetProductsAsync();
                                                  [HttpGet("{id}")]
                                                  public async Task GetProduct(ObjectId id)
                                                  {
                                                      var (success, msg, data) = await productService.GetProductByIdAsync(id);
                                                      return success ? Ok(data) : NotFound(new { message = msg });
                                                  }
                                                  [HttpPost]
                                                  public async Task CreateProduct(Product product)
                                                  {
                                                      var (success, msg, id) = await productService.CreateProductAsync(product);
                                                      return success
                                                          ? CreatedAtAction(nameof(GetProduct), new { id }, new { message = msg, id })
                                                          : BadRequest(new { message = msg });
                                                  }
                                                  [HttpPut]
                                                  public async Task UpdateProduct(Product product)
                                                  {
                                                      var (success, msg, id) = await productService.UpdateProductAsync(product);
                                                      return success
                                                          ? Ok(new { message = msg, id })
                                                          : BadRequest(new { message = msg });
                                                  }
                                                  [HttpDelete("{id}")]
                                                  public async Task DeleteProduct(ObjectId id)
                                                  {
                                                      var (success, msg) = await productService.DeleteProductAsync(id);
                                                      return success ? Ok(new { message = msg }) : NotFound(new { message = msg });
                                                  }
                                              }
                                              

                                              服务注册

                                              在 Program.cs 中实现服务注册。

                                              using MongoDB.Driver;
                                              using MongoDbExample;
                                              using MongoDbExample.Database.Repositorys;
                                              using MongoDbExample.Repositorys;
                                              var builder = WebApplication.CreateBuilder(args);
                                              // 添加日志等基础服务
                                              // 创建 ILoggerFactory
                                              var loggerFactory = LoggerFactory.Create(loggingBuilder =>
                                              {
                                                  loggingBuilder.AddConsole();
                                              });
                                              var logger = loggerFactory.CreateLogger();
                                              // Add services to the container.
                                              // 从环境变量获取连接字符串
                                              var connectionString = Environment.GetEnvironmentVariable("MONGODB_URI");
                                              if (connectionString == null)
                                              {
                                                  Console.WriteLine("You must set your 'MONGODB_URI' environment variable. To learn how to set it, see https://www.mongodb.com/docs/drivers/csharp/current/quick-start/#set-your-connection-string");
                                                  Environment.Exit(0);
                                              }
                                              // 创建 DbContext 实例
                                              var database = CinemaAppDbContext.GetDatabase(connectionString, "sample_mflix");
                                              var cinemaContext = CinemaAppDbContext.Create(logger, database);
                                              {
                                                  var movie = cinemaContext.Movies.First(m => m.Title == "Back to the Future");
                                                  Console.WriteLine(movie.Plot);
                                              }
                                              // 将实例注册为服务
                                              builder.Services.AddSingleton(cinemaContext);
                                              // 注册 ICinemaAppRepository 仓储
                                              builder.Services.AddScoped();
                                              // 添加控制器
                                              builder.Services.AddControllers();
                                              // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
                                              builder.Services.AddOpenApi();
                                              var app = builder.Build();
                                              // Configure the HTTP request pipeline.
                                              if (app.Environment.IsDevelopment())
                                              {
                                                  app.MapOpenApi();
                                              }
                                              app.UseAuthorization();
                                              app.MapControllers();
                                              await app.RunAsync();
                                              

                                              快照注入数据库连接配置

                                              以上就是使用 MongoDB.EntityFrameworkCore 示例的 demo 实现,获取数据库连接字符串可以使用 快照方式注入,改进如下:

                                              • MongoDbSettings
                                                public class MongoDbSettings
                                                {
                                                    public string ConnectionString { get; set; } = "mongodb://localhost:27017";
                                                    public string DatabaseName { get; set; } = "cinema_app";
                                                }
                                                
                                                1. 注册配置类

                                                在 Program.cs 中将 MongoDbSettings 注册为服务,并绑定到配置。

                                                var builder = WebApplication.CreateBuilder(args);
                                                // 从 appsettings.json 或其他配置源绑定 MongoDbSettings
                                                builder.Services.Configure(builder.Configuration.GetSection("MongoDbSettings"));
                                                
                                                2. 注入 IOptionsSnapshot

                                                在需要使用的类中,通过构造函数注入 IOptionsSnapshot,并获取当前配置快照。

                                                public class SomeService
                                                {
                                                    private readonly MongoDbSettings _mongoDbSettings;
                                                    public SomeService(IOptionsSnapshot optionsSnapshot)
                                                    {
                                                        _mongoDbSettings = optionsSnapshot.Value;
                                                    }
                                                    public void PrintSettings()
                                                    {
                                                        Console.WriteLine($"ConnectionString: {_mongoDbSettings.ConnectionString}");
                                                        Console.WriteLine($"DatabaseName: {_mongoDbSettings.DatabaseName}");
                                                    }
                                                }
                                                
                                                3. 配置文件 appsettings.json 示例

                                                添加 MongoDbSettings 配置:

                                                {
                                                  "Logging": {
                                                    "LogLevel": {
                                                      "Default": "Information",
                                                      "Microsoft.AspNetCore": "Warning"
                                                    }
                                                  },
                                                  "AllowedHosts": "*",
                                                  "MongoDbSettings": {
                                                    "ConnectionString": "mongodb://localhost:27017",
                                                    "DatabaseName": "cinema_app"
                                                  }
                                                }
                                                

                                                关于 MongoDB 更多信息,请查看官方文档

                                                • https://www.mongodb.com/zh-cn/docs/

                                                  总结

                                                  本文详细讲解了如何在 .NET/C# 项目中使用 MongoDB.EntityFrameworkCore 这一官方提供程序,轻松地连接并操作 MongoDB 数据库。文章从基础讲起,介绍了 MongoDB 连接字符串的格式和用法,并通过具体的 C# 示例演示了如何连接到 MongoDB 副本集。随后,通过构建一个完整的电影票信息查询 Web API 应用,逐步展示了基于 EF Core 的数据库上下文(DbContext)、实体类设计、仓储模式(Repository)、服务逻辑以及控制器的具体实现方式。最后,还补充了如何通过配置快照的方式动态注入 MongoDB 的连接配置信息。整篇文章内容由浅入深,结构清晰,适合希望将 .NET 应用与 MongoDB 结合使用的开发者参考学习。

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码