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

在Dapper中使用模板对象创建存储过程的动态参数

刚好之前做类似需求的时候研究过,用反射来批量处理类属性转DynamicParameters是最高效的方式,不用手动一个个写Add参数,省心又不容易出错。下面给你一步步讲怎么实现:

核心思路

利用C#反射遍历输入/输出类的公共属性,自动将属性名和对应值映射到DynamicParameters中——输入类转成存储过程的输入参数,输出类则预先在DynamicParameters中声明输出参数,执行完存储过程后再把参数值回填到输出对象里。

扩展方法实现(复用性拉满)

写一个静态扩展类,封装转换逻辑,这样任何输入/输出类都能直接用:

using Dapper;
using System.Data;
using System.Reflection;

public static class DynamicParametersExtensions
{
    // 将输入对象转换为存储过程输入参数
    public static DynamicParameters ToInputParameters<T>(this T inputObj) where T : class
    {
        var parameters = new DynamicParameters();
        if (inputObj == null) return parameters;

        // 遍历输入类的所有公共实例属性
        foreach (var prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            // 跳过标记了忽略特性的属性(可选,后面会讲)
            if (prop.GetCustomAttribute<IgnoreParameterAttribute>() != null)
                continue;

            var propValue = prop.GetValue(inputObj);
            // 存储过程参数通常带@前缀,根据你的实际命名规则调整
            parameters.Add($"@{prop.Name}", propValue);
        }

        return parameters;
    }

    // 从执行后的DynamicParameters中提取值到输出对象
    public static T ToOutputObject<T>(this DynamicParameters parameters) where T : class, new()
    {
        var outputObj = new T();

        foreach (var prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            var paramName = $"@{prop.Name}";
            if (parameters.ParameterNames.Contains(paramName))
            {
                var paramValue = parameters.Get<object>(paramName);
                // 处理可空类型的赋值
                if (paramValue != DBNull.Value)
                    prop.SetValue(outputObj, paramValue);
            }
        }

        return outputObj;
    }

    // 辅助方法:根据属性类型自动匹配DbType(用于输出参数声明)
    private static DbType GetDbType(Type propertyType)
    {
        // 基础类型映射,可根据需求扩展
        return propertyType switch
        {
            Type t when t == typeof(string) || t == typeof(char) => DbType.String,
            Type t when t == typeof(int) || t == typeof(int?) => DbType.Int32,
            Type t when t == typeof(DateTime) || t == typeof(DateTime?) => DbType.DateTime,
            Type t when t == typeof(decimal) || t == typeof(decimal?) => DbType.Decimal,
            _ => DbType.Object
        };
    }

    // 批量添加输出参数到DynamicParameters
    public static void AddOutputParameters<T>(this DynamicParameters parameters) where T : class
    {
        foreach (var prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            var dbType = GetDbType(prop.PropertyType);
            parameters.Add($"@{prop.Name}", dbType: dbType, direction: ParameterDirection.Output);
        }
    }
}

// 可选:自定义特性,标记不需要作为参数的属性
[AttributeUsage(AttributeTargets.Property)]
public class IgnoreParameterAttribute : Attribute { }
实际调用示例

结合你提供的StudentInput和StudentOutput类,调用存储过程的代码会非常简洁:

using System.Data.SqlClient;

// 1. 初始化输入对象
var studentInput = new StudentInput(); // 默认StudentId=1

// 2. 转换为输入参数
var parameters = studentInput.ToInputParameters();

// 3. 添加输出参数(自动匹配类型)
parameters.AddOutputParameters<StudentOutput>();

// 4. 执行存储过程
using (var conn = new SqlConnection("你的数据库连接字符串"))
{
    conn.Open();
    conn.Execute("你的存储过程名称", parameters, commandType: CommandType.StoredProcedure);
}

// 5. 提取输出结果到StudentOutput对象
var studentOutput = parameters.ToOutputObject<StudentOutput>();

// 验证结果
Console.WriteLine($"学生姓名:{studentOutput.StudentName}");
Console.WriteLine($"喜欢的科目:{studentOutput.FavSubject}");
Console.WriteLine($"年级:{studentOutput.Grade}");
Console.WriteLine($"入学年份:{studentOutput.YearOfJoining}");
优化与注意事项
  • 参数名称匹配:确保存储过程的参数名和类的属性名完全一致(SQL Server默认不区分大小写,但严格匹配能避免意外问题)。如果存储过程参数有前缀(比如@In_StudentId),只需要修改扩展方法里的参数名拼接逻辑即可。
  • 忽略特定属性:如果输入类里有不需要作为参数的属性,给它加上[IgnoreParameter]特性,扩展方法会自动跳过。
  • 可空类型处理:代码里已经处理了DBNull.Value的情况,避免给可空类型赋值时报错。
  • 类型映射扩展:如果你的输出类有更多数据类型(比如Guid、bool),可以在GetDbType方法里添加对应的映射规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:58:33