【问题标题】:Can I use DynamicParameters with Template and have a return parameter in dapper?我可以在模板中使用 DynamicParameters 并在 dapper 中有一个返回参数吗?
【发布时间】:2011-12-22 02:40:55
【问题描述】:

我目前正在使用的系统使用存储过程进行所有数据访问。我目前正在研究 Dapper(到目前为止它看起来很棒),但我想知道是否可以使用使用模板创建的 DynamicParameters 对象,但将其中一个参数设为输出参数。例如:

SP:

CREATE PROCEDURE InsertPerson
  @ID int Output,
  @Name varchar(100),
  @DOB DateTime2
AS
--INSERT STATEMENT

SET @ID = SCOPE_IDENTITY()

POCO:

internal class Person
{
  public int ID { get; set; }
  public string Name { get; set; }
  public DateTime DOB { get; set; }
}

代码:

var procParams = new DynamicParameters(person);
connection.Execute("InsertPerson", procParams, commandType: CommandType.StoredProcedure);

// This is where i'm having the issue, can it be done?
person.ID = procParams.Get<int>("ID");

当前我收到一个错误,因为找不到密钥。有没有办法在不手动设置所有存储的 procs 参数的情况下获取 ID 输出参数?

【问题讨论】:

    标签: c# .net dapper


    【解决方案1】:

    通过快速调整,Add 现在替换模板中的值,允许:

    public void TestProcWithOutParameter()
    {
        connection.Execute(
            @"CREATE PROCEDURE #TestProcWithOutParameter
    @ID int output,
    @Foo varchar(100),
    @Bar int
    AS
    SET @ID = @Bar + LEN(@Foo)");
        var obj = new
        { // this could be a Person instance etc
            ID = 0,
            Foo = "abc",
            Bar = 4
        };
        var args = new DynamicParameters(obj);
        args.Add("ID", 0, direction: ParameterDirection.Output);
        connection.Execute("#TestProcWithOutParameter", args,
                     commandType: CommandType.StoredProcedure);
        args.Get<int>("ID").IsEqualTo(7);
    }
    

    够近了吗?您还可以使用ParameterDirection.ReturnValue,既可以是预先存在的值,也可以是新值。请注意,它不会直接更新回原始模板;该值必须从DynamicParameters 实例中获取(如图所示)。

    【讨论】:

    • 我假设该值可以通过使用.Output&lt;T&gt;(T, Expression&lt;Func&lt;T, object&gt;&gt;) 方法自动更新DynamicParameters 对吧?
    【解决方案2】:

    当您使用DynamicParameters 的构造函数来指定模板对象时,您仍然需要指定@ID 是一个输出参数。首先,通过模板,它将被设置为ParameterDirection.Input。添加后,它将被覆盖以具有更新的值,然后您可以通过参数名称获取值,如下所示:

    procParams.Add("@ID", dbType: DbType.Int32, direction: ParameterDirection.Output);
    // ... execute ...
    person.ID = procParams.Get<int>("@ID");
    

    除了上面展示的内容之外,我还使用了您的类和代码。

    编辑: 正如 cmets 中所讨论的,存储过程不接受比它声明的更多的参数。因此,另一种方法是放弃存储过程并使用一些内联 SQL。当您使用查询时,Dapper 将忽略任何未在 SQL 语句中指定的参数。这是解决此问题的解决方法:

    string sql = "INSERT INTO Person (Name, DOB) VALUES (@Name, @DOB) SELECT SCOPE_IDENTITY()";
    decimal id = conn.Query<decimal>(sql, procParams).First();
    person.ID = (int)id;
    

    请注意,SCOPE_IDENTITY() 返回的是小数,而不是 int。

    另一个我认为不太理想的想法是修改 Dapper 代码并在 DynamicParameters 类中添加一个 Remove 方法以删除不需要的参数。不过,这并没有为您节省太多,因为您仍然会花时间指定要删除所有参数以使存储过程满意。如果您决定实现这一点,请记住在指定要从 parameters 字典中删除的键时,大小写很重要。

    【讨论】:

    • 我已经尝试过了,但是我得到一个异常“Procedure or function InsertPerson has too many arguments specified.”。对不起,应该把它放在问题中。
    • @Fermin 当参数包含的参数多于存储过程中声明的参数数量时,您会遇到异常。由于您将person 作为模板传递给DynamicParameters 构造函数,因此Person 类上的所有属性都为其生成了一个参数。所以我怀疑你的班级不仅仅是IDNameDOB。如果是这种情况,那么我认为您需要自己指定参数,如上所示添加它们。
    • 我很怀疑,我正在尝试编写一个通用的 DataAccess 类,因此试图避免单独指定参数。
    • @Ahmed 感谢您对此的看法;碰巧,Add 已经是一个 replace,所以在许多方面它应该已经工作了,但是由于各种原因,template 与显式参数的处理方式不同。通过快速调整,Add 现在可以覆盖模板中的值 - 请参阅我的答案以了解它现在如何工作。这不会带来兼容性风险,因为这种情况以前总是会在异常中失败。
    • @AhmadMageed 我已将 1.7 推送到 nuget
    猜你喜欢
    • 2016-08-12
    • 2014-06-07
    • 2017-11-13
    • 1970-01-01
    • 2020-02-20
    • 1970-01-01
    • 2020-06-16
    • 1970-01-01
    相关资源
    最近更新 更多