使用Dapper在C#代码中传递输出参数给存储过程

78

我有一个存储过程的格式如下:

CREATE PROCEDURE SP_MYTESTpROC
    @VAR1 VARCHAR(10),
    @VAR2 VARCHAR(20),
    @BASEID INT ,
    @NEWID INT OUTPUT
As Begin
   INSERT INTO TABLE_NAME(username, firstname)
      select @VAR1, @VAR2 
      WHERE ID = @BASEID

   SET @NEWID = SCOPE_IDENTITY() AS INT
END

我正在使用Dapper从C#代码调用这个存储过程。我的问题是:在使用Dapper的同时如何将输出参数传递给存储过程?


11
请注意:您不应该为存储过程使用 sp_ 前缀。微软已经将该前缀保留给自己使用(请参阅命名存储过程,而且您可能会在未来遇到名称冲突的风险。 这也会影响您的存储过程性能。最好是避免使用 sp_ 前缀,改用其他前缀或根本不使用前缀! - marc_s
回复:在存储过程名称中使用sp_,正如我之前对@Steve提供的答案评论中所指出的,这绝对是针对MS SQL服务器的。 - lacoder
以下内容不是 dapper ... 对于那些感兴趣的人,我在这里展示了一个 MySQL / c# Visual Studio 2015 的工作示例 HERE。那种情况涉及到 IN 和 OUT 参数。自然地,重点在于 OUT - Drew
3个回答

124

仅通过搜索Tests.cs文件,您就可以找到此示例

    public void TestProcSupport()
    {
        var p = new DynamicParameters();
        p.Add("a", 11);
        p.Add("b", dbType: DbType.Int32, direction: ParameterDirection.Output);
        p.Add("c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
        connection.Execute(@"create proc #TestProc 
                         @a int,
                             @b int output
                             as 
                             begin
                                 set @b = 999
                                 select 1111
                                 return @a
                             end");
        connection.Query<int>("#TestProc", p, commandType: CommandType.StoredProcedure).First().IsEqualTo(1111);
        p.Get<int>("c").IsEqualTo(11);
        p.Get<int>("b").IsEqualTo(999);
    }

所以,我想你的C#代码可以这样写

    public void InsertData()
    {
        var p = new DynamicParameters();
        p.Add("VAR1", "John");
        p.Add("VAR2", "McEnroe");
        p.Add("BASEID", 1);
        p.Add("NEWID", dbType: DbType.Int32, direction: ParameterDirection.Output);
        connection.Query<int>("SP_MYTESTpROC", p, commandType: CommandType.StoredProcedure);
        int newID =  p.Get<int>("NEWID");
    }

顺便提一下,不要使用SP作为存储过程的前缀。它被保留用于系统定义的过程,如果微软决定使用相同的名称,您可能会遇到麻烦。虽然这很不可能,但这是一个不好的习惯,为什么要冒险呢?


嗨,史蒂夫,我会尝试这个,它似乎合理而全面。是的,我知道存储过程的命名约定,我想使用的是 spMyTestProc - lacoder
1
这个代码运行得很干净,但是我不得不将 First() 更改为 FirstOrDefault(),因为 First() 一直返回 sequence contains no elements - lacoder
1
可能你可以移除它(FirstOrDefault)。我在从示例中复制/粘贴时忘记清除它(First)。(而在那个示例中有一个原因,因为他们返回了 @a,而你的存储过程中没有 @a) - Steve
是的!我刚刚把“FirstOrDefault()”去掉了,它起作用了。 - lacoder
1
如果TestProc正在执行插入操作,并且您调用.Execute而不是.Query,是否有一种方法可以批处理过程调用并获取在批处理中进行的每个调用的输出参数和返回值? - user1790300
我不确定。可能是,但我建议您在此发布一个新问题(Dapper标签),这样您就可以吸引Dapper的大师们的注意,他们是该网站非常活跃的用户。 - Steve

16

根据 "ath" 的建议:为了避免反射,DynamicParams.AddDynamicParams() 接收一个匿名对象,之后您可以像这样添加返回参数...

var param = new { A="a", B="b" };
var dynamicParameters = new DynamicParameters();
dynamicParameters.AddDynamicParams(param);
dynamicParameters.Add("return", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);

现在,在您的dapper调用中使用dynamicParameters对象而不是匿名参数对象。

(如果更喜欢,也可以对输出参数执行此操作)


2
我认为 AddDynamicParams(parameters) 应该改成 param 而不是 parameters,但我不能只编辑这个,因为它必须是6个字符。 - Jason Goemaat

2
如果你总是有一个名为@id的INTEGER类型的OUTPUT参数(@id = @id OUTPUT),你可以创建一个扩展方法,像这样使用常规的Dapper语法传递SQL字符串和一个anonymous对象:最初的回答
using Dapper;
using System.Data;
using System.Data.SqlClient;

public static int ExecuteOutputParam
            (this IDbConnection conn, string sql, object args)
        {
            // Stored procedures with output parameter require
            // dynamic params. This assumes the OUTPUT parameter in the
            // SQL is an INTEGER named @id.
            var p = new DynamicParameters();
            p.Add("id", dbType: DbType.Int32, direction: ParameterDirection.Output);

            var properties = args.GetType().GetProperties();
            foreach (var prop in properties)
            {
                var key = prop.Name;
                var value = prop.GetValue(args);

                p.Add(key, value);
            }

            conn.Execute(sql, p);

            int id = p.Get<int>("id");
            return id;
        }

这段代码使用反射来读取所有属性,但如果你可以接受这种惩罚,就不必为每个调用繁琐地编写DynamicParameters

对于事务,请在SqlTransaction上创建一个扩展方法,并将其传递给Execute方法:

最初的回答:

transaction.Connection.Execute(sql, p, transaction);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接