0

2012-06-27コメント

元の投稿にはいくつかの便利なコードがありますが、クライアントアプリからの1回のリクエストでDataSnapサーバーから複数のデータセットを返す方法を実際には示していません。その方法の例を見るには、ページの一番下にある「正解」とマークされた回答を見てください。


2011-08-31コメント

ガニーのおかげで、私はすべてをもう一度見ました。厄介な問題は私自身のバグでしたが、現在は修正されています。各データベースクエリの間にコンポーネントを作成/破棄することで、単一のクライアントからサーバーへの要求内のDataSnapサーバーで複数のSQLステートメントを実行できますTSQLQuery

outputhttp://qc.embarcadero.com/wc/qcmain.aspx _ d = 90211)。TSQLStoredProc.Open

したがって、私の問題は解決されましたが、元の問題は残っています。メソッドを呼び出してOpenデータをプルしてからoutputパラメータにアクセスすることはできません。また、単一のストアドプロシージャから返された複数のデータセットにアクセスすることもできません。

ガニー、あなたの提案にもう一度感謝します。


元の投稿

1回のリクエストでDataSnapサーバーから2つの異なるデータセットを返そうとしています。どちらも同じデータベースからのものです。1つは単一フィールド/単一レコード値であり、もう1つはマルチフィールド/マルチレコードデータセットです。

DataSnapサーバーには次の方法があります。

function TDSSvrMethods.GetData(const SQL: string; var Params: OleVariant; var Key: string): OleVariant;
var qry: TSQLQuery; cds: TClientDataSet;
begin
  // create TSQLQuery & TClientDataSet
  // Link the two components via cds.SetProvider(qry);
  // run first query, set 'Key' to the result <-- this works
  qry.Close;
  // run second query <-- I see this hit the database
  // return dataset via 'Result := cds.Data;'
  // destory TSQLQuery & TClientDataSet
end;

これは機能しません。両方の個別のリクエストがデータベースにヒットしていることはわかりますが、2番目の結果セットにアクセスできません。試してみると、2番目の結果セットではなく最初の結果セットが(再び)返されます。

クエリコンポーネントを作成/破棄する前に(各クライアントからサーバーへのリクエストで)、後続のすべてのクライアントからサーバーへのリクエストは最初のデータセットを返していました。非常にイライラします。クエリコンポーネントを作成/破棄するとその問題は修正されましたが、1つのクライアントからサーバーへのリクエストで複数のクエリを実行すると、問題が返されます。新しいクエリが実行された場合でも、最初のデータセットが返されます。

私はいくつかのアプローチを試しました:

ONETSQLQuery :最初のリクエストのコンポーネントを動的に作成し、db値をプルし、を破棄しTSQLQuery、新しいデータセットを作成してTSQLQuery、2番目のデータセットをプルします。それは役に立ちませんでした。SQL Server Profilerを使用して、両方のコマンドがデータベースにヒットすることを確認できますが、最初の結果セットが両方のクエリのデータセットとして表示されます。

TWO:#1と同じことを行いますが、TSQLStoredProcedureの代わりに使用しTSQLQueryます。結果は同じです。

3:を使用してTSQLStoredProcedure、次のように同じストアドプロシージャ内から両方のデータセットを返します。

create procedure sp_test_two_datasets
as
  select 'dataset1' as [firstdataset]
  select * from sometable -- 2nd dataset
go

TSQLStoredProcedureを持っているのでNextRecordSet、私は両方のデータセットにアクセスすることを望んでいましたが、喜びはありませんでした。を呼び出すとNextRecordSet、が返されますnil

4TSQLStoredProcedure :データセットとoutputパラメーターを使用する1回の呼び出しで2つの値を返します。

create procedure sp_another_test
  @singlevalue varchar(255) output
as
  select * from sometable
go

Delphiのコードは次のようになります。

var sp: TSQLStoredProc; cds: TClientDataSet;
...
cds.SetProvider(sp);
...
sp.CommandText := 'sp_another_test :value output';
sp.Params.ParamByName('value').Value := Key; // in/out method parameter from above
sp.Open;
Key := sp.Params.ParamByName('value').Value; // single string value
Result := cds.Data; // dataset
...

sp.Paramsを調べたところ、。という名前のin/outパラメーターが1つありますvalueoutputデータセットも返されるときにパラメータにアクセスできません。これは既知のバグです(長年にわたって):http://qc.embarcadero.com/wc/qcmain.aspx?d = 90211

結論:

DataSnapサーバーはTSQLConnection接続しているすべてのクライアントとメインを共有しており、TSQLQuery(またはTSQLStoredProc)とTClientDataSetコンポーネントはすべてリクエストごとに作成/解放されるため、前のデータセットを保持してTSQLQueryTSQLStoredProcコンポーネントはTSQLConnectionコンポーネントです。(または)コンポーネントTSQLConnection.CloseDataSetsを閉じて解放する前に呼び出してみましたが、それも役に立ちませんでした。TSQLQueryTStoredProc

おそらく、よく見るTSQLConnectionと役立つでしょう。.dfmファイル内での表示は次のとおりです。

object sqlcon: TSQLConnection
  DriverName = 'MSSQL'
  GetDriverFunc = 'getSQLDriverMSSQL'
  LibraryName = 'dbxmss.dll'
  LoginPrompt = False
  Params.Strings = (
    'SchemaOverride=%.dbo'
    'DriverUnit=DBXMSSQL'

      'DriverPackageLoader=TDBXDynalinkDriverLoader,DBXCommonDriver150.' +
      'bpl'

      'DriverAssemblyLoader=Borland.Data.TDBXDynalinkDriverLoader,Borla' +
      'nd.Data.DbxCommonDriver,Version=15.0.0.0,Culture=neutral,PublicK' +
      'eyToken=91d62ebb5b0d1b1b'

      'MetaDataPackageLoader=TDBXMsSqlMetaDataCommandFactory,DbxMSSQLDr' +
      'iver150.bpl'

      'MetaDataAssemblyLoader=Borland.Data.TDBXMsSqlMetaDataCommandFact' +
      'ory,Borland.Data.DbxMSSQLDriver,Version=15.0.0.0,Culture=neutral' +
      ',PublicKeyToken=91d62ebb5b0d1b1b'
    'GetDriverFunc=getSQLDriverMSSQL'
    'LibraryName=dbxmss.dll'
    'VendorLib=sqlncli10.dll'
    'HostName=localhost'
    'Database=Database Name'
    'MaxBlobSize=-1'
    'LocaleCode=0000'
    'IsolationLevel=ReadCommitted'
    'OSAuthentication=False'
    'PrepareSQL=True'
    'User_Name=user'
    'Password=password'
    'BlobSize=-1'
    'ErrorResourceFile='
    'OS Authentication=False'
    'Prepare SQL=False')
  VendorLib = 'sqlncli10.dll'
  Left = 352
  Top = 120
end

また、実行時に、DBXドライバー用の.INIファイルを展開する必要がないようにいくつかのことを行います。まず、自分のINIレスドライバーを登録できるユニット:

unit DBXRegDB;

interface

implementation

uses
  DBXCommon, DBXDynalinkNative;

type
  TDBXInternalDriver = class(TDBXDynalinkDriverNative)
  public
    constructor Create(DriverDef: TDBXDriverDef); override;
  end;

  TDBXInternalProperties = class(TDBXProperties)
  private
  public
    constructor Create(DBXContext: TDBXContext); override;
  end;

{ TDBXInternalDriver }

constructor TDBXInternalDriver.Create(DriverDef: TDBXDriverDef);
begin
  inherited Create(DriverDef, TDBXDynalinkDriverLoader);
  InitDriverProperties(TDBXInternalProperties.Create(DriverDef.FDBXContext));
end;

{ TDBXInternalProperties }

constructor TDBXInternalProperties.Create(DBXContext: TDBXContext);
begin
  inherited Create(DBXContext);

  Values[TDBXPropertyNames.SchemaOverride]         :=       '%.dbo';
  Values[TDBXPropertyNames.DriverUnit]             :=       'DBXMSSQL';
  Values[TDBXPropertyNames.DriverPackageLoader]    :=       'TDBXDynalinkDriverLoader,DBXCommonDriver150.bpl';
  Values[TDBXPropertyNames.DriverAssemblyLoader]   :=       'Borland.Data.TDBXDynalinkDriverLoader,Borland.Data.DbxCommonDriver,Version=15.0.0.0,Culture=neutral,PublicKeyToken=91d62ebb5b0d1b1b';
  Values[TDBXPropertyNames.MetaDataPackageLoader]  :=       'TDBXMsSqlMetaDataCommandFactory,DbxMSSQLDriver150.bpl';
  Values[TDBXPropertyNames.MetaDataAssemblyLoader] :=       'Borland.Data.TDBXMsSqlMetaDataCommandFactory,Borland.Data.DbxMSSQLDriver,Version=15.0.0.0,Culture=neutral,PublicKeyToken=91d62ebb5b0d1b1b';
  Values[TDBXPropertyNames.GetDriverFunc]          :=       'getSQLDriverMSSQL';
  Values[TDBXPropertyNames.LibraryName]            :=       'dbxmss.dll';
  Values[TDBXPropertyNames.VendorLib]              :=       'sqlncli10.dll';
  Values[TDBXPropertyNames.HostName]               :=       'ServerName';
  Values[TDBXPropertyNames.Database]               :=       'Database Name';
  Values[TDBXPropertyNames.MaxBlobSize]            :=       '-1';
  Values['LocaleCode']                             :=       '0000';
  Values[TDBXPropertyNames.IsolationLevel]         :=       'ReadCommitted';
  Values['OSAuthentication']                       :=       'False';
  Values['PrepareSQL']                             :=       'True';
  Values[TDBXPropertyNames.UserName]               :=       'user';
  Values[TDBXPropertyNames.Password]               :=       'password';
  Values['BlobSize']                               :=       '-1';
  Values[TDBXPropertyNames.ErrorResourceFile]      :=       '';
  Values['OS Authentication']                      :=       'False';
  Values['Prepare SQL']                            :=       'True';
  Values[TDBXPropertyNames.ConnectTimeout]         :=       '30';

  // Not adding connection pooling to the default driver parameters
end;

var
  InternalConnectionFactory: TDBXMemoryConnectionFactory;

initialization
  TDBXDriverRegistry.RegisterDriverClass('MSSQL_NoINI', TDBXInternalDriver);
  InternalConnectionFactory := TDBXMemoryConnectionFactory.Create;
  InternalConnectionFactory.Open;
  TDBXConnectionFactory.SetConnectionFactory(InternalConnectionFactory);

end.

上記のメソッドはプロジェクト(.dprファイル)に含まれており、ドライバーを自己登録します。次の方法では、これを利用してTSQLConnection実行時(DataSnapサーバーの起動時)に(sqlcon)をセットアップします。

procedure SetupConnection(const hostname, port, dbname, username, password, maxcon: string);
begin
  if sqlcon.Connected then
    Exit;

  // Our custom driver -- does not use DBXDrivers.ini
  sqlcon.Params.Clear;
  sqlcon.DriverName := 'MSSQL_NoINI';
  sqlcon.VendorLib := sqlcon.Params.Values[TDBXPropertyNames.VendorLib];
  sqlcon.LibraryName := sqlcon.Params.Values[TDBXPropertyNames.LibraryName];
  sqlcon.GetDriverFunc := sqlcon.Params.Values[TDBXPropertyNames.GetDriverFunc];

  sqlcon.Params.Values[TDBXPropertyNames.HostName]           := hostname;
  sqlcon.Params.Values[TDBXPropertyNames.Port]               := port;
  sqlcon.Params.Values[TDBXPropertyNames.Database]           := dbname;
  sqlcon.Params.Values[TDBXPropertyNames.UserName]           := username;
  sqlcon.Params.Values[TDBXPropertyNames.Password]           := password;
  sqlcon.Params.Values[TDBXPropertyNames.DelegateConnection] := DBXPool.sDriverName;
  sqlcon.Params.Values[DBXPool.sDriverName + '.' + TDBXPoolPropertyNames.MaxConnections]    := maxcon;
  sqlcon.Params.Values[DBXPool.sDriverName + '.' + TDBXPoolPropertyNames.MinConnections]    := '1';
  sqlcon.Params.Values[DBXPool.sDriverName + '.' + TDBXPoolPropertyNames.ConnectTimeout]    := '1000';
  sqlcon.Params.Values[DBXPool.sDriverName + '.' + 'DriverUnit']                            := DBXPool.sDriverName;
  sqlcon.Params.Values[DBXPool.sDriverName + '.' + 'DelegateDriver']                        := 'True';
  sqlcon.Params.Values[DBXPool.sDriverName + '.' + 'DriverName']                            := DBXPool.sDriverName;
end;

これらの設定のいずれかが、コンポーネントを台無しにして、TSQLConnectionデータセットをキャッシュし、最新のTSQLQueryコンポーネントが実行したものの代わりにそれらを返すようにしている可能性がありますか?

どんな助けでも大歓迎です。お分かりのように、これは私を夢中にさせています!

ありがとう、ジェームズ

4

2 に答える 2

1

CDS も閉じるとどうなりますか?

function TDSSvrMethods.GetData(const SQL: string; var Params: OleVariant; var Key: string): OleVariant; 
var qry: TSQLQuery; cds: TClientDataSet; 
begin 
  // create TSQLQuery & TClientDataSet
  // Link the two components via cds.SetProvider(qry);
  // run first query, set 'Key' to the result <-- this works
  qry.Close;
  cds.Close;
  // run second query <-- I see this hit the database
  cds.Open
  // return dataset via 'Result := cds.Data;'
  // destory TSQLQuery & TClientDataSet end;

于 2011-08-27T21:33:19.673 に答える
0

前述のように、2つのDBX Frameworkのバグを回避しようとして、TSQLConnectionが後続のデータリクエストのために前のデータセットを返しているように見えるバグを導入しました。バグを修正したら、2つのDBXフレームワークのバグを回避する必要がありました(フレームワークを自分で修正/再コンパイルできないため)。

ONE :メソッドを呼び出してパラメータOpenにアクセスすることはできません。output

TWO:単一のストアドプロシージャから返された複数のデータセットにアクセスすることはできません。

回避策:DataSnapサーバーからデータベースへの2つのクエリを実行してから、個々のデータセットを処理/パッケージ化して、クライアントに送り返します(1つの応答で)。


2012-06-27コメント

このスレッドは複数のビューを取得するため、DataSnapサーバーからの単一の応答に複数のデータセットをパッケージ化する方法を説明しようと思いました。

DataSnapOleVariantはクライアントアプリにを返すことができます。OleVariantの配列であるを作成するのは簡単ですOleVariantTClientDataSet.DataプロパティはであるためOleVariant、クライアントに返すデータセットの配列を作成できます。この例では、5つのデータセットが返されます。これらのメソッドがDataSnapサーバーに存在すると仮定します。

function TServerMethods1.GetData(SQL: string): OleVariant;
var cds: TClientDataSet;
begin
  cds := TClientDataSet.Create(nil);
  try

    { setup 'cds' to connect to database }
    { pull data }

    Result := cds.Data;
  finally
    FreeAndNil(cds);
  end;
end;

function TServerMethods1.GetMultipleDataSets: OleVariant;
begin
  Result := VarArrayCreate([0, 4], varVariant);
  Result[0] := GetData('select * from Table1');
  Result[1] := GetData('select * from Table2');
  Result[2] := GetData('select * from Table3');
  Result[3] := GetData('select * from Table4');
  Result[4] := GetData('select * from Table5');
end;

TClientDataSetフォームに5つのコンポーネントを配置し、それらのDataプロパティをからの要素に割り当てることで、クライアント側でデータを割り当てることができますOleVariant

procedure X;
var DataArray: OleVariant;
begin
  try
    with ProxyMethods.TServerMethods1.Create(SQLConnection1.DBXConnection, True) do
    try
      DataArray := GetMultipleDataSets;
    finally
      Free;
    end;

    ClientDataSet1.Data := DataArray[0];
    ClientDataSet2.Data := DataArray[1];
    ClientDataSet3.Data := DataArray[2];
    ClientDataSet4.Data := DataArray[3];
    ClientDataSet5.Data := DataArray[4];
  finally
    VarClear(DataArray);
  end;
end;

(この例はテストせずに入力しました。実際のコードには、バリアント配列の境界チェックやその他の動的要素が含まれています。)

于 2011-08-29T22:42:53.743 に答える