在 Delphi 异常中获取 MS SQL Server 异常用户消息

And*_*res 1 sql-server delphi exception

我正在使用 Datasnap.DBClient 从我的 Delphi 应用程序访问 SQL Server 数据库。

我有一个记录器记录我的应用程序中的每个异常,但我发现 SQL 异常消息真的很不友好。

示例:当我在 Delphi 中捕获 SQL 异常时,我得到的消息是:

EOleException:SQL 状态:42S22,SQL 错误代码:207

如果我在 SQL Profiler 中打开跟踪,我会收到两条消息:

第一个异常与应用程序显示的消息相同:

例外:错误:207,严重性:16,状态:1

第二个,是:

用户错误消息:无效的列名“Column1”

是否有可能从我的 Delphi 应用程序中捕获的异常中获取此UserErrorMessage

Mar*_*ynA 5

我不确定您使用的是 Ado 还是 DBExpress,但如果是 DBExpress,您可能不会问这个问题(请参阅下面的更新)。

所述TAdoConnection具有一个Errors对象(参见定义中AdoInt.Pas)。为了调查它,我在服务器上使用了一个存储过程,定义为

create PROCEDURE [dbo].[spRaiseError](@AnError int)
AS
BEGIN
  declare @Msg Char(20)
  if @AnError > 0
    begin
      Select @Msg = 'MyError ' + convert(Char(8), @AnError)
      RaisError(@Msg, 16, -1)
    end
  else
    select 1
END
Run Code Online (Sandbox Code Playgroud)

然后,在我的 Delphi 代码中,我有这样的事情:

uses [...] AdoInt, AdoDB, [...]

procedure TForm1.Button1Click(Sender: TObject);
var
  S : String;
  IErrors : Errors;
  IError : Error;
  ErrorCount : Integer;
  i : Integer;
begin
  S := 'exec spRaiseError ' + Edit1.Text;
  AdoQuery1.SQL.Text := S;
  try
    AdoQuery1.Open;
  except
    IErrors := AdoConnection1.Errors;
    ErrorCount := IErrors.Count;
    for i := 0 to ErrorCount - 1 do begin
      IError := IErrors.Item[i];
      S := Format('error: %d, source: %s description: %s', [i, IError.Source, IError.Description]);
      Memo1.Lines.Add(S);
    end;
    Caption := IntToStr(ErrorCount);
  end;
end;
Run Code Online (Sandbox Code Playgroud)

如果您尝试一下,您应该会发现Errors集合的内容是累积的,因此它应该捕获您所追求的第二条消息。但是,当您说您使用“Datasnap dbclient”时,我不完全确定您的意思:“DBClient”是声明 TClientDataSet 的单元,但您可能将它与 ADO 或 DBExpress 一起使用。我不确定 DBX Sql Server 驱动程序是否与 AdoConnections Errors 集合有任何对应物,您可以通过使用 DBX 组件获得。

更新 使用上面的代码,如果我执行了一个故意引用不存在的列“aname”的 SELECT,我会在 Memo1 中得到这个:

错误:0,来源:Microsoft OLE DB Provider for SQL Server 描述:列名“aname”无效。

,但它不会报告您得到的错误 207。

如果我在 DBX 项目中执行相同的 SELECT 并在打开连接到 SqlQuery 的 ClientDataSet 时捕获异常,如下所示:

procedure TForm1.Button1Click(Sender: TObject);
var
  S : String;
begin
  S := 'select id, aname from atable';
  ClientDataSet1.CommandText := S;
  try
    ClientDataSet1.Open;
  except
    Memo1.Lines.Add(Exception(ExceptObject).Message);
  end;
end;
Run Code Online (Sandbox Code Playgroud)

我明白了

SQL 状态:42000,SQL 错误代码:8180 无法准备语句。SQL 状态:42S22,SQL 错误代码:207 列名“aname”无效。

这在某些方面提供了更多信息。同时,您的日志记录功能会记录以下内容:

Fecha: 2017-02-03 18:44:02
Sesion: {2E7176CB-56FD-41C4-BE67-7B9E1D3486B4}
Proyecto: dbxerrors.exe
Aplicación:    
Ruta: D:\aaad7\Ado\
Usuario: ???
Error: SQL State: 42000, SQL Error Code: 8180
Statement(s) could not be prepared.
SQL State: 42S22, SQL Error Code: 207
Invalid column name 'aname'.

Exception EDatabaseError: SQL State: 42000, SQL Error Code: 8180
Statement(s) could not be prepared.
SQL State: 42S22, SQL Error Code: 207
Invalid column name 'aname'.

    Exception 
    UnitName : 
    Procedure : 
    Line : 0
    BinaryFileName : 


Fecha: 2017-02-03 18:44:02
Sesion: {2E7176CB-56FD-41C4-BE67-7B9E1D3486B4}
Proyecto: dbxerrors.exe
Aplicación:    
Ruta: D:\aaad7\Ado\
Usuario: ???
Error: SQL State: 42000, SQL Error Code: 8180
Statement(s) could not be prepared.
SQL State: 42S22, SQL Error Code: 207
Invalid column name 'aname'
Exception EOleException: SQL State: 42000, SQL Error Code: 8180
Statement(s) could not be prepared.
SQL State: 42S22, SQL Error Code: 207
Invalid column name 'aname'
    Exception 
    UnitName : 
    Procedure : 
    Line : 0
    BinaryFileName : 
Run Code Online (Sandbox Code Playgroud)

如果有帮助,这是我的测试项目的 DFM 的部分摘录

  object SQLConnection1: TSQLConnection
    ConnectionName = 'MSSQLConnection'
    DriverName = 'MSSQL'
    GetDriverFunc = 'getSQLDriverMSSQL'
    LibraryName = 'dbexpmss.dll'
    LoginPrompt = False
    Params.Strings = (
      'DriverName=MSSQL'
      'HostName=MAT410\ss2014'
      'DataBase=MATest'
      'User_Name=sa'
      'Password=sa'
      'BlobSize=-1'
      'ErrorResourceFile='
      'LocaleCode=0000'
      'MSSQL TransIsolation=ReadCommited'
      'OS Authentication=False')
    VendorLib = 'oledb'
    Left = 40
    Top = 32
  end
  object SQLQuery1: TSQLQuery
    MaxBlobSize = -1
    Params = <>
    SQLConnection = SQLConnection1
    Left = 88
    Top = 32
  end
  object DataSetProvider1: TDataSetProvider
    DataSet = SQLQuery1
    Options = [poAllowCommandText]
    Left = 136
    Top = 32
  end
  object ClientDataSet1: TClientDataSet
    Aggregates = <>
    Params = <>
    ProviderName = 'DataSetProvider1'
    Left = 184
    Top = 32
  end
Run Code Online (Sandbox Code Playgroud)

如您所见,它非常简约。Delphi 版本在 Win10 64 位上为 D7,服务器为 Sql Server 2014。