Parameter.AsString 在 Oracle/MSSQL 下失败 - Parameter.Value Oracle 下的 2 字节字符

Parameter.AsString failing under Oracle/MSSQL - Parameter.Value 2-byte chars under Oracle

更改为 FireDAC 后,我无法让此代码在 MSSQL/Oracle 上运行:

with DataFormsettings do
begin
  Close;
  if Params.Count=0 then FetchParams;
  Params.ParamByName('TT_EMP_ID').Asinteger := AEmpID;
  Params.ParamByName('TT_FORM').AString := UpperCase(AKey);  
  Open;
  if (RecordCount>0) then
     S := FieldByName('TT_VIEWDATA').Asstring;     
end;   

AKey和S都是字符串

Open语句报错

[FireDAC][Phys][MSSQL]-338 Param type changed from [ftString] to [ftWidestring]
[FireDAC][Phys][Ora]-338 Param type changed from [ftString] to [ftWidestring]

连接到 MSSQL 或 Oracle 数据库时;连接到 FireBird 时不是。
FetchParams 之后,DataFormsettings.params[1].datatype 始终是 ftString

如果我替换

Params.ParamByName('TT_FORM').AString := UpperCase(AKey);  

Params.ParamByName('TT_FORM').Value := UpperCase(AKey);

...Open语句没有错误。我认为这已经解决了它,尽管我并不真正理解错误。毕竟这应该都是默认的 Delphi 字符串类型...
但是现在 S 分配对于 Oracle(不是 FireBird 或 MSSQL)失败了,因为我看到返回了 2 个字节的字符。 S 包含:

\'#0'S'#0'o'#0'f'#0't'#0'w'#0'a'#0'r'#0'e'#0'\'#0'T'#0'i'#0'm'#0'e'#0'T'#0'e'#0'l'#0'l'#0'...

我可以处理

S := TEncoding.Unicode.GetString(FieldByName('TT_VIEWDATA').AsBytes);  

对于 Oracle,但是(当然)在使用其他两种不起作用的数据库类型时:

No mapping for the Unicode character exists in the target multi-byte code page

我在这里错过了什么?具体来说,我只想让 AsString retrievals/assignments 工作。
请注意 设置 AsString 属性 将 DataType 属性 设置为 ftWideString 或 ftString FireDAC TFDParam.AsString documentation 中的注释。似乎参数值分配只是将类型从 ftString 切换为 ftWideString(如原始错误所示)。

DataFormSettings 是客户端应用程序中的 TClientDataSet,连接到 TDataSetProviderTFDQuery 所在的服务器应用程序。查询是

select
  TT_FORMSETTINGS_ID,
  TT_EMP_ID,
  TT_FORM,
  TT_VERSION,
  TT_VIEWDATA
from TT_FORMSETTINGS
where TT_EMP_ID=:TT_EMP_ID
and TT_FORM=:TT_FORM

创建的表如下:

火鸟:

CREATE TABLE TT_FORMSETTINGS
(
  TT_FORMSETTINGS_ID    INTEGER DEFAULT 0 NOT NULL,
  TT_EMP_ID     INTEGER,
  TT_FORM       VARCHAR(50),
  TT_VERSION        INTEGER,
  TT_VIEWDATA       BLOB SUB_TYPE TEXT SEGMENT SIZE 80,
  TT_TAG    INTEGER,
  TT_TAGTYPE    INTEGER,
  TT_TAGDATE    TIMESTAMP
);

甲骨文:

CREATE TABLE TT_FORMSETTINGS
(
  TT_FORMSETTINGS_ID    NUMBER(10,0) DEFAULT 0 NOT NULL,
  TT_EMP_ID     NUMBER(10,0),
  TT_FORM       VARCHAR(50),
  TT_VERSION        NUMBER(10,0),
  TT_VIEWDATA       CLOB,
  TT_TAG    NUMBER(10,0),
  TT_TAGTYPE    NUMBER(10,0),
  TT_TAGDATE    DATE
);

MSSQL:

CREATE TABLE TT_FORMSETTINGS
(
  TT_FORMSETTINGS_ID    INTEGER  NOT NULL CONSTRAINT TT_C0_FORMSETTINGS DEFAULT 0,
  TT_EMP_ID     INTEGER NULL,
  TT_FORM       VARCHAR(50) NULL,
  TT_VERSION        INTEGER NULL,
  TT_VIEWDATA       TEXT NULL,
  TT_TAG    INTEGER NULL,
  TT_TAGTYPE    INTEGER NULL,
  TT_TAGDATE    DATETIME NULL
);

我已经检查 TT_VIEWDATA 在所有数据库中包含正确的数据;它是一个包含 CRLF 的长字符串:

\Software\TimeTell\Demo8\Forms\TFormTileMenu'#$D#$A'Version,1,80502'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu\FormTileMenu.TileControlMenu'#$D#$A'Version,4,2'#$D#$A'\Software\TimeTell\Demo8\Forms\TFormTileMenu\TileControlMenu\FormTileMenu.TileControlMenu...

备注:


附加信息:这是在 DataFormsettings TClientDataset 上具有持久字段定义的遗留应用程序。 TT_VIEWDATA 定义为 TMemoField:

DataFormsettingsTT_VIEWDATA: TMemoField;

在一个小型测试应用程序(直接连接到 Oracle;不是客户端-服务器)中,我让 Delphi 添加字段定义,然后它说:

DataFormsettingsTT_VIEWDATA: TWideMemoField;

如果我在主应用程序中使用它,Oracle 工作正常,但随后我得到 'garbage' for MSSQL。

我还尝试为 Oracle 连接设置映射规则,例如(许多变体):

with AConnection.FormatOptions.MapRules.Add do
begin
  SourceDataType := dtWideMemo;
  TargetDataType := dtMemo;
end;
AConnection.FormatOptions.OwnMapRules := true;

但这并没有帮助。

这不是最终的解决方案,请参阅代码块之前的最后评论。它仍然感觉像一个黑客。我没有将它添加到问题中(如 'attempts'),因为最终这会起作用。

发生了两件事,它们都可以通过以下更改解决:

  1. Param 类型已更改 Params 值分配错误
  2. 字段定义和 FieldByName().AsString retrieval/assigning 不工作

请注意,我在整个应用程序中受到设计时字段定义的限制,该应用程序必须处理所有三种数据库类型,特别是 DataFormSettingsTT_VIEWDATA 持久字段是 TMemoField

使用问题底部提到的 table 定义,如果您设置 TFDConnection -> TFDQuery -> TDataSetProvider -> TClientDataSet 并使用 添加所有字段[=79= 添加字段定义], DataFormSettingsTT_VIEWDATA 将是类型:

  • TMemoFieldBlobType=ftMemoField for FireBird

  • TMemoFieldBlobType=ftWideMemoField for MSSQL

  • TWideMemoField 对于 Oracle BlobType=ftWideMemoField

手动编辑 .DFM 和 .PAS 以将 Oracle TWideMemoField 设置回 TMemoField 可行(好吧,我不必更改它,它是遗留代码)如果我还:

  • force BlobType=ftWideMemoField for the design time TMemoFields at 运行 time(我可以在父级的 OnCreate 中做到这一点,我的所有数据模块都来自);

  • 处理字符串检索 仅适用于 Oracle 作为 TEncoding.Unicode.GetString(FieldByName(SFormSettingsViewData).AsBytes)

但这仍然不是最优的。我的客户端代码带有 TClientDataSet will now have to know what kind of database it is。我可以在客户端应用程序中查询服务器。

这是一个包含这些更改的示例应用程序:

uFireDacOracleBlob.pas 文件:

unit uFireDacOracleBlob;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, FireDAC.Stan.Intf, FireDAC.Stan.Option,
  FireDAC.Stan.Error, FireDAC.UI.Intf, FireDAC.Phys.Intf, FireDAC.Stan.Def,
  FireDAC.Stan.Pool, FireDAC.Stan.Async, FireDAC.Phys, FireDAC.Phys.Oracle,
  FireDAC.Phys.OracleDef, FireDAC.VCLUI.Wait, FireDAC.Stan.Param, FireDAC.DatS,
  FireDAC.DApt.Intf, FireDAC.DApt, Datasnap.DBClient, Datasnap.Provider,
  Data.DB, FireDAC.Comp.DataSet, FireDAC.Comp.Client, Vcl.StdCtrls, Vcl.ExtCtrls,
  FireDAC.Phys.MSSQL, FireDAC.Phys.MSSQLDef, FireDAC.Phys.IB,
  FireDAC.Phys.IBDef, FireDAC.Phys.FBDef, FireDAC.Phys.IBBase, FireDAC.Phys.FB,
  FireDAC.Phys.ODBCBase;

type
  TFrmFireDacOracleBlob = class(TForm)
    FDConnection1: TFDConnection;
    FDPhysOracleDriverLink1: TFDPhysOracleDriverLink;
    FDQuery1: TFDQuery;
    DataSetProvider1: TDataSetProvider;
    ClientDataSet1: TClientDataSet;
    Edit0: TEdit;
    Label1: TLabel;
    LblPos0: TLabel;
    RGpDB: TRadioGroup;
    BtnOpen: TButton;
    FDConnection2: TFDConnection;
    FDQuery2: TFDQuery;
    DataSetProvider2: TDataSetProvider;
    ClientDataSet2: TClientDataSet;
    FDConnection0: TFDConnection;
    FDQuery0: TFDQuery;
    DataSetProvider0: TDataSetProvider;
    ClientDataSet0: TClientDataSet;
    FDPhysMSSQLDriverLink1: TFDPhysMSSQLDriverLink;
    FDPhysFBDriverLink1: TFDPhysFBDriverLink;
    ClientDataSet0TT_FORMSETTINGS_ID: TIntegerField;
    ClientDataSet0TT_EMP_ID: TIntegerField;
    ClientDataSet0TT_FORM: TStringField;
    ClientDataSet0TT_VERSION: TIntegerField;
    ClientDataSet0TT_VIEWDATA: TMemoField;

    ClientDataSet1TT_FORMSETTINGS_ID: TIntegerField;
    ClientDataSet1TT_EMP_ID: TIntegerField;
    ClientDataSet1TT_FORM: TStringField;
    ClientDataSet1TT_VERSION: TIntegerField;
    ClientDataSet1TT_VIEWDATA: TMemoField;

    ClientDataSet2TT_FORMSETTINGS_ID: TIntegerField;
    ClientDataSet2TT_EMP_ID: TIntegerField;
    ClientDataSet2TT_FORM: TStringField;
    ClientDataSet2TT_VERSION: TIntegerField;
    ClientDataSet2TT_VIEWDATA: TMemoField;
    BtnSet: TButton;
    Label2: TLabel;
    LblPos1: TLabel;
    Edit1: TEdit;
    Label4: TLabel;
    LblPos2: TLabel;
    Edit2: TEdit;
    BtnParam: TButton;
    procedure BtnOpenClick(Sender: TObject);
    procedure BtnSetClick(Sender: TObject);
    procedure BtnParamClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    FStrFirebird,
    FStrOracle,
    FStrMSSQL   :String;
    procedure ShowString(AStr: String; ALbl: TLabel; AEdit: TEdit);
  public
  end;

var
  FrmFireDacOracleBlob: TFrmFireDacOracleBlob;

implementation

{$R *.dfm}

const
   cSQLText = 'select TT_FORMSETTINGS_ID,TT_EMP_ID,TT_FORM,TT_VERSION,TT_VIEWDATA from TT_FORMSETTINGS where TT_EMP_ID=:TT_EMP_ID and TT_FORM=:TT_FORM';

procedure TFrmFireDacOracleBlob.BtnParamClick(Sender: TObject);
begin
  case RGpDB.ItemIndex of
     0: begin
           FDQuery0.SQL.Text := cSQLText;
           with ClientDataSet0 do
           begin
              if Params.Count=0 then FetchParams;
              Params.ParamByName('TT_EMP_ID').Asinteger := 1;
              Params.ParamByName('TT_FORM').AsString := 'TFORMTILEMENU';
              Open;
              if (RecordCount>0) then
                 FStrFirebird := FieldByName('TT_VIEWDATA').Asstring;
              ShowString(FStrFireBird,LblPos0,Edit0);
           end;
        end;
     1: begin
           FDQuery1.SQL.Text := cSQLText;
           with ClientDataSet1 do
           begin
              if Params.Count=0 then FetchParams;
              Params.ParamByName('TT_EMP_ID').Asinteger := 1;
              Params.ParamByName('TT_FORM').AsString := 'TFORMTILEMENU';
              Open;
              if (RecordCount>0) then
                 // FStrOracle := FieldByName('TT_VIEWDATA').Value;
                 FStrOracle := TEncoding.Unicode.GetString(FieldByName('tt_viewdata').AsBytes);
              ShowString(FStrOracle,LblPos1,Edit1);
           end;
        end;
     2: begin
           FDQuery2.SQL.Text := cSQLText;
           with ClientDataSet2 do
           begin
              if Params.Count=0 then FetchParams;
              Params.ParamByName('TT_EMP_ID').Asinteger := 1;
              Params.ParamByName('TT_FORM').AsString := 'TFORMTILEMENU';
              Open;
              if (RecordCount>0) then
                 FStrMSSQL := FieldByName('TT_VIEWDATA').Asstring;
              ShowString(FStrMSSQL,LblPos2,Edit2);
           end;
        end;
  end;
end;

procedure TFrmFireDacOracleBlob.BtnSetClick(Sender: TObject);
begin
  case RGpDB.ItemIndex of
     0: begin
           FStrFirebird := FStrFirebird + #13#10'Added another line';
           ClientDataSet0.Edit;
           ClientDataSet0.FieldByName('tt_viewdata').Value := FStrFireBird;
           ClientDataSet0.ApplyUpdates(0);
        end;
     1: begin
           FStrOracle := FStrOracle + #13#10'Added another line';
           ClientDataSet1.Edit;
           // ClientDataSet1.FieldByName('tt_viewdata').AsString := FStrOracle; // does not work
           // ClientDataSet1.FieldByName('tt_viewdata').Value := FStrOracle;    // does not work
           ClientDataSet1.FieldByName('tt_viewdata').Value := TEncoding.Unicode.GetBytes(FStrOracle);
           // ClientDataSet1.FieldByName('tt_viewdata').AsBytes := TEncoding.Unicode.GetBytes(FStrOracle);  Also works
           ClientDataSet1.ApplyUpdates(0);
        end;
     2: begin
           FStrMSSQL := FStrMSSQL + #13#10'Added another line';
           ClientDataSet2.Edit;
           ClientDataSet2.FieldByName('tt_viewdata').AsString := FStrFireBird;
           ClientDataSet2.ApplyUpdates(0);
        end;
  end;
end;

procedure TFrmFireDacOracleBlob.FormCreate(Sender: TObject);
var i: integer;
begin
   for i := 0 to self.ComponentCount-1 do
      if (self.Components[i] is TMemoField) then
         (self.Components[i] as TMemoField).BlobType := ftWideMemo;
end;

procedure TFrmFireDacOracleBlob.ShowString(AStr: String; ALbl: TLabel; AEdit: TEdit);
begin
  ALbl.Caption := IntToStr(Pos(#13#10,AStr));
  AEdit.Text := AStr;
end;

procedure TFrmFireDacOracleBlob.BtnOpenClick(Sender: TObject);
begin
  case RGpDB.ItemIndex of
     0: begin
           // SetFireBirdMapRules(FDConnection1);   Design time
           ClientDataSet0.Open;
           FStrFirebird := ClientDataSet0.FieldByName('tt_viewdata').AsString;
           ShowString(FStrFireBird,LblPos0,Edit0);
        end;
     1: begin
           // SetOracleMapRules(FDConnection1);   Design time
           ClientDataSet1.Open;
           // FStrOracle := ClientDataSet1.FieldByName('tt_viewdata').AsString;
           FStrOracle := TEncoding.Unicode.GetString(ClientDataSet1.FieldByName('tt_viewdata').AsBytes);
           ShowString(FStrOracle,LblPos1,Edit1);
        end;
     2: begin
           // SetMSSQLMapRules(FDConnection1);   Design time
           ClientDataSet2.Open;
           FStrMSSQL := ClientDataSet2.FieldByName('tt_viewdata').AsString;
           ShowString(FStrMSSQL,LblPos2,Edit2);
        end;
  end;
end;

end.

uFireDacOracleBlob.dfm 文件:

object FrmFireDacOracleBlob: TFrmFireDacOracleBlob
  Left = 0
  Top = 0
  Caption = 'FireDac and Oracle Clobs'
  ClientHeight = 278
  ClientWidth = 577
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  Position = poScreenCenter
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 32
    Top = 161
    Width = 91
    Height = 13
    Caption = 'Position first CRLF:'
  end
  object LblPos0: TLabel
    Left = 128
    Top = 161
    Width = 6
    Height = 13
    Caption = '0'
  end
  object Label2: TLabel
    Left = 32
    Top = 203
    Width = 91
    Height = 13
    Caption = 'Position first CRLF:'
  end
  object LblPos1: TLabel
    Left = 128
    Top = 203
    Width = 6
    Height = 13
    Caption = '0'
  end
  object Label4: TLabel
    Left = 32
    Top = 245
    Width = 91
    Height = 13
    Caption = 'Position first CRLF:'
  end
  object LblPos2: TLabel
    Left = 128
    Top = 245
    Width = 6
    Height = 13
    Caption = '0'
  end
  object Edit0: TEdit
    Left = 32
    Top = 138
    Width = 505
    Height = 21
    TabOrder = 0
  end
  object RGpDB: TRadioGroup
    Left = 32
    Top = 8
    Width = 249
    Height = 33
    Columns = 3
    ItemIndex = 0
    Items.Strings = (
      'FireBird'
      'Oracle'
      'MSSQL')
    TabOrder = 1
  end
  object BtnOpen: TButton
    Left = 32
    Top = 56
    Width = 75
    Height = 25
    Caption = 'Open Table'
    TabOrder = 2
    OnClick = BtnOpenClick
  end
  object BtnSet: TButton
    Left = 120
    Top = 56
    Width = 75
    Height = 25
    Caption = 'Update field'
    TabOrder = 3
    OnClick = BtnSetClick
  end
  object Edit1: TEdit
    Left = 32
    Top = 180
    Width = 505
    Height = 21
    TabOrder = 4
  end
  object Edit2: TEdit
    Left = 32
    Top = 222
    Width = 505
    Height = 21
    TabOrder = 5
  end
  object BtnParam: TButton
    Left = 32
    Top = 96
    Width = 104
    Height = 25
    Caption = 'Open with params'
    TabOrder = 6
    OnClick = BtnParamClick
  end
  object FDConnection1: TFDConnection
    Params.Strings = (
      'User_Name=testv4'
      'Password=testv4'
      'Database=VS2003-2005-10'
      'DriverID=Ora')
    FormatOptions.AssignedValues = [fvMapRules]
    FormatOptions.OwnMapRules = True
    FormatOptions.MapRules = <
      item
        SourceDataType = dtBCD
        TargetDataType = dtInt32
      end
      item
        SourceDataType = dtFmtBCD
        TargetDataType = dtDouble
      end>
    Connected = True
    LoginPrompt = False
    Left = 312
    Top = 72
  end
  object FDPhysOracleDriverLink1: TFDPhysOracleDriverLink
    Left = 368
    Top = 72
  end
  object FDQuery1: TFDQuery
    Connection = FDConnection1
    SQL.Strings = (
      'select * from tt_formsettings')
    Left = 416
    Top = 72
  end
  object DataSetProvider1: TDataSetProvider
    DataSet = FDQuery1
    Left = 464
    Top = 72
  end
  object ClientDataSet1: TClientDataSet
    Aggregates = <>
    Params = <>
    ProviderName = 'DataSetProvider1'
    Left = 512
    Top = 72
    object ClientDataSet1TT_FORMSETTINGS_ID: TIntegerField
      FieldName = 'TT_FORMSETTINGS_ID'
      Required = True
    end
    object ClientDataSet1TT_EMP_ID: TIntegerField
      FieldName = 'TT_EMP_ID'
    end
    object ClientDataSet1TT_FORM: TStringField
      FieldName = 'TT_FORM'
      Size = 50
    end
    object ClientDataSet1TT_VERSION: TIntegerField
      FieldName = 'TT_VERSION'
    end
    object ClientDataSet1TT_VIEWDATA: TMemoField
      FieldName = 'TT_VIEWDATA'
      BlobType = ftWideMemo
    end
  end
  object FDConnection2: TFDConnection
    Params.Strings = (
      'Database=test'
      'Password=test'
      'User_Name=test'
      'Server=VS2003-2008'
      'DriverID=MSSQL')
    FormatOptions.AssignedValues = [fvMapRules]
    FormatOptions.OwnMapRules = True
    FormatOptions.MapRules = <
      item
        SourceDataType = dtDateTimeStamp
        TargetDataType = dtDateTime
      end>
    Connected = True
    LoginPrompt = False
    Left = 312
    Top = 144
  end
  object FDQuery2: TFDQuery
    Connection = FDConnection2
    SQL.Strings = (
      'select * from tt_formsettings')
    Left = 416
    Top = 144
  end
  object DataSetProvider2: TDataSetProvider
    DataSet = FDQuery2
    Left = 464
    Top = 144
  end
  object ClientDataSet2: TClientDataSet
    Aggregates = <>
    Params = <>
    ProviderName = 'DataSetProvider2'
    Left = 512
    Top = 144
    object ClientDataSet2TT_FORMSETTINGS_ID: TIntegerField
      FieldName = 'TT_FORMSETTINGS_ID'
      Required = True
    end
    object ClientDataSet2TT_EMP_ID: TIntegerField
      FieldName = 'TT_EMP_ID'
    end
    object ClientDataSet2TT_FORM: TStringField
      FieldName = 'TT_FORM'
      Size = 50
    end
    object ClientDataSet2TT_VERSION: TIntegerField
      FieldName = 'TT_VERSION'
    end
    object ClientDataSet2TT_VIEWDATA: TMemoField
      FieldName = 'TT_VIEWDATA'
      BlobType = ftMemo
    end
  end
  object FDConnection0: TFDConnection
    Params.Strings = (
      'Database=D:\Testing\Diverse\FireDacOracleBlob\TIMETELL_DEMO.GDB'
      'User_Name=SYSDBA'
      'Password=masterkey'
      'DriverID=IB')
    FormatOptions.AssignedValues = [fvMapRules]
    FormatOptions.OwnMapRules = True
    FormatOptions.MapRules = <
      item
        SourceDataType = dtDateTimeStamp
        TargetDataType = dtDateTime
      end
      item
        SourceDataType = dtSingle
        TargetDataType = dtDouble
      end>
    Connected = True
    LoginPrompt = False
    Left = 312
    Top = 8
  end
  object FDQuery0: TFDQuery
    Connection = FDConnection0
    SQL.Strings = (
      'select * from tt_formsettings')
    Left = 416
    Top = 8
  end
  object DataSetProvider0: TDataSetProvider
    DataSet = FDQuery0
    Left = 464
    Top = 8
  end
  object ClientDataSet0: TClientDataSet
    Aggregates = <>
    Params = <>
    ProviderName = 'DataSetProvider0'
    Left = 512
    Top = 8
    object ClientDataSet0TT_FORMSETTINGS_ID: TIntegerField
      FieldName = 'TT_FORMSETTINGS_ID'
      Required = True
    end
    object ClientDataSet0TT_EMP_ID: TIntegerField
      FieldName = 'TT_EMP_ID'
    end
    object ClientDataSet0TT_FORM: TStringField
      FieldName = 'TT_FORM'
      Size = 50
    end
    object ClientDataSet0TT_VERSION: TIntegerField
      FieldName = 'TT_VERSION'
    end
    object ClientDataSet0TT_VIEWDATA: TMemoField
      FieldName = 'TT_VIEWDATA'
      BlobType = ftMemo
    end
  end
  object FDPhysMSSQLDriverLink1: TFDPhysMSSQLDriverLink
    Left = 368
    Top = 144
  end
  object FDPhysFBDriverLink1: TFDPhysFBDriverLink
    Left = 368
    Top = 8
  end
end

注意:参数分配现在(也)起作用的事实在 Data Type Mapping (FireDAC) 文档中:

In case of a result set column, each rule defines a transformation of a source data type, returned by a driver, into a target one, preferred by an application. In case of a command parameter, the rule defines a transformation of a target data type, specified by an application, into a source data type, supported by a driver. All rules, excluding the name-based ones, work bidirectionally for both cases.

这是它不起作用的原因:

FireDAC.Stan.Option中:

procedure TFDFormatOptions.ColumnDef2FieldDef()
...
dtWideHMemo:
  // Here was ftOraClob, but then will be created TMemoField,
  // which does not know anything about Unicode. So, I have
  // changed to ftFmtMemo. But probably may be problems ...
  ADestFieldType := ftWideMemo;

的确,可能是问题

解决方案是添加一个映射规则,将dtWideHMemo转换为dtMemo
之后,读写 CLOB .AsString 工作正常。

在 Embarcadero 质量门户中报告为 RSP-19600


为了完整起见:因为我的另一个答案中提到的映射不再有效,您必须使用 .Value 而不是 .AsString.

来更改对参数的访问