我遇到了Dapper调用存储过程的情况,它执行一些验证逻辑,更新一行,然后将行结果作为强类型POCO返回。
问题在于验证逻辑,因为有多个退出条件,我想为给定情况返回错误代码 - 当Dapper QueryAsync
没有收到与给定POCO匹配的结果集时它会抛出一个InvalidOperationException
。
(在任何人评论混合逻辑与存储过程之前,我理解这里的问题,但是由于严格的性能限制,这是不可避免的。设计不是要解决的问题。)
Dapper如何处理可以返回POCO或错误代码的存储过程?
在使用QueryAsync<T>
时,我在Dapper教程中没有看到有关读取存储过程返回码的任何内容。
所以我最初的想法是做这样的事情(下面的伪代码):
SELECT TOP 0 FROM PocoTable
)SELECT -1 AS ErrorCode
作为最终结果集conn.QueryAsync<Poco>()
执行conn.QueryMultiple()
,然后执行ReadAsync<Poco>
,如果返回null,则ReadAsync<int>
返回错误代码有没有更好的方法来解决这个问题?
此处的示例代码:
(请注意,这是伪代码,只是为了传达这个想法,实际上并不是一个可执行的例子 - 请不要评论纠正语法或用法)
存储过程:
CREATE PROCEDURE dbo.SwapData
@ID1 INT,
@ID2 INT
AS
BEGIN
DECLARE @Count INT
DECLARE @Swap Table
(
[ID] INT Identity(1,1),
[PocoID] INT,
[MyData] [nvarchar](255)
)
INSERT INTO @Swap
SELECT PocoID, MyData
FROM PocoTable
WHERE PocoID IN (@ID1, @ID2)
SELECT @Count = COUNT(1) FROM @Swap
IF @Count <> 2
BEGIN
SELECT TOP 0 NULL FROM PocoTable
SELECT -1 AS ErrorCode
RETURN -1 -- never used in C# just returning non-0 for convention
END
UPDATE ORIG
SET ORIG.MyData = swap.MyData
FROM PocoTable AS ORIG
INNER JOIN @Swap AS swap ON (ORIG.PocoID = @ID1 AND swap.ID = 2)
OR (ORIG.PocoID = @ID2 AND swap.ID = 1)
SELECT PocoID, MyData
FROM PocoTable
WHERE PocoID = @ID1
END
存储库:
public class Repo
{
public Task<Poco> SwapData(int targetId, int sourceId)
{
using (var conn = GetSqlConnection())
{
await conn.OpenAsync();
var mrs = await conn.QueryMultipleAsync(
"dbo.SwapData",
new { ID1 = targetId, ID2 = sourceId },
commandType: CommandType.StoredProcedure);
var result = await mrs.ReadFirstOrDefaultAsync<TType>();
if (result != null) return result;
var errorCode = await mrs.ReadFirstAsync<int>();
throw new PocoRepositorySwapException(errorCode);
}
}
}
更新
我发现了一个很好的解决方案,同步但不是异步!
存储过程w /简单返回:
CREATE PROCEDURE dbo.SwapData
@ID1 INT,
@ID2 INT
AS
BEGIN
DECLARE @Count INT
DECLARE @Swap Table
(
[ID] INT Identity(1,1),
[PocoID] INT,
[MyData] [nvarchar](255)
)
INSERT INTO @Swap
SELECT PocoID, MyData
FROM PocoTable
WHERE PocoID IN (@ID1, @ID2)
SELECT @Count = COUNT(1) FROM @Swap
IF @Count <> 2
BEGIN
RETURN -1
END
UPDATE ORIG
SET ORIG.MyData = swap.MyData
FROM PocoTable AS ORIG
INNER JOIN @Swap AS swap ON (ORIG.PocoID = @ID1 AND swap.ID = 2)
OR (ORIG.PocoID = @ID2 AND swap.ID = 1)
SELECT PocoID, MyData
FROM PocoTable
WHERE PocoID = @ID1
END
存储库(同步,工作):
public class Repo
{
public Task<Poco> SwapData(int targetId, int sourceId)
{
using (var conn = GetSqlConnection())
{
var p = new { ID1 = targetId, ID2 = sourceId };
var pWithReturnValue = new DynamicParameters(p);
p.Add("return", null, DbType.Int32, ParameterDirection.ReturnValue);
// no exception when synchronous
var result = conn.Query<Poco>("dbo.SwapData", p, commandType: CommandType.StoredProcedure);
var errorCode = p.Get<int>("return");
if (errorCode != 0)
throw new SwapRepositoryException(errorCode);
return result.FirstOrDefault();
}
}
}
存储库(异步,Dapper异常):
public class Repo
{
public Task<Poco> SwapData(int targetId, int sourceId)
{
using (var conn = GetSqlConnection())
{
var p = new { ID1 = targetId, ID2 = sourceId };
var pWithReturnValue = new DynamicParameters(p);
p.Add("return", null, DbType.Int32, ParameterDirection.ReturnValue);
// "InvalidOperationException: No columns were selected" happens here when async
var result = await conn.QueryAsync<Poco>("dbo.SwapData", p, commandType: CommandType.StoredProcedure);
var errorCode = p.Get<int>("return");
if (errorCode != 0)
throw new SwapRepositoryException(errorCode);
return result.FirstOrDefault();
}
}
}
唯一的变化是Query to QueryAsync,导致InvalidOperationException。