c# - 为什么 Entity Framework 对具有事务和 RAISERROR 的存储过程抛出事务计数错误?

标签 c# sql-server entity-framework t-sql

这是我的带有输出字符串参数的存储过程。

Create
 PROCEDURE [dbo].[SpTest]
    (@ReturnMessage varchar(50) output)
AS
    BEGIN
        SET NOCOUNT ON;
        BEGIN TRY
            BEGIN TRANSACTION;
            RAISERROR('asdf',16,1)
            COMMIT TRANSACTION;
        END TRY
        BEGIN CATCH
            ROLLBACK TRANSACTION;
            DECLARE @ErrorMessage VARCHAR(50)= ERROR_MESSAGE();

            IF ( @ErrorMessage = 'asdf' )
                BEGIN
                    SET @ReturnMessage = @ErrorMessage;
                    RETURN;
                END;
            ELSE


            THROW;
        END CATCH;
    END;

我正在尝试使用 Raiserror 手动捕获错误并将错误作为输出参数发送。 当我尝试从管理工作室执行该过程时,该过程工作正常,但是当我从应用程序执行时, Entity Framework 会抛出此错误 :

SqlException: Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 1, current count = 0.

但是如果我删除 RAISERROR 它就可以正常工作。

我正在使用 Entity Framework 版本 6 的 C# MVC 应用程序。 这是我的 C# 代码:

public ReturnMessageModel Test()
        {
            ReturnMessageModel result = new ReturnMessageModel();
            ObjectParameter returnMessage = new ObjectParameter("ReturnMessage", typeof(String));
            using (InsurestEntities db = new InsurestEntities())
            {
           db.SpTest(returnMessage);
            }
                result.ReturnMessage = returnMessage.Value.ToString();
            }
            return result;
        }

更新:当我删除此代码时,我发现了错误,事务计数错误已自动解决。

IF ( @ErrorMessage = 'asdf' )
                BEGIN
                    SET @ReturnMessage = @ErrorMessage;
                    RETURN;
                END;

现在工作正常。我认为与 EntityFramework 一起使用时,RAISEERROR 必须始终由 THROW 语句处理。

最佳答案

您在 C# 代码中确实有事务,只是您不知道它们并且没有显式创建它们。 EF 正在为某些操作创建自己的 TransactionScope。

在此存储过程中存在事务时,您的错误处理将不起作用。你已经发现了一个原因。您还会遇到其他问题,例如在没有事务时尝试回滚。 CATCH block 必须检查XACT_STATE() block 内的值并采取相应的行动。

如果您希望在存在事务时有正确的错误处理模式,请参阅 Exception handling and nested transactions :

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch   
end
go

关于c# - 为什么 Entity Framework 对具有事务和 RAISERROR 的存储过程抛出事务计数错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45030772/

相关文章:

用于搜索术语文本的 C# 代码/算法

c# - 如何获取图像文件的尺寸?

sql-server - 使用 tsql 连接到 tdsool 超时

asp.net-mvc - MVC6 全局范围的值

c# - 如何调试 Linq Lambda 表达式?

c# - 如何重置缩放 oxyplot c# wpf

c# - Azure WCF 服务上的 maxReceivedMessageSize 太小

sql-server - 什么会阻止在 Docker 容器中运行的代码连接到单独服务器上的数据库?

sql-server - SQL Server 和 ORACLE 中的 STUFF 函数

c# - 将Guid.NewGuid添加到SQL数据库问题