【问题标题】:How to get return value from query with Dapper?如何使用 Dapper 从查询中获取返回值?
【发布时间】:2020-03-28 12:49:22
【问题描述】:

我正在尝试使用 Dapper 从插入查询中获取返回值。

这是我尝试使其工作的方法:

// the query with a "returning" statement
// note : I have a trigger that sets the Id to a new value using the generator IF Id is null...
string SQL = "UPDATE OR INSERT INTO \"MyTable\" (\"Id\", \"Name\") " + "VALUES (@Id, @Name) RETURNING \"Id\"";
using (var conn = new FbConnection(MyConnectionString)) {
    var parameters = new DynamicParameters();
    parameters.Add("Id", null, System.Data.DbType.Int32);
    parameters.Add("Name", "newName", System.Data.DbType.String);
    // --- also add the returned parameters
    parameters.Add("retval", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);
    // execute the query with Dapper....
    conn.Execute(SQL, parameters);
    // expecting the new ID here but it is ALWAYS null....!!!
    var newId = parameters.Get<object>("retval"); 
}

现在,为了确保我的查询没有问题,而不是问题的根源,我用我的实际连接器(在本例中为 Firebird)实现了一个类似的代码,如下所示:

using (var conn = new FbConnection(MyConnectionString)) {
    FbCommand cmd = new FbCommand(SQL, conn);
    cmd.Parameters.Add("Id", null);
    cmd.Parameters.Add("Name", "newName");
    FbParameter pRet = cmd.Parameters.Add("retval", FbDbType.Integer);
    pRet.Direction = ParameterDirection.ReturnValue;
    conn.Open();
    cmd.ExecuteNonQuery();
    // => the new value is NOT null here, it returns the correct id!!
    var newId = Convert.ToInt32(pRet.Value);
    conn.Close();
}

我在 Dapper 代码中的错误是什么?为什么一个版本可以,而另一个版本不行?我读过 Dapper 执行 ExecuteNonQuery() 所以我不认为这是原因。

【问题讨论】:

  • 您必须从数据库中读取以获取更新的值。您需要执行一个选择查询。
  • @jdweng 不一定;直接 ado.net 代码使用 ExecuteNonQuery 所以这应该类似
  • 但是,我同意@jdweng 的观点——阅读returning 上的文档——这可能应该Query&lt;T&gt; 的用法。坦率地说,我非常不清楚为什么 cmd.ExecuteNonQuery() 版本有效。
  • 实际上,我使用它来取回几个值(新 ID 和同步内容的一些时间戳) - 目标不是插入/更新数据,然后进行第二次查询以获取更新的数据,这据我所知,这正是 RETURNING 应该用于的用途......
  • @neggenbe 是的,但问题是 如何 它回来了;如果这作为结果网格返回,那么它应该使用Query&lt;T&gt; 执行 - 完全相同的命令,但 - 不是两个查询;

标签: c# return-value firebird dapper firebird-.net-provider


【解决方案1】:

returning 子句的作用类似于select,因为它在结果网格中返回数据。因此,您的查询应该作为查询执行。这还具有显着简化调用代码的优点:

var newId = conn.QuerySingle<int>(SQL, new { Id = (int?)null, Name = "newName" });

如果您需要其他字段,可以将其扩展为使用与返回的列匹配的自定义返回类型或值元组。例如:

var row = conn.QuerySingle<MyTable>(SQL, new { Id = (int?)null, Name = "newName" });

var row = conn.QuerySingle<(int id, string name)>(SQL, new { Id = (int?)null, Name = "newName" });

-- 编辑 您可以通过

访问返回的值
int iVal = row.Result.id;
string sVal = row.Result.name; 

【讨论】:

  • 旁注:我还没有设法让FbConnection 在本地连接以全面了解差异,但我已经让 ISQL 工具验证returning 子句的行为。
  • 我会试一试,让您知道它是否工作正常。无论如何,感谢您提供宝贵的反馈!
  • ok 更新了您的答案,以确保它还包含实际读取返回结果的代码。做到了,谢谢一百万!
  • 在 Firebird 内部,带有 RETURNING 子句的语句不像 select 那样工作(很遗憾)。它是在执行后立即返回的单例结果(行)(因此您不需要从中获取游标)。这可能也解释了为什么ExecuteNonQuery 可以直接使用 Firebird .net 提供程序。从技术上讲,它表现为带有 OUT 列的(可执行)存储过程。
  • @neggenbe 目前没有;不过,您可以使用 Select 或 SelectMany 填充它:var results = source.Select(x =&gt; conn.QuerySingle&lt;ReturnType&gt;(sql, new {...x...}).ToList();(如果使用 Query 而不是 QuerySingle,则为 SelectMany)
【解决方案2】:

Dapper 的 Execute() 的最大缺点是它返回“受影响的行数”(通过更新、删除等)... 即使所有发生在发生错误后的事务中,通过 ROLLBACK 取消。返回值仍然保留回滚前的受影响行数,但未提交事务。 哎呀!!

DynamicParameters() 更复杂,但有效。但在 Moq Tests 中,我遇到了一些我无法轻易解决的异常。

我的解决方案(类似于 Marc 和 neggenbe 的)遵循以下步骤:

  1. 在 SQL 存储过程中,通过返回一个整数值,
SELECT -1    -- 0 for success, -1 for error

note--> SQL-Returns (ie. RETURN(1)) are ignored for some reason.
  1. 直接使用 Dapper,
int result = conn.QueryFirst<int>(SProcName, new { id = req.Id, value = req.Value }, commandType: CommandType.StoredProcedure);

note--> Other commands work as well with differing return types:
           QueryFirst:       result = key/value where value=[return value]
           QueryFirst<int>:  result = integer
           QuerySingle:      Detailed by Marc and neggenbe's answer.
  1. 如上例所示,适当检查结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-16
    • 2020-08-19
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    • 2013-11-22
    相关资源
    最近更新 更多