[转载]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.

posted @ 2005-06-10 14:20  hingman  阅读(377)  评论(0)    收藏  举报