C# 使用 MongoDB 完整指南
阅读量:12次 发布时间::2026/1/5
摘要说明
C# + MongoDB 是一个强大的现代开发组合,特别适合需要快速迭代和处理半结构化数据的 .NET 项目。
C# 使用 MongoDB 的优势:
开发效率高:对象-文档映射自然
异步支持完善:全异步API
LINQ支持:使用熟悉的语法
与.NET生态完美集成:ASP.NET Core、依赖注入等
性能优秀:官方驱动经过优化
一、绝佳组合
-
官方提供 .NET/C# 驱动程序:MongoDB.Driver 是官方维护的一流库
-
.NET 对象与 BSON 文档自然映射:C# 类直接序列化为 MongoDB 文档
-
异步支持完善:全异步 API,适合现代 .NET 开发
-
与 ASP.NET Core 集成良好:Web API 开发的理想数据存储方案
-
LINQ 支持:可以使用熟悉的 LINQ 语法查询 MongoDB
二、环境准备与安装
1. 安装 NuGet 包
<PackageReference Include="MongoDB.Driver" Version="2.23.0" />
或使用 NuGet 包管理器控制台:
Install-Package MongoDB.Driver Install-Package MongoDB.Driver.GridFS Install-Package MongoDB.Driver.Core
2. 创建实体类
using MongoDB.Bson; using MongoDB.Bson.Serialization.Attributes; namespace MongoDemo { public class User { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; } [BsonElement("name")] public string Name { get; set; } [BsonElement("email")] public string Email { get; set; } [BsonElement("age")] public int Age { get; set; } [BsonElement("address")] public Address Address { get; set; } [BsonElement("tags")] public List<string> Tags { get; set; } = new(); [BsonElement("created_at")] [BsonDateTimeOptions(Kind = DateTimeKind.Local)] public DateTime CreatedAt { get; set; } } public class Address { [BsonElement("city")] public string City { get; set; } [BsonElement("street")] public string Street { get; set; } [BsonElement("postal_code")] public string PostalCode { get; set; } } public class Product { [BsonId] [BsonRepresentation(BsonType.ObjectId)] public string Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } public int Stock { get; set; } public List<string> Categories { get; set; } public Dictionary<string, object> Attributes { get; set; } } }
三、基础操作示例
1. 连接 MongoDB
using MongoDB.Driver; using System; namespace MongoDemo { public class MongoService { private readonly IMongoDatabase _database; public MongoService() { var client = new MongoClient("mongodb://localhost:27017"); _database = client.GetDatabase("testdb"); } public MongoService(string connectionString) { var client = new MongoClient(connectionString); _database = client.GetDatabase("testdb"); } public MongoService() { var settings = new MongoClientSettings { Servers = new[] { new MongoServerAddress("localhost", 27017) }, Credential = MongoCredential.CreateCredential( "admin", "username", "password" ), MaxConnectionPoolSize = 100, MinConnectionPoolSize = 10, ReadPreference = ReadPreference.SecondaryPreferred, WriteConcern = WriteConcern.WMajority }; var client = new MongoClient(settings); _database = client.GetDatabase("testdb"); } public IMongoCollection<User> Users => _database.GetCollection<User>("users"); public IMongoCollection<Product> Products => _database.GetCollection<Product>("products"); } }
2. CRUD 操作(异步版)
public class UserRepository { private readonly IMongoCollection<User> _users; public UserRepository(IMongoDatabase database) { _users = database.GetCollection<User>("users"); } public async Task<User> CreateUserAsync(User user) { user.Id = ObjectId.GenerateNewId().ToString(); user.CreatedAt = DateTime.Now; await _users.InsertOneAsync(user); return user; } public async Task CreateMultipleUsersAsync(List<User> users) { foreach (var user in users) { user.Id = ObjectId.GenerateNewId().ToString(); user.CreatedAt = DateTime.Now; } await _users.InsertManyAsync(users); } public async Task<User> GetUserByIdAsync(string id) { var filter = Builders<User>.Filter.Eq(u => u.Id, id); return await _users.Find(filter).FirstOrDefaultAsync(); } public async Task<List<User>> GetUsersByAgeAsync(int minAge, int maxAge) { var filter = Builders<User>.Filter.And( Builders<User>.Filter.Gte(u => u.Age, minAge), Builders<User>.Filter.Lte(u => u.Age, maxAge) ); return await _users.Find(filter) .SortBy(u => u.Name) .Limit(100) .ToListAsync(); } public async Task<User> FindUserByEmailAsync(string email) { var filter = Builders<User>.Filter.Eq(u => u.Email, email); return await _users.Find(filter).FirstOrDefaultAsync(); } public async Task<bool> UpdateUserEmailAsync(string userId, string newEmail) { var filter = Builders<User>.Filter.Eq(u => u.Id, userId); var update = Builders<User>.Update .Set(u => u.Email, newEmail) .Set(u => u.UpdatedAt, DateTime.Now); var result = await _users.UpdateOneAsync(filter, update); return result.ModifiedCount > 0; } public async Task<long> IncrementUserAgeAsync(string userId) { var filter = Builders<User>.Filter.Eq(u => u.Id, userId); var update = Builders<User>.Update.Inc(u => u.Age, 1); var result = await _users.UpdateOneAsync(filter, update); return result.ModifiedCount; } public async Task<bool> AddTagToUserAsync(string userId, string tag) { var filter = Builders<User>.Filter.Eq(u => u.Id, userId); var update = Builders<User>.Update.AddToSet(u => u.Tags, tag); var result = await _users.UpdateOneAsync(filter, update); return result.ModifiedCount > 0; } public async Task<bool> DeleteUserAsync(string userId) { var filter = Builders<User>.Filter.Eq(u => u.Id, userId); var result = await _users.DeleteOneAsync(filter); return result.DeletedCount > 0; } public async Task<long> DeleteUsersByAgeAsync(int age) { var filter = Builders<User>.Filter.Eq(u => u.Age, age); var result = await _users.DeleteManyAsync(filter); return result.DeletedCount; } }
3. 使用 LINQ 查询(推荐)
public class LinqRepository { private readonly IMongoCollection<User> _users; public LinqRepository(IMongoDatabase database) { _users = database.GetCollection<User>("users"); } public async Task<List<User>> GetUsersByLinqAsync() { var query = from user in _users.AsQueryable() where user.Age > 18 && user.Email.Contains("@gmail.com") orderby user.CreatedAt descending select user; return await query.ToListAsync(); } public async Task<List<User>> GetUsersInCityAsync(string city) { return await _users.AsQueryable() .Where(u => u.Address.City == city) .Where(u => u.Tags.Contains("vip")) .OrderBy(u => u.Name) .Take(50) .ToListAsync(); } public async Task<User> FindUserByEmailLinqAsync(string email) { return await _users.AsQueryable() .FirstOrDefaultAsync(u => u.Email == email); } public async Task<List<object>> GetUserNamesAndEmailsAsync() { return await _users.AsQueryable() .Where(u => u.Age > 20) .Select(u => new { u.Name, u.Email, u.Age }) .ToListAsync(); } }
4. 复杂查询与聚合
public class AdvancedOperations { private readonly IMongoCollection<User> _users; public async Task<List<User>> ComplexQueryAsync() { var filter = Builders<User>.Filter.And( Builders<User>.Filter.Gte(u => u.Age, 18), Builders<User>.Filter.Lte(u => u.Age, 65), Builders<User>.Filter.ElemMatch(u => u.Tags, Builders<string>.Filter.In("tag", new[] { "developer", "designer" })), Builders<User>.Filter.Or( Builders<User>.Filter.Eq(u => u.Address.City, "北京"), Builders<User>.Filter.Eq(u => u.Address.City, "上海") ) ); var sort = Builders<User>.Sort .Ascending(u => u.Age) .Descending(u => u.CreatedAt); return await _users.Find(filter) .Sort(sort) .Skip(0) .Limit(20) .ToListAsync(); } public async Task<List<BsonDocument>> AggregateUsersAsync() { var pipeline = new[] { new BsonDocument("$match", new BsonDocument("age", new BsonDocument("$gte", 18))), new BsonDocument("$group", new BsonDocument { { "_id", "$address.city" }, { "count", new BsonDocument("$sum", 1) }, { "averageAge", new BsonDocument("$avg", "$age") }, { "maxAge", new BsonDocument("$max", "$age") } }), new BsonDocument("$sort", new BsonDocument("count", -1)), new BsonDocument("$limit", 10) }; return await _users.Aggregate<BsonDocument>(pipeline).ToListAsync(); } public class CityStats { public string City { get; set; } public int Count { get; set; } public double AverageAge { get; set; } } public async Task<List<CityStats>> AggregateUsersTypedAsync() { var pipeline = _users.Aggregate() .Match(u => u.Age >= 18) .Group(u => u.Address.City, g => new CityStats { City = g.Key, Count = g.Count(), AverageAge = g.Average(u => u.Age) }) .SortByDescending(s => s.Count) .Limit(10); return await pipeline.ToListAsync(); } }
1. 依赖注入配置
using MongoDB.Driver; var builder = WebApplication.CreateBuilder(args); builder.Services.Configure<MongoDBSettings>( builder.Configuration.GetSection("MongoDB")); builder.Services.AddSingleton<IMongoClient>(serviceProvider => { var settings = serviceProvider.GetRequiredService<IOptions<MongoDBSettings>>().Value; return new MongoClient(settings.ConnectionString); }); builder.Services.AddScoped<IMongoDatabase>(serviceProvider => { var client = serviceProvider.GetRequiredService<IMongoClient>(); var settings = serviceProvider.GetRequiredService<IOptions<MongoDBSettings>>().Value; return client.GetDatabase(settings.DatabaseName); }); builder.Services.AddScoped<IUserRepository, UserRepository>(); builder.Services.AddScoped<IProductRepository, ProductRepository>(); var app = builder.Build();
2. 配置文件 appsettings.json
{ "MongoDB": { "ConnectionString": "mongodb://localhost:27017", "DatabaseName": "myappdb", "UsersCollection": "users", "ProductsCollection": "products" }, "Logging": { "LogLevel": { "Default": "Information" } } }
3. 配置文件类
public class MongoDBSettings { public string ConnectionString { get; set; } public string DatabaseName { get; set; } public string UsersCollection { get; set; } public string ProductsCollection { get; set; } }
4. 仓储模式实现
public interface IUserRepository { Task<User> GetByIdAsync(string id); Task<IEnumerable<User>> GetAllAsync(); Task<User> CreateAsync(User user); Task UpdateAsync(User user); Task DeleteAsync(string id); } public class UserRepository : IUserRepository { private readonly IMongoCollection<User> _users; public UserRepository(IMongoDatabase database, IOptions<MongoDBSettings> settings) { _users = database.GetCollection<User>( settings.Value.UsersCollection); CreateIndexes(); } private void CreateIndexes() { var emailIndex = Builders<User>.IndexKeys.Ascending(u => u.Email); var emailIndexOptions = new CreateIndexOptions { Unique = true }; _users.Indexes.CreateOne( new CreateIndexModel<User>(emailIndex, emailIndexOptions)); var compoundIndex = Builders<User>.IndexKeys .Ascending(u => u.Address.City) .Ascending(u => u.Age); _users.Indexes.CreateOne( new CreateIndexModel<User>(compoundIndex)); } public async Task<User> GetByIdAsync(string id) { return await _users.Find(u => u.Id == id).FirstOrDefaultAsync(); } public async Task<IEnumerable<User>> GetAllAsync() { return await _users.Find(_ => true).ToListAsync(); } public async Task<User> CreateAsync(User user) { await _users.InsertOneAsync(user); return user; } public async Task UpdateAsync(User user) { await _users.ReplaceOneAsync(u => u.Id == user.Id, user); } public async Task DeleteAsync(string id) { await _users.DeleteOneAsync(u => u.Id == id); } }
5. Web API 控制器示例
[ApiController] [Route("api/[controller]")] public class UsersController : ControllerBase { private readonly IUserRepository _userRepository; public UsersController(IUserRepository userRepository) { _userRepository = userRepository; } [HttpGet] public async Task<IActionResult> GetAll() { var users = await _userRepository.GetAllAsync(); return Ok(users); } [HttpGet("{id}")] public async Task<IActionResult> GetById(string id) { var user = await _userRepository.GetByIdAsync(id); if (user == null) return NotFound(); return Ok(user); } [HttpPost] public async Task<IActionResult> Create([FromBody] CreateUserDto dto) { var user = new User { Name = dto.Name, Email = dto.Email, Age = dto.Age, Address = dto.Address, CreatedAt = DateTime.Now }; var createdUser = await _userRepository.CreateAsync(user); return CreatedAtAction(nameof(GetById), new { id = createdUser.Id }, createdUser); } [HttpPut("{id}")] public async Task<IActionResult> Update(string id, [FromBody] UpdateUserDto dto) { var existingUser = await _userRepository.GetByIdAsync(id); if (existingUser == null) return NotFound(); existingUser.Name = dto.Name; existingUser.Email = dto.Email; existingUser.Age = dto.Age; await _userRepository.UpdateAsync(existingUser); return NoContent(); } [HttpDelete("{id}")] public async Task<IActionResult> Delete(string id) { await _userRepository.DeleteAsync(id); return NoContent(); } [HttpGet("search")] public async Task<IActionResult> Search( [FromQuery] string city, [FromQuery] int? minAge, [FromQuery] int? maxAge, [FromQuery] string tag) { var builder = Builders<User>.Filter; var filter = builder.Empty; if (!string.IsNullOrEmpty(city)) filter &= builder.Eq(u => u.Address.City, city); if (minAge.HasValue) filter &= builder.Gte(u => u.Age, minAge.Value); if (maxAge.HasValue) filter &= builder.Lte(u => u.Age, maxAge.Value); if (!string.IsNullOrEmpty(tag)) filter &= builder.AnyEq(u => u.Tags, tag); return Ok(); } } public class CreateUserDto { public string Name { get; set; } public string Email { get; set; } public int Age { get; set; } public Address Address { get; set; } } public class UpdateUserDto { public string Name { get; set; } public string Email { get; set; } public int Age { get; set; } }
五、高级功能与最佳实践
1. 事务支持(MongoDB 4.0+)
public async Task<bool> TransferPointsAsync( string fromUserId, string toUserId, int points) { var client = _database.Client; using (var session = await client.StartSessionAsync()) { session.StartTransaction(); try { var fromFilter = Builders<User>.Filter.Eq(u => u.Id, fromUserId); var fromUpdate = Builders<User>.Update.Inc(u => u.Points, -points); await _users.UpdateOneAsync(session, fromFilter, fromUpdate); var toFilter = Builders<User>.Filter.Eq(u => u.Id, toUserId); var toUpdate = Builders<User>.Update.Inc(u => u.Points, points); await _users.UpdateOneAsync(session, toFilter, toUpdate); await session.CommitTransactionAsync(); return true; } catch (Exception ex) { await session.AbortTransactionAsync(); Console.WriteLine($"Transaction failed: {ex.Message}"); return false; } } }
2. 批量操作优化
public async Task BulkInsertUsersAsync(List<User> users) { var writes = new List<WriteModel<User>>(); foreach (var user in users) { var filter = Builders<User>.Filter.Eq(u => u.Email, user.Email); var update = Builders<User>.Update .SetOnInsert(u => u.Id, ObjectId.GenerateNewId().ToString()) .Set(u => u.Name, user.Name) .Set(u => u.Age, user.Age) .Set(u => u.CreatedAt, DateTime.Now); writes.Add(new UpdateOneModel<User>(filter, update) { IsUpsert = true }); } var options = new BulkWriteOptions { IsOrdered = false }; await _users.BulkWriteAsync(writes, options); }
3. 监控与日志
public class MongoServiceWithLogging { private readonly IMongoCollection<User> _users; private readonly ILogger<MongoServiceWithLogging> _logger; public MongoServiceWithLogging( IMongoDatabase database, ILogger<MongoServiceWithLogging> logger) { _users = database.GetCollection<User>("users"); _logger = logger; var client = database.Client; client.Settings.ClusterConfigurator = cb => { cb.Subscribe<CommandStartedEvent>(e => { _logger.LogDebug( "MongoDB Command: {CommandName} - {CommandJson}", e.CommandName, e.Command.ToJson()); }); cb.Subscribe<CommandSucceededEvent>(e => { _logger.LogDebug( "MongoDB Command succeeded in {Duration}ms", e.Duration.TotalMilliseconds); }); }; } }
4. 索引管理
public class IndexManager { private readonly IMongoCollection<User> _users; public async Task EnsureIndexesAsync() { var emailIndex = Builders<User>.IndexKeys.Ascending(u => u.Email); await _users.Indexes.CreateOneAsync( new CreateIndexModel<User>(emailIndex, new CreateIndexOptions { Unique = true })); var compoundIndex = Builders<User>.IndexKeys .Ascending(u => u.Address.City) .Ascending(u => u.Age) .Descending(u => u.CreatedAt); await _users.Indexes.CreateOneAsync( new CreateIndexModel<User>(compoundIndex)); var ttlIndex = Builders<User>.IndexKeys.Ascending(u => u.CreatedAt); await _users.Indexes.CreateOneAsync( new CreateIndexModel<User>(ttlIndex, new CreateIndexOptions { ExpireAfter = TimeSpan.FromDays(30) })); var textIndex = Builders<User>.IndexKeys.Text(u => u.Name); await _users.Indexes.CreateOneAsync( new CreateIndexModel<User>(textIndex)); } public async Task<List<string>> GetIndexesAsync() { using (var cursor = await _users.Indexes.ListAsync()) { var indexes = await cursor.ToListAsync(); return indexes.Select(i => i["name"].AsString).ToList(); } } }
六、性能优化建议
1. 连接池配置
var settings = new MongoClientSettings { Servers = new[] { new MongoServerAddress("localhost", 27017) }, MaxConnectionPoolSize = 100, MinConnectionPoolSize = 10, MaxConnectionIdleTime = TimeSpan.FromMinutes(5), MaxConnectionLifeTime = TimeSpan.FromMinutes(30), WaitQueueTimeout = TimeSpan.FromSeconds(30), ConnectTimeout = TimeSpan.FromSeconds(10), SocketTimeout = TimeSpan.FromSeconds(30) };
2. 查询优化
var users = await _users.Find(_ => true).ToListAsync(); var projection = Builders<User>.Projection .Include(u => u.Name) .Include(u => u.Email) .Exclude(u => u.Id); var users = await _users.Find(_ => true) .Project<User>(projection) .ToListAsync(); using (var cursor = await _users.Find(_ => true) .BatchSize(1000) .ToCursorAsync()) { while (await cursor.MoveNextAsync()) { foreach (var user in cursor.Current) { } } }
七、学习资源
官方资源
NuGet 包
MongoDB.Driver # 主驱动包
MongoDB.Driver.GridFS # GridFS文件存储
MongoDB.Driver.Core # 核心驱动
MongoDB.Bson # BSON序列化
示例项目结构
MongoDemo/
├── Models/ # 实体类
│ ├── User.cs
│ ├── Product.cs
│ └── Address.cs
├── Repositories/ # 数据访问层
│ ├── IUserRepository.cs
│ └── UserRepository.cs
├── Services/ # 业务逻辑层
│ └── UserService.cs
├── Controllers/ # Web API
│ └── UsersController.cs
├── DTOs/ # 数据传输对象
│ ├── CreateUserDto.cs
│ └── UpdateUserDto.cs
└── Program.cs # 配置和启动
八、常见问题解决
1. 连接字符串格式
"mongodb://localhost:27017" "mongodb://username:password@localhost:27017/dbname" "mongodb://host1:27017,host2:27017,host3:27017/dbname?replicaSet=rs0" "mongodb+srv://username:password@cluster0.mongodb.net/dbname"
2. 序列化配置
BsonClassMap.RegisterClassMap<User>(cm => { cm.AutoMap(); cm.MapMember(u => u.CreatedAt) .SetSerializer(new DateTimeSerializer(DateTimeKind.Local)); cm.UnmapMember(u => u.InternalField); }); public class CustomDateTimeSerializer : DateTimeSerializer { public override DateTime Deserialize(...) { } }
3. 错误处理
try { var user = await _users.Find(u => u.Email == email).FirstAsync(); return user; } catch (MongoException ex) when (ex is MongoCommandException || ex is MongoWriteException) { _logger.LogError(ex, "MongoDB操作失败"); throw new ApplicationException("数据库操作失败", ex); } catch (TimeoutException) { throw new ApplicationException("数据库连接超时"); }