使用Dapper类型的QueryAsync与存储过程返回码的模式

时间:2017-08-28 17:40:35

标签: c# sql-server dapper

我遇到了Dapper调用存储过程的情况,它执行一些验证逻辑,更新一行,然后将行结果作为强类型POCO返回。

问题在于验证逻辑,因为有多个退出条件,我想为给定情况返回错误代码 - 当Dapper QueryAsync没有收到与给定POCO匹配的结果集时它会抛出一个InvalidOperationException

(在任何人评论混合逻辑与存储过程之前,我理解这里的问题,但是由于严格的性能限制,这是不可避免的。设计不是要解决的问题。)

Dapper如何处理可以返回POCO或错误代码的存储过程?

在使用QueryAsync<T>时,我在Dapper教程中没有看到有关读取存储过程返回码的任何内容。

所以我最初的想法是做这样的事情(下面的伪代码):

  1. 存储过程:使其始终返回POCO列作为第一个结果集(SELECT TOP 0 FROM PocoTable
  2. 存储过程:然后SELECT -1 AS ErrorCode作为最终结果集
  3. C#存储库:而不是执行conn.QueryAsync<Poco>()执行conn.QueryMultiple(),然后执行ReadAsync<Poco>,如果返回null,则ReadAsync<int>返回错误代码
  4. 有没有更好的方法来解决这个问题?

    此处的示例代码:

    (请注意,这是伪代码,只是为了传达这个想法,实际上并不是一个可执行的例子 - 请不要评论纠正语法或用法)

    存储过程:

    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。

0 个答案:

没有答案