2012-06-27 发表评论
原始帖子有一些有用的代码,但并没有真正说明如何在来自客户端应用程序的一个请求中从 DataSnap 服务器返回多个数据集。要查看如何执行此操作的示例,请查看页面底部标记为正确的答案。
2011-08-31 评论
多亏了 Gunny,我又看了一遍。令人烦恼的问题是我自己的错误,现在已修复。通过在每个数据库查询之间创建/销毁组件,我可以在单个客户端到服务器请求中在 DataSnap 服务器上执行多个 SQL 语句。TSQLQuery
当我尝试解决一个众所周知的问题时,我在存储过程中留下了一行调试代码,该问题阻止您output
在调用后访问参数TSQLStoredProc.Open
(http://qc.embarcadero.com/wc/qcmain.aspx? d=90211 )。
因此,即使我的问题解决了,原来的问题仍然存在——您不能调用该Open
方法来提取数据然后访问output
参数,并且您不能访问从单个存储过程返回的多个数据集。
再次感谢甘尼,您的建议。
原帖
我试图在一个请求中从 DataSnap 服务器返回两个不同的数据集。两者都来自同一个数据库。一种是单字段/单记录值,另一种是多字段/多记录数据集。
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;
这行不通。即使我可以看到两个单独的请求都命中了数据库,但我无法访问第二个结果集。当我尝试时,(再次)返回第一个结果集而不是第二个结果集。
在我创建/销毁查询组件(每个客户端到服务器请求)之前,所有后续客户端到服务器请求都将返回第一个数据集。非常令人沮丧。创建/销毁查询组件解决了这个问题,但现在我在一个客户端执行多个查询到服务器请求,问题又回来了——即使执行新查询,也会返回第一个数据集。
我尝试了几种方法:
ONE:为第一个请求动态创建TSQLQuery
组件,拉取 db 值,销毁TSQLQuery
,创建一个新的TSQLQuery
并拉取第二个数据集。那没有帮助。我可以使用SQL Server Profiler并观察两个命令都命中数据库,但第一个结果集显示为两个查询的数据集。
二:做与#1相同,但使用TSQLStoredProcedure
而不是TSQLQuery
。结果是一样的。
三:使用 aTSQLStoredProcedure
并从同一个存储过程中返回两个数据集,如下所示:
create procedure sp_test_two_datasets
as
select 'dataset1' as [firstdataset]
select * from sometable -- 2nd dataset
go
自从TSQLStoredProcedure
有了NextRecordSet
,我曾希望访问这两个数据集,但没有乐趣。当我打电话时NextRecordSet
,它会返回nil
。
四:在一次调用中返回两个值以TSQLStoredProcedure
使用数据集和output
参数:
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 并且有一个名为 的输入/输出参数value
。output
当还返回数据集时,我无法访问该参数。这是一个已知的错误(多年来):http: //qc.embarcadero.com/wc/qcmain.aspx ?d=90211
结论:
由于 DataSnap 服务器TSQLConnection
与所有连接的客户端共享其主服务器,并且因为TSQLQuery
(or TSQLStoredProc
) 和TClientDataSet
组件都是随每个请求创建/释放的,所以剩下的唯一可能是保留先前的数据集并将其返回到TSQLQuery
andTSQLStoredProc
组件是TSQLConnection
组件。我尝试TSQLConnection.CloseDataSets
在关闭和释放TSQLQuery
(或TStoredProc
)组件之前调用,但这也无济于事。
也许仔细研究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
执行的组件?
任何帮助将不胜感激。正如你所知道的,这让我发疯了!
谢谢,詹姆斯