FireDAC CmdExecTimeout超时设置失效问题求助
问题描述
我希望限制查询获取数据的耗时,本应通过控制Execute和Open命令的ResourceOptions.CmdExecTimeout属性实现,但设置后该超时被忽略,查询无响应且未触发异常。
同步查询示例代码
procedure TfrmMainForm.btnSynchronousOpenClick(Sender: TObject); begin var Query := TFDQuery.Create(Self); Query.Connection := cnConnection; Query.SQL.Text := ''' DECLARE @X int WHILE 1=1 -- Infinite Loop SET @X = 1 '''; Query.ResourceOptions.CmdExecTimeout := 1000; try Query.Open; ShowMessage('Query Opened'); except on E: EFDDBEngineException do if E.Kind = ekCmdAborted then ShowMessage('Query Aborted'); end; end;
上述SQL为无限循环,预期1000ms后触发超时异常,但无任何反应。
连接配置
cnConnection通过Microsoft ODBC Driver 17 for SQL Server连接SQL Server:
object cnConnection: TFDConnection Params.Strings = ( 'Database=agilITy_0002' 'Server=10.0.0.48' 'OSAuthent=Yes' 'MARS=yes' 'ODBCAdvanced=TrustServerCertificate=Yes' 'DriverID=MSSQL') Connected = True LoginPrompt = False Left = 60 Top = 152 end
异步查询尝试
我也尝试了异步查询,同样未触发异常:
procedure TfrmMainForm.QueryOpened(Dataset: TDataset); begin ShowMessage('Query Opened'); end; procedure TfrmMainForm.btnAynchronousOpenClick(Sender: TObject); begin var Query := TFDQuery.Create(Self); Query.Connection := cnConnection; Query.SQL.Text := ''' DECLARE @X int WHILE 1=1 -- Infinite Loop SET @X = 1 '''; Query.ResourceOptions.CmdExecMode := amAsync; Query.ResourceOptions.CmdExecTimeout := 1000; Query.AfterOpen := QueryOpened; try Query.Open; except on E: EFDDBEngineException do if E.Kind = ekCmdAborted then ShowMessage('Query Aborted'); end; end;
请问我哪里操作错误?如何正确设置查询打开的超时时间?
解决方案
1. 修正SQL字符串语法错误
你的代码中Query.SQL.Text的赋值存在核心问题:使用三重单引号导致实际传递给SQL Server的语句被外层单引号包裹,变成了字符串字面量而非可执行的批处理语句。SQL Server不会执行循环逻辑,自然不会触发超时。
正确的SQL赋值写法(二选一):
- 单引号转义的多行写法:
Query.SQL.Text := 'DECLARE @X int'#13#10 + 'WHILE 1=1 -- Infinite Loop'#13#10 + ' SET @X = 1';
- Delphi 10.2+支持的多行字符串语法:
Query.SQL.Text := ` DECLARE @X int WHILE 1=1 -- Infinite Loop SET @X = 1 `;
2. 协同配置FireDAC与ODBC超时
在ODBC驱动连接SQL Server的场景下,仅设置FireDAC的CmdExecTimeout可能无法生效,需要配合ODBC驱动的查询超时参数:
方法一:连接级别全局设置
在TFDConnection的参数中添加QueryTimeout(单位为秒),对应ODBC的SQL_ATTR_QUERY_TIMEOUT,强制SQL Server在指定时间后终止查询:
object cnConnection: TFDConnection Params.Strings = ( 'Database=agilITy_0002' 'Server=10.0.0.48' 'OSAuthent=Yes' 'MARS=yes' 'ODBCAdvanced=TrustServerCertificate=Yes' 'DriverID=MSSQL' 'QueryTimeout=1') // 1秒超时 Connected = True LoginPrompt = False Left = 60 Top = 152 end
方法二:单查询精细控制
如果需要为不同查询设置不同超时,可在代码中同时配置FireDAC和ODBC参数:
procedure TfrmMainForm.btnSynchronousOpenClick(Sender: TObject); begin var Query := TFDQuery.Create(Self); Query.Connection := cnConnection; // 修正后的SQL Query.SQL.Text := ` DECLARE @X int WHILE 1=1 -- Infinite Loop SET @X = 1 `; // FireDAC层面超时(毫秒) Query.ResourceOptions.CmdExecTimeout := 1000; // ODBC驱动超时(秒),与FireDAC超时同步 Query.Connection.Params.Values['QueryTimeout'] := '1'; try Query.Open; ShowMessage('Query Opened'); except on E: EFDDBEngineException do if E.Kind = ekCmdAborted then ShowMessage('Query Aborted'); end; end;
3. 异步模式的额外配置
异步模式下需启用FireDAC的异步等待机制,确保超时触发:
procedure TfrmMainForm.btnAynchronousOpenClick(Sender: TObject); begin var Query := TFDQuery.Create(Self); Query.Connection := cnConnection; Query.SQL.Text := ` DECLARE @X int WHILE 1=1 -- Infinite Loop SET @X = 1 `; Query.ResourceOptions.CmdExecMode := amAsync; Query.ResourceOptions.CmdExecTimeout := 1000; Query.AfterOpen := QueryOpened; // 启用异步超时检查 Query.ResourceOptions.WaitForAsync := True; try Query.Open; // 等待执行完成或超时 Query.WaitFor(1000); except on E: EFDDBEngineException do if E.Kind = ekCmdAborted then ShowMessage('Query Aborted'); end; end;
内容的提问来源于stack exchange,提问作者Marc Guillot
相关产品推荐
相关产品推荐

