跳到主要內容

發表文章

目前顯示的是 一月, 2014的文章

REST Server 取得 Remote IP ( Client IP ) 的方法

有關這個問題,解法如下
procedure TWebModule.DSServerConnect( DSConnectEventObject: TDSConnectEventObject); var _Session: TDSSession; begin try if Assigned(DSConnectEventObject.ChannelInfo) then begin _Session := TDSSessionManager.GetThreadSession; if Assigned(_Session) then begin if _Session.GetData('RemoteAddr') = '' then _Session.PutData('RemoteAddr', DSConnectEventObject.ChannelInfo.Info); end; end; except end; end;
今天測試大概是可以解決:
1. Request.RemoteAddr 不定時會存取失敗
2. REST 方法呼叫時,DSConnectEventObject.ChannelInfo 和 TDSSessionManager.GetThreadSession 必會存取失敗的問題

可能有人會問,如果使用 REST 方法時又想記錄 IP 的時候該怎麼處理?
目前原則上就只能避開使用 TDSSessionManage 。
至於什麼時候會修正這個 Bug ?
 嗯.
.
.
.
……我想應該是在不久的將來吧!

See also:
QC#121931 Request.RemoteAddr may causes Access Violation in TDSServer.OnConnect event
(Request not access in DSServer.OnConnect Event)
QC#121930 TDSServer can not get the Client IP via Request.RemoteAddr in its OnConnect event
(DSServer can no…

決定 DataSnap Sevice 連結資料庫的方式

既然要把 Database 連接元件抽離,就免不了還要重新決定哪一種連線方法。

當然如果已經有喜好的連接元件,也是可以直接使用,並不是說 DataSnap Service 就必須採用特定元件。

Delphi / C++ Builder 提供了以下元件:
BDE (Borland Database Engine)
經典中的經典,強大的 BDE Administrator 可以說是老 Delphi 開發者美好回憶。雖然最新版的 XE 已經把 BDE 排除在安裝包中,但對 BDE 有執念的開發者,還是會想方設法地把它找回來。

*ID: 29997, BDE Installer for RAD Studio, Delphi, C++Builder XE7

只是 BDE 在停止維護後, Unicode 的支援計畫也隨之停止,對於這讓人又愛又恨的元件,還是讓它還給歷史,擦擦眼淚,來看下一組元件。


ADO(ActiveX Data Objects) Express / dbGO
Borland 重新包裝 ADO 後,所做出的超完美元件組。在經歷的十餘年後,才有了可以和它相提併論的 FireDAC 。在這段期間,能走出 BDE 傷痛的開發者,絕大多數都選擇了 ADO ,它的穩定性及 SQL Server 完美搭配,再也沒有元件能出其右。

dbExpress
這個元件組很有戲,當時的開發市場情形簡述如下:

1. Borland 為了擺脫微軟的陰影。
2. 因應 BDE 停止開發後所帶來的衝擊。
3. Borland 高層決定推廣 DataSnap 技術。

於是 dbExpress 就在這個期待下誕生了,當時 dbExpress 的教學如雨後春筍般的不斷冒出。

只是, dbExpress 面臨了以下的問題:

1. 和 BDE、ADO 的開發方式有很大的矛盾。
2. dbExpress 的 Driver 完成度不足。

dbExpress 先天就是以搭配 DataSnap 為前提的元件,所以它的設定非常的簡單,而且和 BDE、ADO 相比,只有它們一半的功能,另一半則由 TDataSetProvider 及 TClientDataSet 來實現,這就和原本使用 BDE 和 ADO 的開發者的使用習慣完全不同,而所有關於 dbExpress 的書,對於 DataSnap 也沒有多所著墨,一…

Session funtion programming in PostgreSQL's PL/pgSQL

In SQL Server, we can to used:

DECLARE @Variable Type
SELECT * FROM TableName WHERE KeyField = @Variable

like something...

But, PostgreSQL not support SESSION VARIABLE.

However, In PostgreSQL 9.x, it add the statement in PL/pgSQL.

1. DO statement

Example code:
DO $$DECLARE myvar integer;BEGINSELECT5INTO myvar;DROPTABLEIFEXISTS tmp_table;CREATETABLE tmp_table ASSELECT*FROM yourtable WHERE id = myvar;END$$;SELECT*FROM tmp_table;

2. CREATE OR REPLACE FUNCTION

Example code:
CREATEOR REPLACE FUNCTION somefuncname() RETURNS int LANGUAGE plpgsql AS$$DECLARE one int; two int;BEGIN one :=1; two :=2;RETURN one + two;END$$;SELECT somefuncname();

These solution can solve need execute PL/pgSQL in session function.

The Sequelae(後遺症) is remain temp tempFunction / tempTable in PostgreSQL database, when the executed PL/pgSQL command.

See also:
How to declare local variables in postgresql?
PostgreSQL Documentation: SQL-DO
How to declare a variable in a PostgreSQL query

葡萄牙人寫的 Master / Detail for ClientDataSet 教學

雖然完全看不懂葡萄牙文,但英文的程式仍具有很大的幫助。

在 DataSetProvider.Options 設定 段落可以說是全篇的精華啊!

PASSO-A-PASSO COMO CRIAR UM RELACIONAMENTO MASTER/DETAIL USANDO CLIENTDATASET


主要設定為 True 的 Options 項目有:(說明參閱)
poCascadeDeletes: Tells the server to delete detail records automatically when master table records are deleted. To use this option, the provider must represent the master of a master/detail relationship and the database must support cascaded deletes as part of its referential integrity settings.

poCascadeUpdates: Tells the server to automatically update detail records when master table key values are changed. To use this option, the provider must represent the master of a master/detail relationship and the database must support cascaded updates as part of its referential integrity settings. 

poAutoRefresh: Refreshes the client dataset with current record values whenever it applies updates.

poPropogateChanges: Changes made in a BeforeUpdateRecord or AfterUpdateRecord event handler are sent back to the clien…

DBCommon.pas 雖然有 BUG ,但裡頭值得研究

此單元可以分析SQL語句,提供對於SQL 語句的TOKEN方式的解析。提供TExprParser,分析了類。
SQL Parser in Delphi for SQL Server

很開心的,我遇到這個問題……
DBCommon.GetIndexForOrderBy Get ERROR
List Index Out of Bounds (-1)

簡單的說就是:
如果 Order 後面接了一些內容 (EX: LIMIT),而該 SQL 語句也沒有回傳值(RecordCount = 0)時,就會發生錯誤。

仿間的解決方法 Can I recompile the .PAS files used by the Delphi IDE?

2014/01/20 更新
在這篇文章中,有個出乎意料的解法:
dbExpress + InterBase and ORDER BY
Datasnap.Provider.TProviderOptions

DataSetProvider.Options.poRetainServerOrder := True;
這樣就能夠解決 GetIndexForOrderBy 在特殊情況下會出錯的問題。

只是為什麼要這樣設定?
EMBT 沒說清楚,論壇上也沒講明白。

2014/01/21 更新
針對 poRetainServerOrder 設定項, Devart 論壇的回覆是這樣的:
This behavior is due to the fact that, when setting the poRetainServerOrder option to True, sorting on the client side doesn't occur, the sorting returned by the server is used.

那麼,這個錯誤真的算是 "Bug" 嗎?
按照 2004 年的 Borland 論壇,某位 Delphi 狂熱者是這麼說的:
Therefore there is no bug, it is as designed, and there are two documented
ways of dealing with the situation.

是 Bug 還是 Feature?就交給在座前的各位看倌評判囉…

Trim 全形空白 Delphi & C#

C#:
using System; namespace SampleApplication { static class Program { ///<summary> /// Trim (char)12288. Power by EdenW. ///</summary> public static string TrimEx(string S) { int I = 0; int L = S.Length - 1; while ((I <= L) && ((S[I]==' ') || (S[I]==' '))) I++; if (I > L) return ""; else { while ((S[L]==' ') || (S[L]<=' ')) L--; return S.Substring(I, L - I + 1); } } /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { //Console.WriteLine("Hello world!"); Console.WriteLine(TrimEx(" Hello world! ")); } } }

Delphi
///<summary> /// Trim (char…