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

如何在Dapper中优雅处理派生类型的查询操作?

如何在Dapper中优雅处理派生类型的查询操作?

我完全理解你的困扰——既然删除操作能通过反射动态调用对应存储过程,查询操作却要写一堆switch分支,确实不够优雅。核心问题在于Dapper的QuerySingle<T>是泛型方法,需要在编译时明确类型,但你现在只有运行时的Type对象。不过我们可以通过反射来绕开这个限制,把重复的switch逻辑替换成动态调用的代码。

解决方案一:直接反射调用Dapper的泛型查询方法

我们可以通过反射获取Dapper的QuerySingle泛型方法,再动态创建对应实体类型的方法实例并调用,这样就能彻底去掉switch分支:

public static ModelBase ModelGet(Type t, int id, SqlConnection? conn = null, SqlTransaction? trans = null, bool withDependents = false)
{
    bool connectionIsNew = (conn is null);
    bool transactionIsNew = (trans is null);
    ModelBase model = default!;

    try
    {
        if (connectionIsNew) 
           conn = new SqlConnection(Globals.connString);

        if (conn is null)
        {
             throw new Exception("Unable to establish a database connection in Data.ModelGet");
        }
        else
        {
            if (conn.State != System.Data.ConnectionState.Open) 
                conn.Open();
        }

        if (transactionIsNew) 
            trans = conn.BeginTransaction();

        var p = new { Id = id };
        string spName = $"sp{t.Name}_Get";

        // 反射获取Dapper的QuerySingle泛型方法
        var querySingleMethod = typeof(SqlConnectionExtensions)
            .GetMethod("QuerySingle", new[] { 
                typeof(SqlConnection), typeof(string), typeof(object), 
                typeof(SqlTransaction), typeof(int?), typeof(CommandType?) 
            })
            ?.MakeGenericMethod(t);
        
        if (querySingleMethod == null)
        {
            throw new InvalidOperationException("无法找到Dapper的QuerySingle泛型方法");
        }

        // 调用动态生成的泛型方法
        object? result = querySingleMethod.Invoke(
            null, 
            new object?[] { conn, spName, p, trans, null, CommandType.StoredProcedure }
        );
        
        model = result as ModelBase ?? throw new InvalidCastException(
            $"存储过程返回的对象无法转换为ModelBase类型"
        );
    }
    catch (Exception exc)
    {
        // 不要留空catch块,至少记录日志或重新抛出异常
        throw new Exception($"查询类型 {t.Name} 时出错", exc);
    }
    finally
    {
        // 补充:处理事务提交/回滚和连接关闭,避免资源泄漏
        if (transactionIsNew && trans != null)
        {
            try
            {
                trans.Commit();
            }
            catch
            {
                trans.Rollback();
                throw;
            }
        }
        if (connectionIsNew && conn != null && conn.State == System.Data.ConnectionState.Open)
        {
            conn.Close();
        }
    }

    return model;
}

解决方案二:封装泛型辅助方法后反射调用

如果觉得直接调用Dapper的方法太繁琐,可以先封装一个泛型内部方法,再通过反射调用它,代码可读性会更好:

// 内部泛型辅助方法
private static T ModelGetInternal<T>(int id, SqlConnection conn, SqlTransaction trans) 
    where T : ModelBase
{
    var p = new { Id = id };
    string spName = $"sp{typeof(T).Name}_Get";
    return conn.QuerySingle<T>(spName, p, trans, commandType: CommandType.StoredProcedure);
}

// 改造后的公共方法
public static ModelBase ModelGet(Type t, int id, SqlConnection? conn = null, SqlTransaction? trans = null, bool withDependents = false)
{
    // (连接、事务的初始化逻辑和方案一一致,此处省略)

    // 替换switch分支:反射调用内部泛型方法
    var getMethod = typeof(YourDataClass) // 替换成你的实际类名
        .GetMethod("ModelGetInternal", BindingFlags.NonPublic | BindingFlags.Static)
        ?.MakeGenericMethod(t);

    if (getMethod == null)
    {
        throw new InvalidOperationException("无法找到内部的ModelGetInternal泛型方法");
    }

    object? result = getMethod.Invoke(null, new object[] { id, conn!, trans! });
    model = result as ModelBase ?? throw new InvalidCastException(
        $"存储过程返回的对象无法转换为ModelBase类型"
    );

    // (事务、连接的清理逻辑和方案一一致,此处省略)

    return model;
}

关键注意事项

  • 缓存提升性能:如果这个查询方法会被频繁调用,建议用Dictionary<Type, MethodInfo>缓存生成好的泛型方法实例,避免每次都反射查找方法,减少性能开销。
  • 异常处理优化:你原来的代码里存在空catch块、throw后写return(死代码)等问题,这些都会隐藏错误或导致逻辑异常,一定要修正。
  • 资源泄漏防范:务必在finally块中处理事务的提交/回滚和数据库连接的关闭,避免长期占用数据库资源。
  • 类型映射匹配:确保每个实体类对应的存储过程返回字段,和类的属性名称、类型完全匹配,否则Dapper会无法正确映射对象。
  • 空值场景处理:如果存储过程可能返回null(比如找不到对应Id的记录),建议用QuerySingleOrDefault代替QuerySingle,并提前处理null返回的情况。

备注:内容来源于stack exchange,提问作者mdicoope

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 09:43:07