TManagedDataSet和DataSetPool的实现
【打印文章】
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; // 滦籠摸Freeぃ穦睦龟ㄒ
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;
end.
由于代码中使用了很多的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; // 滦籠摸Freeぃ穦睦龟ㄒ
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;
end.
本栏文章均来自于互联网,版权归原作者和各发布网站所有,本站收集这些文章仅供学习参考之用。任何人都不能将这些文章用于商业或者其他目的。( Pfan.cn )
【编程爱好者论坛】