You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Unit of Work+Repository+Dapper构建ORM:外键模型设计疑问

用Dapper结合Unit of Work和Repository模式获取关联Entity的解决方案

嘿,我来帮你搞定这个问题~ 首先得明确:Dapper是轻量型ORM,不像EF那样自动处理导航属性关联,所以得咱们手动写查询和映射逻辑。针对你方案1的实体设计,我给你一步步拆解实现方式:

核心思路

Dapper本身不支持自动映射导航属性,所以我们需要:

  1. 写关联查询SQL,把Instance和Entity的字段都查出来
  2. 用Dapper的多映射功能,把查询结果拆分并映射到Instance和Entity对象里
  3. 在Repository层封装这个逻辑,再通过Unit of Work统一管理连接和事务

具体实现步骤

1. 确认数据库表结构

首先你的数据库里Instance表必须有一个外键字段(比如EntityId),关联Entity表的Id字段——这是关联查询的基础,你的实体类里虽然没显式定义EntityId,但数据库里肯定得有哈。

2. 实现InstanceRepository的查询方法

在你的InstanceRepository里,写一个带关联查询的方法,用Dapper的多映射重载来处理对象关联:

public class InstanceRepository : IInstanceRepository
{
    private readonly IDbConnection _dbConnection;

    // 通过构造函数注入数据库连接(Unit of Work会负责提供这个连接)
    public InstanceRepository(IDbConnection dbConnection)
    {
        _dbConnection = dbConnection;
    }

    // 根据Id查询Instance并关联Entity
    public async Task<Instance> GetInstanceWithEntityAsync(int instanceId)
    {
        // 写关联查询SQL,用别名区分两个表的同名字段(比如Name)
        var querySql = @"
            SELECT 
                i.Id AS InstanceId,
                i.Name AS InstanceName,
                e.Id AS EntityId,
                e.Name AS EntityName
            FROM Instance i
            INNER JOIN Entity e ON i.EntityId = e.Id
            WHERE i.Id = @InstanceId";

        // 用字典来避免重复实例(如果是查询列表的话更有用)
        var instanceMap = new Dictionary<int, Instance>();

        // 调用Dapper的多映射QueryAsync方法
        var result = await _dbConnection.QueryAsync<Instance, Entity, Instance>(
            sql: querySql,
            // 映射逻辑:把Entity赋值给Instance的Entity属性
            map: (instance, entity) =>
            {
                if (!instanceMap.TryGetValue(instance.Id, out var existingInstance))
                {
                    existingInstance = instance;
                    instanceMap.Add(existingInstance.Id, existingInstance);
                }
                existingInstance.Entity = entity;
                return existingInstance;
            },
            // 指定从哪个字段开始拆分到第二个实体(Entity)
            splitOn: "EntityId",
            // 查询参数
            param: new { InstanceId = instanceId }
        );

        // 返回查询到的第一个实例(按Id查应该只有一个)
        return result.FirstOrDefault();
    }
}

3. 整合Unit of Work

Unit of Work的核心是统一管理数据库连接和事务,所以你需要在UoW里注入连接,并提供Repository的实例:

public class UnitOfWork : IUnitOfWork, IDisposable
{
    private readonly IDbConnection _dbConnection;
    private IDbTransaction _dbTransaction;
    private IInstanceRepository _instanceRepository;

    public UnitOfWork(IDbConnection dbConnection)
    {
        _dbConnection = dbConnection;
        _dbConnection.Open(); // 打开连接
    }

    // 延迟加载InstanceRepository实例
    public IInstanceRepository InstanceRepository
    {
        get
        {
            return _instanceRepository ??= new InstanceRepository(_dbConnection);
        }
    }

    // 开启事务
    public void BeginTransaction()
    {
        _dbTransaction = _dbConnection.BeginTransaction();
        // 如果需要给Repository传递事务,可以在这里修改Repository的构造或属性
    }

    // 提交事务
    public async Task CommitAsync()
    {
        try
        {
            await _dbTransaction.CommitAsync();
        }
        catch
        {
            await _dbTransaction.RollbackAsync();
            throw; // 重新抛出异常让上层处理
        }
        finally
        {
            _dbTransaction.Dispose();
            _dbTransaction = null;
        }
    }

    // 释放资源
    public void Dispose()
    {
        _dbTransaction?.Dispose();
        _dbConnection.Close();
        _dbConnection.Dispose();
    }
}

4. 使用示例

在业务层调用的时候,就可以通过Unit of Work来获取Repository并查询:

public async Task<Instance> GetInstanceWithEntity(int instanceId)
{
    using var unitOfWork = new UnitOfWork(new SqlConnection("你的连接字符串"));
    var instance = await unitOfWork.InstanceRepository.GetInstanceWithEntityAsync(instanceId);
    return instance;
}

额外注意点

  • 如果需要查询多个Instance并关联Entity,上面的代码同样适用,Dapper会帮你处理所有结果,字典会自动去重同一个Instance的重复记录
  • 如果你需要懒加载(即用到Entity的时候再查询),Dapper本身不支持,你得自己实现(比如用动态代理类,但一般不推荐,预先加载关联数据更高效)
  • 确保你的实体类属性名称和SQL里的别名对应(比如Instance的Id对应SQL里的InstanceId,Name对应InstanceName),或者你可以用Dapper的Column特性来映射

内容的提问来源于stack exchange,提问作者Red Wei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:31:29