這是本專題的續集,沒讀過第一部的看這裏:
http://bbs.2ccc.com/topic.asp?topicid=548153
之因此要搞第二部是由於第一部跟貼太多,讀起來不方便,浪費你們的時間。
今天我們聊的主題是:Delphi的DataSnap實質分析
先說DataSnap中文應該翻譯成什麼,我我的的譯法是:數據快照。DataSnap是後來名字,原來叫MIDAS, Multi-tier Distributed Application Services Suite( 多 層 分 布 式 應 用 程 序 服 務 包) 的 縮 寫。你們不要被這麼多介紹DataSnap的資料弄暈了,其實原理很是簡單。
要把DataSnap搞明白,必須先把客戶端的TClientDataset控件搞明白,不會,找度娘。下面簡稱CDS。
CDS有兩個OleVarient屬性,一個叫Data,一個叫Delta。Delphi的多層框架全靠這哥倆。Data用於客戶端從服務器端獲取數據,Delta用於客戶端將修改的數據保存到服務器端。
那麼,這就簡單了,服務器端只要能實現輸出Data,接收Delta,三層應用就搞起來了。服務器端這項工做安排給誰呢?TDatasetProvider,下面簡稱DP。
DP有一個Data屬性,也是OleVariant類型,還有一個ApplyUpdate方法,接受Delta做爲輸入參數。html
明白了這個道理,咱們徹底能夠拋開Delphi那個複雜的DataSnap不理,本身來構建簡單可靠並且高效的多層框架。
服務器端天然用mORMot來稿,用THttpApiServer+DP,充分發揮http.sys的威力,站在巨人的肩膀上,呼風喚雨。
客戶端咱們用Delphi10.2.3來作,支持PC/Android/iOS, 用CDS+TNetHTTPRequest來作。TNetHTTPRequest爲XE8新增控件,使用操做系統內置http與https,不須要indy與openssl。追求極致的還能夠用更底層的THTTPClient控件。
不考慮手機的,繼續用Delphi7作PC應用的鐵粉,用CDS+THttpClientSocket(mORMot自帶)。服務器
Data與Delta都是Variant,沒法在網絡上傳輸,咱們須要這倆變成字符串,先來兩個函數:
{$IFNDEF UNICODE}
type
RawByteString = AnsiString;
{$ENDIF}
function VariantArrayToString(const V: OleVariant): RawByteString;
var
P: Pointer;
Size: Integer;
begin
Result := '';
if VarIsArray(V) and (VarType(V) and varTypeMask = varByte) then begin
Size := VarArrayHighBound(V, 1) - VarArrayLowBound(V, 1) + 1;
if Size > 0 then begin
SetLength(Result, Size);
P := VarArrayLock(V);
try
Move(P^, Result[1], Size);
finally
VarArrayUnlock(V);
end;
end;
end;
end;
function StringToVariantArray(const S: RawByteString): OleVariant;
var
P: Pointer;
begin
Result := NULL;
if Length(S) > 0 then begin
Result := VarArrayCreate([0, Length(S) - 1], varByte);
P := VarArrayLock(Result);
try
Move(S[1], P^, Length(S));
finally
VarArrayUnlock(Result);
end;
end;
end;網絡
看明白了嗎?Data與Delta內部存儲的都是字節流。變成字節流之後有兩種傳送方式,一種是以Stream方式,ContentType設置成application/octet-stream;另外一種將字節流base64編碼成純文本。大數據流能夠加入壓縮/減壓機制,保密數據能夠加入加密/解密機制。app
base64編碼與解碼,Delphi自帶,單元名爲EncdDecd。裏面有EncodeString與DecodeString兩個函數。
爲了讓你們把原理搞懂,咱們先拋開網絡傳輸層,將CDS與DP放在一個屋子裏讓他倆親熱一把。框架
unit DP2CDSMain;ide
interface函數
uses
Forms, DBClient, DB, Provider, ADODB, Controls, Grids, DBGrids, ComCtrls,
Classes, StdCtrls;大數據
type
TForm2 = class(TForm)
ServerData: TADODataSet;
Button1: TButton;
ClientData: TClientDataSet;
ServerDataSetProvider: TDataSetProvider;
PageControl1: TPageControl;
TabSheet2: TTabSheet;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;ui
var
Form2: TForm2;編碼
implementation
uses SysUtils, Variants, EncdDecd;
{$R *.dfm}
{$IFNDEF UNICODE}
type
RawByteString = AnsiString;
{$ENDIF}
function VariantArrayToString(const V: OleVariant): RawByteString;
var
P: Pointer;
Size: Integer;
begin
Result := '';
if VarIsArray(V) and (VarType(V) and varTypeMask = varByte) then begin
Size := VarArrayHighBound(V, 1) - VarArrayLowBound(V, 1) + 1;
if Size > 0 then begin
SetLength(Result, Size);
P := VarArrayLock(V);
try
Move(P^, Result[1], Size);
finally
VarArrayUnlock(V);
end;
end;
end;
end;
function StringToVariantArray(const S: RawByteString): OleVariant;
var
P: Pointer;
begin
Result := NULL;
if Length(S) > 0 then begin
Result := VarArrayCreate([0, Length(S) - 1], varByte);
P := VarArrayLock(Result);
try
Move(S[1], P^, Length(S));
finally
VarArrayUnlock(Result);
end;
end;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
vDataIn, vDataOut: OleVariant;
cDataIn, cDataOut: RawByteString;
begin
ClientData.Close;
vDataIn := ServerDataSetProvider.Data;
cDataIn := VariantArrayToString(vDataIn);
//模擬網絡傳送
cDataOut:=cDataIn;
vDataOut := StringToVariantArray(cDataOut);
ClientData.Data := vDataOut;
end;
end.
我這裏轉載了一篇文章細說CDS的用法:
https://www.cnblogs.com/c5soft/p/9121775.html
沒仔細看過手機版的CDS,不知道是否是徹底實現了PC版的功能,用過的朋友多發帖。
說點題外話,CDS鼠標右鍵菜單有一項「Assign Local Data...」,能夠將相同學體上的任何TDataset的數據複製到CDS中,如何實現的呢?我猜測就是用到了DP, 應該是這樣寫的:var DP:TDatasetProvider;begin DP:=TDatasetProvider.Create; DP.Dataset=ADODataset1; ClientDataset1.Data:=DP.Data; DP.Freeend;