C# 如何使用Dapper从查询中获取返回值?

C# 如何使用Dapper从查询中获取返回值?,c#,return-value,firebird,dapper,firebird-.net-provider,C#,Return Value,Firebird,Dapper,Firebird .net Provider,我正在尝试使用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\") " + "VA

我正在尝试使用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"); 
}
我在简洁的代码中犯了什么错误?为什么一个版本可以而另一个版本不行?我已经读到Dapper执行ExecuteOnQuery,所以我不认为这是原因。

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

var newId=conn.QuerySingleSQL,new{Id=int?null,Name=newName}; 如果需要其他字段,可以将其扩展为使用与返回的列匹配的自定义返回类型或值元组。例如:

var row=conn.QuerySingleSQL,new{Id=int?null,Name=newName}; 或

var row=conn.QuerySingleSQL,new{Id=int?null,Name=newName}; -编辑 您可以通过以下方式访问返回的值:

int iVal=row.Result.id; 字符串sVal=row.Result.name; returning子句的作用类似于select,因为它返回结果网格中的数据。因此,您的查询应该作为查询执行。这还具有显著简化调用代码的优点:

var newId=conn.QuerySingleSQL,new{Id=int?null,Name=newName}; 如果需要其他字段,可以将其扩展为使用与返回的列匹配的自定义返回类型或值元组。例如:

var row=conn.QuerySingleSQL,new{Id=int?null,Name=newName}; 或

var row=conn.QuerySingleSQL,new{Id=int?null,Name=newName}; -编辑 您可以通过以下方式访问返回的值:

int iVal=row.Result.id; 字符串sVal=row.Result.name;
Dapper的Execute最大的缺点是它返回受更新、删除等影响的行数。。。即使所有事务都发生在错误发生后通过回滚取消的事务中。返回值在回滚之前仍然保留受影响的行号,尽管事务未提交。哎呀

动态参数更复杂,但有效。但在Moq测试中,我遇到了一些我无法轻松解决的异常

我的解决方案与Marc和neggenbe的类似,遵循以下步骤:

在SQL存储过程中,通过返回一个整数值, 用整洁的方式, 如上述示例所示,适当检查结果。
Dapper的Execute最大的缺点是它返回受更新、删除等影响的行数。。。即使所有事务都发生在错误发生后通过回滚取消的事务中。返回值在回滚之前仍然保留受影响的行号,尽管事务未提交。哎呀

动态参数更复杂,但有效。但在Moq测试中,我遇到了一些我无法轻松解决的异常

我的解决方案与Marc和neggenbe的类似,遵循以下步骤:

在SQL存储过程中,通过返回一个整数值, 用整洁的方式, 如上述示例所示,适当检查结果。
您必须从数据库中读取以获取更新的值。您需要执行Select查询。@jdweng不一定;direct ado.net代码使用ExecuteOnQuery,因此这应该可以类似地工作。但是,我同意@jdweng的观点,即在返回时阅读文档,这可能应该是一种查询用法。坦白地说,我不清楚为什么cmd.ExecuteNonQuery版本可以工作。事实上,我使用它来获取几个值返回新ID和一些同步内容的时间戳-目标不是插入/更新数据,然后进行第二次查询以获取更新的数据,就我所知,这正是返回应该用于的…@neggenbe yes,但问题是它是如何回来的;如果它作为一个结果网格返回,那么它应该使用Query执行——尽管命令完全相同——而不是两个查询;您必须从数据库中读取以获取更新的值。您需要执行Select查询。@jdweng不一定;direct ado.net代码使用ExecuteOnQuery,因此这应该可以类似地工作。但是,我同意@jdweng的观点,即在返回时阅读文档,这可能应该是一种查询用法。坦白地说,我不清楚为什么cmd.ExecuteNonQuery版本可以工作。事实上,我使用它来获取几个值返回新ID和一些同步内容的时间戳-目标不是插入/更新数据,然后进行第二次查询以获取更新的数据,就我所知,这正是返回应该用于的…@neggenbe yes,但问题是它是如何回来的;如果它作为一个结果网格返回,那么它应该使用Query执行——尽管命令完全相同——而不是两个查询;旁注:我还没有设法让FbConnection在本地连接以全面查看差异,但我已经让ISQL工具来验证returning子句的行为。我将尝试一下,并让您知道它是否也在工作。Thx为th
不管怎样,我都会收到宝贵的反馈!ok更新了您的答案,以确保它还包含实际读取返回结果的代码。成功了,万分感谢!在Firebird内部,带有RETURNING子句的语句不像select语句那样工作。它是一个单例结果行,在执行之后立即返回,因此不需要从中获取游标。这可能也解释了为什么ExecuteOnQuery可以直接使用Firebird.net提供程序。从技术上讲,它是一个不带列的可执行存储过程。@neggenbe不是当前的;您可以使用Select或SelectMany来填充它,不过:var results=source.Selectx=>conn.QuerySinglesql,new{…x…}.ToList;如果使用Query而不是QuerySingleSide,请选择many注意:我还没有设法让FbConnection在本地连接以完全查看差异,但我已经使用ISQL工具来验证returning子句的行为。我将尝试一下,并让您知道它是否工作正常。谢谢你宝贵的反馈!ok更新了您的答案,以确保它还包含实际读取返回结果的代码。成功了,万分感谢!在Firebird内部,带有RETURNING子句的语句不像select语句那样工作。它是一个单例结果行,在执行之后立即返回,因此不需要从中获取游标。这可能也解释了为什么ExecuteOnQuery可以直接使用Firebird.net提供程序。从技术上讲,它是一个不带列的可执行存储过程。@neggenbe不是当前的;您可以使用Select或SelectMany来填充它,不过:var results=source.Selectx=>conn.QuerySinglesql,new{…x…}.ToList;如果使用Query而不是QuerySingle,请选择many
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();
}
SELECT -1    -- 0 for success, -1 for error

note--> SQL-Returns (ie. RETURN(1)) are ignored for some reason.
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.