Delphi DataSnap 核心架构与开发指南
Delphi DataSnap 是 Embarcadero 提供的多层应用开发框架,起源于 MIDAS 技术,历经 DBX 演变至现代版本,支持构建高性能的三层或多层分布式应用 。其核心优势在于简化了服务端与客户端的通信,支持多种协议(TCP/IP, HTTP/HTTPS),并能够轻松实现 RESTful 服务 。
- 技术演进与协议对比
DataSnap 从早期的 scktsrv.exe 中间件发展为完全集成在 Delphi IDE 中的框架,不再依赖外部进程,且深度整合了 FireDAC 和 REST 技术 。
| 特性维度 | 传统 DataSnap (DBX) | 现代 DataSnap (REST/HTTP) | 说明 |
|---|---|---|---|
| 通信协议 | 专有 TCP/IP协议 (DBX) | HTTP/HTTPS (RESTful) | 现代版本更易于穿越防火墙和集成 Web 前端 |
| 数据格式 | 二进制 (dbxjson 等) | JSON, XML, PlainText | REST 模式默认使用 JSON 进行数据交换 |
| 服务端组件 | TDSServer, TDSTCPTransport |
TDSServer, TDSHTTPWebDispatcher |
HTTP 模式需加载 TDSHTTPWebDispatcher 处理 Web 请求 |
| 客户端连接 | TDSClientConnection |
TDSRestConnection / TRESTClient |
REST 客户端可利用标准 REST 组件或专用 DataSnap 代理 |
| 部署依赖 | 需特定 DBX 驱动 | 标准 Web 服务器环境 | 摆脱了对特定中间件进程的依赖 |
- 服务端构建流程 (Server Side)
创建 DataSnap 服务端通常采用 "DataSnap REST Application" 向导。其核心逻辑是通过 TCustomWebDispatcher (具体为 TDSHTTPWebDispatcher) 拦截 HTTP 请求,解析命令并调用远程方法 。
关键组件配置:
- TDSServer: 管理整个 DataSnap 会话和服务实例。
- TDSServerClass: 将具体的数据模块类暴露为远程服务。
- TDSHTTPWebDispatcher: 负责将 HTTP 请求分发给 DataSnap 引擎 。
- TDatasetProvider : 位于数据模块中,负责打包数据集 (
TFDQuery) 供客户端获取 。
服务端数据模块代码示例 (Delphi):
pascal
unit ServerModule;
interface
uses
System.SysUtils, System.Classes, DataSnap.DSProviderConnection,
Data.DBXCommon, Data.DBXClient, Data.DBXDataSnap, Data.DBXJSON, Data.DBXJSONReflect, Data.DBXTDR, Data.DBXLog, Data.DBXError,
Data.DBXReaders, Data.DBXWritable, Data.DBXFlatFile, Data.DBXClientConnection,
Data.DBXSocketConn, Data.DBXProxy, Data.DBXMsgResp, Data.DBXMetaData,
Data.DBXCommonTypes, Data.DBXFilters, Data.DBXDataReader,
Data.DBXDynArray, Data.DBXJSONTools, Data.DBXJSONContent, Data.DBXJsonObject, Data.DBXJSONBridge, Data.DBXIPC, 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.FB, FireDAC.Phys.FBDef, FireDAC.VCLUI.Wait, FireDAC.Comp.Client, FireDAC.Comp.DataSet, Data.DB,
Data.SqlExpr, Data.Provider, Datasnap.DSServer, Datasnap.DSHTTPCommon,
Datasnap.DSHTTPWebBroker, Datasnap.DSHTTP;
type
TServerMethods1 = class(TDataModule)
FDConnection1: TFDConnection;
FDQuery1: TFDQuery;
DataSetProvider1: TDataSetProvider;
procedure DataModuleCreate(Sender: TObject);
private
{ Private declarations } public
{ Public declarations }
function GetData: TDataSet; end;
implementation
{$R *.dfm}
procedure TServerMethods1.DataModuleCreate(Sender: TObject);
begin
// 初始化数据库连接,实际项目中建议从配置文件读取
FDConnection1.DriverName := 'FB';
FDConnection1.Params.Add('Database=MyDatabase.fdb');
FDConnection1.Params.Add('User_Name=sysdba');
FDConnection1.Params.Add('Password=masterkey');
end;
// 核心业务方法:返回数据集供客户端调用
function TServerMethods1.GetData: TDataSet;
begin
FDQuery1.Close;
FDQuery1.SQL.Text := 'SELECT * FROM CUSTOMERS';
FDQuery1.Open;
Result := FDQuery1; // DataSnap 自动处理数据集的序列化和传输
end;
end.
- 客户端连接与数据获取 (Client Side)
客户端可通过两种方式获取数据:传统的 TDSClientConnection (适用于同构 Delphi 环境) 或现代的 TDSRestConnection / TRESTClient (适用于跨平台或 Web 集成) 。
方式 A:使用 TDSRestConnection (推荐用于 REST 模式)
此方式利用 TSQLConnection 和 TDataSetProviderConnection 建立连接,支持只读和可更新数据集,并能处理更新冲突 。
pascal
unit ClientForm;
interface
uses
System.SysUtils, System.Classes, Vcl.Forms, Vcl.Controls, Vcl.StdCtrls,
Vcl.Grids, Vcl.DBGrids, Data.DB, Data.SqlExpr, Data.DBXClient, Data.DBXDataSnap, Data.DBXCommon, Data.DBXJSON, Data.DBXJSONTools,
Datasnap.DSClientRes, Datasnap.DSProxy, Datasnap.DSHTTPClient,
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.Comp.Client, FireDAC.Comp.DataSet, FireDAC.DatS,
FireDAC.DApt, FireDAC.DatS.Json;
type
TForm1 = class(TForm)
DSRestConnection1: TDSRestConnection;
SQLConnection1: TSQLConnection;
DSPClientConnection1: TDSPClientConnection; ClientDataSet1: TClientDataSet;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
ButtonFetch: TButton;
procedure ButtonFetchClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
// 配置 REST 连接参数
DSRestConnection1.Host := 'localhost';
DSRestConnection1.Port := 8080; // DataSnap 默认 HTTP 端口
DSRestConnection1.LoginPrompt := False;
// 若需认证,设置 UserName 和 Passwordend;
procedure TForm1.ButtonFetchClick(Sender: TObject);
begin
try
if not DSRestConnection1.Connected then
DSRestConnection1.Connect;
// 调用服务端远程方法 GetData
// 参数顺序需与服务端方法定义一致
ClientDataSet1.CommandText := 'GetData';
ClientDataSet1.Execute;
// 此时 DBGrid1 将显示从服务端获取的数据 except
on E: Exception do
ShowMessage('数据获取失败:' + E.Message);
end;
end;
end.
方式 B:使用 REST Client 直接解析 JSON
对于非 Delphi 客户端或需要更细粒度控制的场景,可直接使用 TRESTClient 配合 TRESTResponseDataSetAdapter 将 JSON 响应转换为数据集 。
pascal
// 伪代码逻辑示意:使用 TRESTResponseDataSetAdapter 处理 JSON 流
// var Adapter: TRESTResponseDataSetAdapter;
// Adapter := TRESTResponseDataSetAdapter.Create(nil);
// try
// Adapter.Dataset := MyLocalFDQuery;
// Adapter.FieldDefs := MyLocalFDQuery.FieldDefs;
// Adapter.RestResponse := RestResponse1; // 来自 TRESTClient 的响应
// Adapter.FillDataset; // 将 JSON 数据填充到本地数据集
// finally
// Adapter.Free;
// end;
- 关键机制与部署策略
- 数据更新与冲突处理 :DataSnap 支持客户端离线修改数据并通过
ApplyUpdates提交。服务端通过TDatasetProvider的OnUpdateError事件处理并发冲突,支持强制覆盖、取消更新或合并策略 。 - 过滤器应用 :可在客户端对
TClientDataSet应用过滤器,减少网络传输量;也可在服务端通过参数化查询实现服务端过滤 。 - 远程数据模块重用 :设计良好的 DataSnap 服务应将业务逻辑封装在独立的 DataModule 中,通过
TDSServerClass暴露,确保逻辑层的复用性和维护性 。 - 部署注意事项 :
- HTTP 模式 :只需部署生成的 EXE 文件,无需额外安装
scktsrv.exe或复杂的 DBX 驱动,极大简化了运维 。 - 线程安全:DataSnap 服务端默认在每个请求中创建数据模块实例(LifeCycle 设置为 Session 或 Invocation),需注意全局变量的线程安全问题 。
- 防火墙:使用 HTTP/HTTPS 协议(默认端口 8080 或 443)比传统 TCP 端口更容易通过企业防火墙 。
- HTTP 模式 :只需部署生成的 EXE 文件,无需额外安装