【问题标题】:Return Resultset from SQL Server to VB.NET application从 SQL Server 返回结果集到 VB.NET 应用程序
【发布时间】:2014-11-28 00:28:25
【问题描述】:

我需要从 SQL Server 存储过程的 CATCH 子句中返回一个包含数据库错误的结果集,但我还是坚持了下来。我是否需要使用游标来返回结果集?如果需要,那么我的 .NET 应用程序中 OUTPUT 参数的类型声明是什么?我试过ObjectVariant 但没有用。

我还尝试了使用SELECT 语句返回的简单方法,它适用于一个存储过程,但不适用于另一个存储过程,因此在我的CATCH 子句中:

 while (@I <= @count)
 begin      
    BEGIN TRY       
        -- delete all previous rows inserted in @customerRow for previous counts @I     
        delete from @customerRow

        -- this is inserting the current row that we want to save in database
        insert into @customerRow 
           SELECT  
               [id],[firstname], [lastname], [street], [city],
               [phone],[mobile],[fax], [email], [companyName],
               [licence],[brn], [vat], [companyStreet], [companyCity], [status]
           FROM 
               (SELECT  
                   ROW_NUMBER() OVER (ORDER BY id ASC) AS rownumber,
                   [id], [firstname], [lastname], [street], [city],
                   [phone], [mobile], [fax], [email], [companyName],
                   [licence], [brn], [vat], [companyStreet], [companyCity], [status]
                FROM    
                   @registerDetails) AS foo
          WHERE 
             rownumber = @I                                             

       -- this stored procedure handles the saving of the current customer row just defined above
        -- if there is any error from that sproc, it will jump to CATCH block 
        --save the error message in the temp table and continue 
        --with the next customer row in the while loop.
        exec dbo.sp_SaveCustomer @customerRow
    END TRY
    BEGIN CATCH
        IF @@TranCount = 0
        -- Transaction started in procedure.
        -- Roll back complete transaction.
        ROLLBACK TRANSACTION;
        if XACT_STATE()= -1 rollback transaction

        DECLARE @ErrorMessage NVARCHAR(4000);
        DECLARE @ErrorSeverity INT;
        DECLARE @ErrorState INT;


        SELECT @ErrorMessage = ERROR_MESSAGE() + ' ' + (select firstname from @registerDetails where id=@I)
        SELECT @ErrorSeverity = ERROR_SEVERITY();
        SELECT @ErrorState = ERROR_STATE() 

        INSERT INTO #registrationResults (error,id)
        SELECT @ErrorMessage, @I 

    END CATCH       

    set @I = @I +1              
end 
COMMIT TRANSACTION registerTran

select * from #registrationResults

当我在我的 vb.net 代码中调用一个存储过程时,上述内容适用于:

ta.Fill(registrationErrors, clientDetailsDT)

其中registrationErrorsclientDetailsDT 是强类型数据表。

这个没有:

    begin catch
    IF @@TranCount > 0 or XACT_STATE()= -1 ROLLBACK TRANSACTION;
    DECLARE @ErrorMessage NVARCHAR(4000);
    DECLARE @ErrorSeverity INT;
    DECLARE @ErrorState INT;
    DECLARE @ErrorLine INT;


    SELECT @ErrorMessage = ERROR_MESSAGE();
    SELECT @ErrorSeverity = ERROR_SEVERITY();
    SELECT @ErrorState = ERROR_STATE();
    SELECT @ErrorLine = ERROR_Line();

    ****ERROR -- THIS SELECT WAS MESSING ALL UP as it was this select that was being        returned to the .NET and not the select of the desired #temp table after, hence returning 0    resultset as this select was EMPTY. !!  
    select status_indicator from InsertInvoiceTriggerData where session_GUID =   guid**
    delete from InsertInvoiceTriggerData where session_GUID = @guid**

    INSERT INTO #registrationResults (error,id)
    SELECT @ErrorMessage, NULL

    select * from #registrationResults

end catch

关于如何返回结果集有什么建议吗?

【问题讨论】:

    标签: sql-server vb.net resultset cursors


    【解决方案1】:

    我还没有看到您的数据库代码,但根据我的经验,catch 捕获的第一个错误意味着必须回滚整个事务。除其他外,这还意味着在任何给定情况下,我返回的错误不会超过 1 个。

    因此,我在存储过程中使用了 2 个标量输出参数,即:

    @Error int = null output,
    @Message nvarchar(2048) = null output
    

    我可以像检索任何其他输出变量一样检索它们。

    UPD:即使您添加了一些代码,我仍然无法准确理解您的问题。但是,我发现您的代码存在一些问题,因此我会指出它们,并且很有可能其中一个可以解决问题。我只评论第一个 sn-p,因为最后一个太不完整了。

    • 您应该在循环之前的某处启动了最外层事务。否则,代码将失败。
    • 如果我猜对了,您在dbo.sp_SaveCustomer 存储过程中实现了所有保存点逻辑。如果不是,那么整个讨论就毫无意义,因为您显示的代码中没有 save tranrollback @savepoint 语句。
    • 第一个catch 语句-IF @@TranCount = 0 ROLLBACK TRANSACTION 完全错误。如果条件成功,将导致尝试回滚不存在的事务时出错。如果您依赖保存点,则不应出现在此处。
    • 下一个应该导致无条件中断: if XACT_STATE()= -1 begin rollback transaction; break; end;
    • 其余的 catch 代码可以替换为: INSERT INTO @registrationResults (error, id) SELECT error_message() + ' ' + firstname, id from @registerDetails where id=@I;
    • 此外,切勿将临时表用于此目的,因为回滚也会影响它们。始终为此使用表变量,它们是非事务性的(就像任何其他变量一样)。
    • commit 应该是有条件的,因为此时您可能最终没有要提交的事务: if @@trancount > 0 commit tran;
    • commit 语句中指定保存点名称是没有意义的,它只会导致混淆(尽管不被视为错误)。此外,此模块中不应有任何保存点(除非您在循环之前定义了它)。

    我怀疑这只是冰山一角,因为我不知道dbo.SaveCustomer 存储过程中实际发生了什么。

    UPD2:这是我用来从存储过程接收记录集的 VB.NET 代码示例:

    Private Function SearchObjectsBase( _
        SearchMode As penum_SEARCH_MODE, SearchCriteria As String
    ) As DataSet
    
    Dim Cmd As DbCommand, Pr As DbParameter, dda As DbDataAdapter
    
    ' Initialise returning dataset object
    SearchObjectsBase = New DataSet()
    
    Cmd = MyBase.CreateCommand(String.Format("dbo.{0}", SearchMode))
    
    With Cmd
    
        ' Parameter definitions
        Pr = .CreateParameter()
        With Pr
            .ParameterName = "@SearchCriteria"
            .DbType = DbType.Xml
            .Value = SearchCriteria
        End With
        .Parameters.Add(Pr)
    
        ' Create data adapter to use its Fill() method
        dda = DbProviderFactories.GetFactory(.Connection).CreateDataAdapter()
        ' Assign the prepared DbCommand as a select method for the adapter
        dda.SelectCommand = Cmd
    
        ' A single resultset is expected here
        dda.Fill(SearchObjectsBase)
    
    End With
    
    ' Set error vars and get rid of it
    Call MyBase.SetErrorOutput(Cmd)
    
    ' Check for errors and, if any, discard the dataset
    If MyBase.ErrorNumber <> 0 Then SearchObjectsBase.Clear()
    
    End Function
    

    我使用.NET 4.5,它有一个非常好的方法可以根据实际连接自动选择最合适的数据适配器。 这是这个函数的调用:

    Dim XDoc As New XElement("Criteria"), DS As DataSet = Nothing, DT As DataTable
    ...
    DS = .SearchPatients(XDoc.ToString(SaveOptions.None))
    ' Assign datasource to a grid
    Me.dgr_Search.DataSource = DS.Tables.Item(0)
    

    这里,SearchPatients() 是 SearchObjectsBase() 之上的一个包装器。

    【讨论】:

    • 标量参数与您提到的单个输出一起工作正常,但在我的情况下,我有一个事务在一个 while 循环中进行,每次事务不成功时,它都会回滚到保存的点并将错误消息保存在上面的临时表中。循环完成后,它会很好地返回结果集。但是假设你想从 CATCH 块中返回你在临时表中提到的参数,你将如何实现呢?
    • 好吧,您可以将错误结果集转换为 XML 并通过 XML 输出参数返回。因为,即使到 2014 年,表变量参数仍然是只读的。
    • 请再看一遍原帖,我已经包含了数据库代码。此外,根据 MSDN 文档,这适用于存储过程,但我们如何从 .NET 中读取游标? msdn.microsoft.com/en-us/library/ms188655.aspx
    • 恐怕我无法理解您的问题。你有正确的代码和错误的代码;为什么要尝试使用错误的?它将为遇到的每个错误生成一个单独的记录集,每个错误都有一行。为什么你不能使用正确的?
    • 而且,为了记录,返回游标的参数的数据类型被调用,很奇怪,cursor。但是我一点也不知道如何在客户端处理它,假设它是可能的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-06
    • 1970-01-01
    • 1970-01-01
    • 2014-12-07
    • 1970-01-01
    相关资源
    最近更新 更多