天天用Delphi,自己有了很多想法。寫代碼之余,有空閒時間就把一些東西整理成文檔。
Delphi中使用最多的大概是AdoExpress組件,這是Borland封裝了Microsoft的Ado的東東,使用頻率最多的TAdoDataSet對應了Ado原生的RecordSet,在功能上做了一些增強,但用法基本一致,用多了就感覺TAdoDataSet還有擴充和改造的地方。
由於代碼中使用了很多的TAdoDataSet控件,創建和釋放對象非常頻繁,而且每次創建後都要設置很多基本相同的屬性,頗為麻煩。於是想到可以實現一個記錄集池,每次當需要一個記錄集時,從這個池中得到一個空閒且符合要求的(只讀或可讀寫),用完了就被池回收,如果池中記錄集不夠,就自動生成新的記錄集對象。
首先要做的是改造TAdoDataSet,我寫了一個TManagedDataSet,繼承自TAdoDataSet,可以自己知道自己是被人使用還是空閒(通過IsUsed()),重寫了Free(),把本來釋放的動作改為僅是把自己設置為空閒,並清除狀態(Session)信息,並可以通過Source()返回一個指向自己的TDataSource對象。
有了這些基礎後,就可以很快的構建TDataSetPool類了,這個類僅是保存可用的TManagedDataSet對象,通過GetDataSet(WantType : TManagedDataSetType)返回一個空閒的數據集對象,如果池中沒有空閒的,就新建一個返回。TManagedDataSetType是枚舉類,標識只讀數據集和讀寫數據集(只讀數據集可通過優化CursorType和LockType來加快讀數據速度)。
下面的代碼是直接從我做的一個項目的源文件中Copy出來的,有些亂,僅做參考。
unit ManagedDataSet;
interface
uses AdoDb, CommonDm, SysUtils, DB, dbgrids, ComObj, classes, contnrs;
type
TManagedDataSetType = (ReadOnly, Editable); // 猅羭摸
TXlsExpAdapter = class
private
_sXlsCaption : string;
_sXlsFileName : string;
_bOverwriteExistFile : Boolean;
_asFIEldName : TStringList;
_asXlsTitle : TStringList;
_aDataType : TObjectList;
function GetDataType(const iniIndex : Integer) : TDataType;
function GetFIEldName(const iniIndex : Integer) : string;
function GetXlsTitle(const iniIndex : Integer) : string;
public
constructor Create();
destructor Destroy();
property XlsCaption : string read _sXlsCaption Write _sXlsCaption;
property XlsFileName : string read _sXlsFileName Write _sXlsFileName;
property OverWriteExistFile : Boolean read _bOverwriteExistFile Write _bOverwriteExistFile;
procedure AddField(const insFIEldName, insCaption : string; const intype : TDataType = ftUnKnown);
procedure GetInfoFromDBGrid(const ingrid : TDBGrid);
property DataType[const iniIndex : Integer] : TDataType read GetDataType;
property FieldName[const iniIndex : Integer] : string read GetFIEldName;
property XlsTitle[const iniIndex : Integer] : string read GetXlsTitle;
function Count() : Integer;
end;
TManagedDataSet = class(TAdoDataSet)
private
_source : TDataSource;
_type : TManagedDataSetType;
_bUsed : Boolean;
procedure SetDataSetType(const intype : TManagedDataSetType);
function GetDataSource() : TDataSource;
public
constructor Create(const intype : TManagedDataSetType = Editable);
destructor Destroy(); override;
procedure Use();
procedure Free(); reintroduce; // 灤護髅篎reeぃ穦睦龜ㄒ
property DataSetType : TManagedDataSetType read _type Write SetDataSetType;
property IsUsed : Boolean read _bUsed;
property Source : TDataSource read GetDataSource;
function ExportToXls(const inadapter : TXlsExpAdapter) : Boolean;
end;
implementation
function TXlsExpAdapter.Count() : Integer;
begin
Result := _asFIEldName.Count;
end;
function TXlsExpAdapter.GetXlsTitle(const iniIndex : Integer) : string;
begin
if (iniIndex >= 0) and (iniIndex <= _aDataType.Count-1) then
begin
Result := _asXlsTitle[iniIndex];
end;
end;
function TXlsExpAdapter.GetFIEldName(const iniIndex : Integer) : string;
begin
if (iniIndex >= 0) and (iniIndex <= _aDataType.Count-1) then
begin
Result := _asFIEldName[iniIndex];
end;
end;
function TXlsExpAdapter.GetDataType(const iniIndex : Integer) : TDataType;
begin
if (iniIndex >= 0) and (iniIndex <= _aDataType.Count-1) then
begin
Result := TDataType(_aDataType[iniIndex]);
end;
end;
procedure TXlsExpAdapter.GetInfoFromDBGrid(const ingrid : TDBGrid);
var
i, j : Integer;
dt : TDataType;
begin
for i := 0 to ingrid.Columns.Count-1 do
begin
if ingrid.Columns[i].Visible then
begin
dt := ftUnknown;
for j := 0 to ingrid.FIEldCount-1 do
begin
if ingrid.Columns[i].FieldName = ingrid.Fields[j].FIEldName then
begin
dt := ingrid.FIElds[j].DataType;
Break;
end;
end;
Self.AddField(ingrid.Columns[i].FIEldName, ingrid.Columns[i].Title.Caption, dt);
end;
end;
end;
procedure TXlsExpAdapter.AddField(const insFIEldName, insCaption : string; const intype : TDataType = ftUnKnown);
var
iIndex : Integer;
begin
iIndex := _asFieldName.IndexOf(insFIEldName);
if iIndex = -1 then
begin
_asFieldName.Add(insFIEldName);
_asXlsTitle.Add(insCaption);
_aDataType.Add(TObject(intype));
end
else begin
_asFieldName[iIndex] := insFIEldName;
_asXlsTitle[iIndex] := insCaption;
_aDataType[iIndex] := TObject(intype);
end;
end;
constructor TXlsExpAdapter.Create();
begin
_asFIEldName := TStringList.Create();
_asXlsTitle := TStringList.Create();
_aDataType := TObjectList.Create();
end;
destructor TXlsExpAdapter.Destroy();
begin
end;
function TManagedDataSet.ExportToXls(const inadapter : TXlsExpAdapter) : Boolean;
var
Excelobj : OleVariant;
i : Integer;
begin
Result := False;
if not Self.Active then
Exit;
try
excelobj := CreateOleObject('Excel.Application');
Excelobj.WorkBooks.Add;
except
Exit;
end;
if FileExists(inadapter.XlsFileName) and inadapter.OverWriteExistFile then
begin
DeleteFile(PChar(inadapter.XlsFileName));
end
else begin
Excelobj.Quit;
Exit;
end;
for i := 0 to inadapter.Count-1 do
begin
end;
end;
constructor TManagedDataSet.Create(const intype : TManagedDataSetType = Editable);
begin
inherited Create(nil);
Self.Connection := DmCommon.Cnn;
Self.CursorLocation := clUseClIEnt;
Self.Prepared := True;
Self.CacheSize := 1000;
if intype = ReadOnly then
begin
Self.CursorType := ctOpenForwardOnly;
Self.LockType := ltReadOnly;
end
else if intype = Editable then
begin
Self.CursorType := ctStatic;
Self.LockType := ltOptimistic;
end;
_type := intype;
_bUsed := False;
end;
destructor TManagedDataSet.Destroy();
begin
if Self.Active then
begin
Self.Close;
end;
if Assigned(_source) then
begin
FreeAndNil(_source);
end;
inherited Destroy();
end;
procedure TManagedDataSet.Use();
begin
if _bUsed then
begin
raise Exception.Create('Cannot get a used managed dataset !');
end;
_bUsed := True;
end;
procedure TManagedDataSet.Free();
begin
if Self.Active then
begin
Self.Close;
end;
Self.CommandText := '';
Self.Parameters.Clear; // 睲埃把計
Self.MasterFIElds := ''; // 睲埃琿
Self.DataSource := nil;
Self.ExecuteOptions := []; // 睲埃磅︽匡兜
_bUsed := False;
end;
procedure TManagedDataSet.SetDataSetType(const intype : TManagedDataSetType);
begin
if intype = _type then
Exit;
if intype = ReadOnly then
begin
Self.CursorType := ctOpenForwardOnly;
Self.LockType := ltReadOnly;
end
else if intype = Editable then
begin
Self.CursorType := ctStatic;
Self.LockType := ltOptimistic;
end;
end;
function TManagedDataSet.GetDataSource() : TDataSource;
begin
if not Assigned(_source) then
begin
_source := TDataSource.Create(nil);
_source.AutoEdit := False;
_source.DataSet := Self;
end;
Result := _source;
end;
end.
unit DataSetPool; // 癘魁棟GlobalVarい承セ摸Ы龜ㄒ跑秖
interface
uses ManagedDataSet, Contnrs, SysUtils, AdoDb, Db, CommonDm;
type
TDataSetPool = class
private
_ads : TObjectList;
function GetCount() : Integer;
public
constructor Create(const ini : Integer = 10);
destructor Destroy(); override;
property Count : Integer read GetCount;
function GetDataSet(const intype : TManagedDataSetType = Editable) : TManagedDataSet;
function GetAdoCommand() : TAdoCommand; // 度TAdoCommand睦パ秸ノ璽砫
end;
implementation
constructor TDataSetPool.Create(const ini : Integer = 10);
begin
_ads := TObjectList.Create;
end;
destructor TDataSetPool.Destroy();
begin
FreeAndNil(_ads);
end;
function TDataSetPool.GetCount() : Integer;
begin
Result := _ads.Count;
end;
function TDataSetPool.GetDataSet(const intype : TManagedDataSetType = Editable) : TManagedDataSet;
var
i : Integer;
begin
Result := nil;
for i := 0 to _ads.Count-1 do
begin
if (not TManagedDataSet(_ads[i]).IsUsed) and (TManagedDataSet(_ads[i]).DataSetType = intype) then
begin
Result := TManagedDataSet(_ads[i]);
Result.Use;
break;
end;
end;
if Result = nil then
begin
_ads.Add(TManagedDataSet.Create(intype));
Result := TManagedDataSet(_ads[_ads.Count-1]);
Result.Use;
end;
end;
function TDataSetPool.GetAdoCommand() : TAdoCommand;
begin
Result := TADOCommand.Create(nil);
Result.Connection := DmCommon.Cnn;
end;