使用Dapper.net查询存储过程的返回值

13

我正在尝试使用 Dapper.Net 调用存储过程并获取返回值。

p.Add("@INCIDENT_ID", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);

var retResults = con.Execute("usp_GetIncidentID", p, commandType:CommandType.StoredProcedure);

int IncidentID = p.Get<int>("INCIDENT_ID"); 

我尝试了几种不同的参数方向和使用 "@INCIDENT_ID"。如果您逐步查看结果,您会发现正确的返回值出现在 retResults 值中,但我无法按照文档中描述的方式访问这些值。

存储过程 Dapper 支持完全存储过程:

var user = cnn.Query<User>("spGetUser", new {Id = 1}, 
    commandType: CommandType.StoredProcedure).First();}}}
If you want something more fancy, you can do:

var p = new DynamicParameters();
p.Add("@a", 11);
p.Add("@b", dbType: DbType.Int32, direction: ParameterDirection.Output);
p.Add("@c", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);

cnn.Execute("spMagicProc", p, commandType: commandType.StoredProcedure); 

int b = p.Get<int>("@b");
int c = p.Get<int>("@c");   

你的意思是“我无法按照文档中描述的方式访问这些值”? - MethodMan
1
你尝试过这个吗?var IncidentID = con.Query<int>("usp_GetIncidentID",commandType:CommandType.StoredProcedure).SingleOrDefault();如果你的存储过程是通过“select”返回值,那么这是最简单的方法且可行。 - user1625066
刚试了一下,我得到了{DapperRow, INCIDENT_ID = '902306'},这是我以前看到过的,但现在尝试访问该值时出现了问题。 - yellowfever
DJ Kraze,我编辑了我的问题以引用文档,谢谢! - yellowfever
1
我正在尝试同样的事情,但是收到了“指定的转换无效”的错误。我的返回值是SCOPE_IDENTITY(),应该返回bigint作为主键字段。我将参数声明为DbType.Int64,并使用long值调用泛型参数的get方法,但这并不起作用。由于某种原因,SCOPE_IDENTITY仍然返回int。非常令人沮丧。有任何想法将不胜感激。 - user1790300
6个回答

7

我怀疑(未经测试)这只是你命名参数的不匹配,请尝试使用以下方式(请注意删除了@):

p.Add("INCIDENT_ID", dbType: DbType.Int32, direction: ParameterDirection.ReturnValue);

var retResults = con.Execute("usp_GetIncidentID", p, commandType:CommandType.StoredProcedure);

int IncidentID = p.Get<int>("INCIDENT_ID"); 

Marc,我尝试使用'@'符号,因为在某个地方读到它会有所不同,但实际上并没有。我得到了正确的值,因为我可以查看监视窗口并在结果中看到这些值。只是我不太确定如何以“正确”的方式获取它们。例如: retResults = {DapperRow, INCIDENT_ID = '902479'} 感谢您的回复! - yellowfever
结果是否以数据网格的形式呈现?这个问题暗示你正在更新参数... - Marc Gravell
Marc,是的,那就是结果。据我理解文档,你应该将值作为参数传递,并使用“ParameterDirection.ReturnValue”作为返回类型。结果是正确的,只是我无法使用“p.get<in>(“INCIDENT_ID”)”将其映射到这些结果。这就是我不明白的部分,为什么我无法从dynamicParameter中获取正确的值。再次感谢您的回复,非常感激。 - yellowfever

2

使用 Dapper 的测试版本,我发现这个功能非常好用:

result = dbConnection.ExecuteScalar<int>(typeof(UCCCCException).Name + "_c",
                        obj, commandType: CommandType.StoredProcedure);

我正在编写一个通用程序,用于将任何类型的对象插入到数据库中。

存储过程如下:

ALTER PROCEDURE [dbo].[UCCCCException_c]
(
@Id int = null,
@ExceptionDate datetime = null,
@HResult int = 0,
@Message varchar(8000) = null,
@Source varchar(8000) = null,
@StackTrace varchar(8000) = null,
@Module varchar(8000) = null,
@Name varchar(8000) = null,
@created_at datetime = null,
@updated_at datetime = null,
@created_by int = null,
@updated_by int = null
,
@Creator varchar(255) = null,
@Updator varchar(255) = null
)
AS

-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

Insert into dbo.uccccexceptions ( ExceptionDate, HResult, [Message], [Source], StackTrace, Module, Name)
                            values (
                                    coalesce(@ExceptionDate,getdate()),
                                    @HResult,
                                    @Message,
                                    @Source,
                                    @StackTrace,
                                    @Module,
                                    @Name
                                    )
                                    ;
select @@Identity;
go

这方面需要注意的是,您必须为您使用的类中的每个属性指定一个条目,即使这些属性在目标表中不存在,也必须作为存储过程的参数进行指定。如果您在MVC情况下有许多视图字段,这可能会很麻烦。
要获取返回值,您只需要使用Execute,并确保您的最后一条语句是Select @@Identity在存储过程中。
它完美地工作,并允许我在我的资料库中编写通用插入命令,如下所示:
        public virtual int Insert(T obj)
    {
        int result = -2;
        if (!databaseOnline)
        {
            throw new Exception(HttpStatusCode.ServiceUnavailable.ToString());
        }
        if (obj != null)
        {
            try
            {
                using (IDbConnection dbConnection = ConnectionProvider.OpenConnection())
                {
                    dbConnection.Open();
                    result = dbConnection.ExecuteScalar<int>(typeof(T).Name + "_c",
                        obj, commandType: CommandType.StoredProcedure);
                }
            }
            catch (SqlException sqlex)
            {
                Logger.LogError(sqlex, false);
                throw;
            }
            catch (Exception ex)
            {
                Logger.LogError(ex);
                throw;
            }
        }
        return result;
    }

我在我的数据库中使用一种约定,即存储过程的名称是类型名称后跟"_s"表示选择,"_c"表示插入,"_d"表示删除和"_u"表示更新。

PS:我不喜欢使用Dapper的DynamicParameters或任何其他需要您在通用存储库中为每个类使用不同的插入或更新方法的设备。


1
您可以如下读取该值。
var incidentId = retResults.ToList()[0].INCIDENT_ID; 

2
当您尝试获取空结果集的返回值时,它无法工作! - alerya

0
我为了简单起见将其简化了。我使用输出参数,因为这使我完全控制传回的数据类型。我可以在任何其他情况下使用此方法,而不仅仅是插入。
存储过程:
ALTER PROCEDURE User_Insert (
@EmailAddress nvarchar(255),
@Id bigint OUT
) AS
INSERT INTO User (
        [EmailAddress]
    ) VALUES (
        @EmailAddress
    )
    set @Id = SCOPE_IDENTITY()

代码仓库:

var sql = "Execute User_Insert @EmailAddress, @Id = @Id OUTPUT";
var _params = new DynamicParameters();
_params.Add("EmailAddress", user.EmailAddress);
_params.Add("Id", dbType: DbType.Int64, direction: ParameterDirection.Output);

using (var connection = new SqlConnection(ConnectionString)) {
    connection.Open();
    using (var transaction = connection.BeginTransaction()) {
        var result = SqlMapper.Execute(connection, sql, param, transaction);
        transaction.Commit();
    }
}    
var id = _params.Get<long>("Id");

0

DynamicParameters解决方案对我来说不够简洁。更好的方法是让存储过程代码返回一个整数(Select 1;),然后使用dapper ExecuteScalar<int>("[sp_name]", params);


0
我遇到了一个类似的问题,读取QueryMultiple的结果时。第一次调用Read()返回了正确的类型,第二次返回了DapperRow。我发现使用带类型的版本Read():
var lineExists = query.Read<int?>().FirstOrDefault() == 1;

问题已解决。


1
如果结果集中没有任何行而你又需要返回一个值,那该怎么办? - alerya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接