csharp
csharp
csharp
csharp
csharp
readme
csharp
csharp
csharp
csharp

Dpz.Core.MongodbAccess

一个基于 MongoDB.Driver 数据访问层封装库,实现了完整的仓储模式(Repository Pattern)和工作单元模式(Unit of Work Pattern)。

📋 主要特性

  • 仓储模式(Repository Pattern):提供统一的数据访问接口,简化 CRUD 操作
  • 工作单元模式(Unit of Work Pattern):完整支持事务管理,确保数据一致性
  • 全异步操作:所有 CRUD 操作均支持异步,提升性能
  • 表达式树查询:支持 LINQ 表达式查询,类型安全
  • 依赖注入友好:原生支持 .NET 依赖注入容器
  • 性能优化:使用表达式树缓存和并发字典提升性能
  • 灵活的集合命名:支持自定义集合名称

🔧 技术栈

  • .NET 10.0
  • MongoDB.Driver 3.5.2
  • MongoDB.Analyzer 2.0.0

📦 快速开始

配置连接字符串

appsettings.json 中配置 MongoDB 连接字符串:

{
  "ConnectionStrings": {
    "mongodb": "mongodb://localhost:27017/YourDatabase"
  }
}

依赖注入配置

// 注册仓储服务(推荐使用 Transient 生命周期)
services.AddTransient(typeof(IRepository<>), typeof(Repository<>));

// 注册工作单元服务(必须使用 Transient 生命周期)
services.AddTransient<IUnitOfWork, UnitOfWork>();

💡 使用示例

1. 基础 Repository 使用

定义实体

public class UserEntity : BaseEntity
{
    public string Name { get; set; } = "";
    public string Email { get; set; } = "";
    public int Age { get; set; }
}

基本 CRUD 操作

// 通过依赖注入获取 Repository
public class UserService
{
    private readonly IRepository<UserEntity> _userRepository;
    
    public UserService(IRepository<UserEntity> userRepository)
    {
        _userRepository = userRepository;
    }
    
    // 插入数据
    public async Task CreateUserAsync(UserEntity user)
    {
        await _userRepository.InsertAsync(user);
    }
    
    // 查询数据
    public async Task<List<UserEntity>> GetUsersAsync()
    {
        return await _userRepository.SearchFor(x => x.Age > 18).ToListAsync();
    }
    
    // 更新数据
    public async Task UpdateUserAsync(UserEntity user)
    {
        await _userRepository.UpdateAsync(user);
    }
    
    // 删除数据
    public async Task DeleteUserAsync(object userId)
    {
        await _userRepository.DeleteAsync(userId);
    }
}

2. 工作单元(Unit of Work)使用

工作单元模式用于管理多个仓储的事务,确保多个操作的原子性。

基本事务操作

public class OrderService
{
    private readonly IUnitOfWork _unitOfWork;
    
    public OrderService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }
    
    public async Task CreateOrderAsync()
    {
        // 获取多个仓储
        var orderRepo = _unitOfWork.GetRepository<OrderEntity>();
        var productRepo = _unitOfWork.GetRepository<ProductEntity>();
        
        // 开始事务
        _unitOfWork.Begin();
        
        try
        {
            // 创建订单
            var order = new OrderEntity { /* ... */ };
            await orderRepo.InsertAsync(order);
            
            // 更新库存
            var updateDef = Builders<ProductEntity>.Update.Inc(x => x.Stock, -1);
            await productRepo.UpdateAsync(x => x.Id == productId, updateDef);
            
            // 提交事务
            await _unitOfWork.CommitAsync();
        }
        catch (Exception)
        {
            // 回滚事务
            await _unitOfWork.RollbackAsync();
            throw;
        }
    }
}

完整的事务示例

public class UnitOfWorkTestEntity : BaseEntity
{
    public string Value { get; set; } = "";
}

public class UnitOfWorkTestEntity2 : BaseEntity
{
    public string Value { get; set; } = "";
}

// 使用工作单元进行事务操作
using var uow = serviceProvider.GetService<IUnitOfWork>();

// 获取仓储(同一类型的 Repository 在同一工作单元中是单例)
var repository1 = uow.GetRepository<UnitOfWorkTestEntity>();
var repository2 = uow.GetRepository<UnitOfWorkTestEntity2>();

// 开始事务
uow.Begin();

// 清空数据
await repository1.DeleteAsync(x => true);
await repository2.DeleteAsync(x => true);

// 插入新数据
var entity1 = new UnitOfWorkTestEntity { Value = Guid.NewGuid().ToString("N") };
await repository1.InsertAsync(entity1);

var entity2 = new UnitOfWorkTestEntity2 { Value = Guid.NewGuid().ToString("N") };
await repository2.InsertAsync(entity2);

// 提交事务
await uow.CommitAsync();

// ===== 测试事务回滚 =====
using var uow2 = serviceProvider.GetService<IUnitOfWork>();
var repository3 = uow2.GetRepository<UnitOfWorkTestEntity>();
var repository4 = uow2.GetRepository<UnitOfWorkTestEntity2>();

var guid1 = Guid.NewGuid().ToString("N");
var guid2 = Guid.NewGuid().ToString("N");

try
{
    // 验证之前的数据已提交
    var list1 = await repository3.SearchFor(x => true).ToListAsync();
    Assert.IsNotNull(list1);
    Assert.IsNotEmpty(list1);
    
    // 开始新事务
    uow2.Begin();
    
    var entity3 = new UnitOfWorkTestEntity { Value = guid1 };
    var entity4 = new UnitOfWorkTestEntity2 { Value = guid2 };
    
    await repository3.InsertAsync(entity3);
    await repository4.InsertAsync(entity4);
    
    // 注意:MongoDB 在事务内可以查询到未提交的数据
    var r1 = await repository3.SearchFor(x => x.Value == guid1).FirstOrDefaultAsync();
    Assert.IsNotNull(r1);
    
    // 模拟异常
    throw new Exception("测试回滚");
}
catch (Exception)
{
    // 回滚事务
    await uow2.RollbackAsync();
}

// 验证回滚后数据不存在
var repo1 = new Repository<UnitOfWorkTestEntity>(connectionString);
var result1 = await repo1.SearchFor(x => x.Value == guid1).FirstOrDefaultAsync();
Assert.IsNull(result1); // 数据已回滚,应为 null

3. 高级查询

// LINQ 表达式查询
var users = await _userRepository
    .SearchFor(x => x.Age > 18 && x.Name.StartsWith("Zhang"))
    .ToListAsync();

// MongoDB FilterDefinition 查询
var filter = Builders<UserEntity>.Filter.And(
    Builders<UserEntity>.Filter.Gt(x => x.Age, 18),
    Builders<UserEntity>.Filter.Regex(x => x.Name, "^Zhang")
);
var result = await _userRepository.SearchFor(filter).ToListAsync();

// 异步流式查询(适合大数据量)
await foreach (var user in _userRepository.SearchForAsync(filter))
{
    Console.WriteLine(user.Name);
}

4. 批量操作

// 批量插入
var users = new List<UserEntity>
{
    new UserEntity { Name = "User1" },
    new UserEntity { Name = "User2" },
    new UserEntity { Name = "User3" }
};
await _userRepository.InsertAsync(users);

// 批量更新
var usersToUpdate = await _userRepository.SearchFor(x => x.Age > 18).ToListAsync();
foreach (var user in usersToUpdate)
{
    user.Age += 1;
}
await _userRepository.UpdateAsync(usersToUpdate);

📚 核心接口说明

IRepository

提供完整的 CRUD 操作接口:

  • Task<T?> FindAsync(object id) - 根据 ID 查找实体
  • Task InsertAsync(T entity) - 插入单个实体
  • Task InsertAsync(IReadOnlyCollection<T> source) - 批量插入
  • Task<DeleteResult> DeleteAsync(Expression<Func<T, bool>> filter) - 删除数据
  • Task<UpdateResult> UpdateAsync(Expression<Func<T, bool>> predicate, UpdateDefinition<T> update) - 更新数据
  • Task<ReplaceOneResult> UpdateAsync(T entity) - 替换实体
  • IQueryable<T> SearchFor(Expression<Func<T, bool>> predicate) - LINQ 查询
  • IAsyncEnumerable<T> SearchForAsync(FilterDefinition<T> filter) - 异步流式查询

IUnitOfWork

提供工作单元和事务管理:

  • void Begin() - 开始工作单元(启动事务)
  • IRepository<T> GetRepository<T>() - 获取指定类型的仓储
  • IRepository<T> GetRepository<T>(string collectionName) - 获取指定集合名称的仓储
  • Task CommitAsync() - 提交事务
  • Task RollbackAsync() - 回滚事务

⚠️ 注意事项

  1. 事务支持:MongoDB 单实例默认不支持事务,需要配置为副本集。参考:单实例事务解决方案

  2. 工作单元生命周期IUnitOfWork 必须以 Transient 方式注入,确保每次使用都是新实例。

  3. 实体要求:实体类必须继承 IBaseEntity 接口,并包含 Id 属性或使用 [BsonId] 特性标记主键。

  4. 事务内查询:MongoDB 在事务内可以查询到未提交的数据,这是正常行为。

  5. 性能优化:库内使用了表达式树缓存和并发字典优化性能,无需担心反射性能问题。

📖 更新日志

  • 2019年12月9日UnitOfWork 部分实现
  • 2019年12月20日UnitOfWork 大部分实现
  • 当前版本UnitOfWork 已完全实现,支持完整的事务管理

📄 许可证

本项目基于 MIT 许可证开源。

🔗 相关链接

评论加载中...