2011. április 13., szerda
Collection Dataset an object oriented database
Problem/Question/Abstract:
Incapsulating a collection in a TDataset decendant. Enabling to save and load diferent datasets bij loading and saving component resources.
Answer:
I have writen a TDataset descendant that allows a collection to be set as property so it will do the deletes inserts and updates for you with a little help from the Data aware controls in delphi.
I made an example that saves some master detail data .
In my example i'll show you how i use the dataset in design time so i can set the fields displaylength and it's displayLabel
For those cracks that do not need an example here's the compleet code of the object.
For those who do just download the sample .
And of course do not forget to vote or leave a message :) ..
Greatings all and keep up the good work.
unit CollectionDataSet;
interface
uses
DB, Classes, typinfo, dialogs;
type
PRecInfo = ^TRecInfo;
TRecInfo = packed record
Bookmark: Integer;
BookmarkFlag: TBookmarkFlag;
end;
{ TCollectionDataSet }
TCollectionDataSet = class(TDataSet)
private
FRecBufSize: Integer;
FRecInfoOfs: Integer;
FCurRec: Integer;
FFileName: string;
FLastBookmark: Integer;
FCollection: TCollection;
FCollectionCount: Integer;
procedure SetCollection(const Value: TCollection);
protected
function AllocRecordBuffer: PChar; override;
procedure FreeRecordBuffer(var Buffer: PChar); override;
procedure GetBookmarkData(Buffer: PChar; Data: Pointer); override;
function GetBookmarkFlag(Buffer: PChar): TBookmarkFlag; override;
function GetRecord(Buffer: PChar; GetMode: TGetMode; DoCheck: Boolean): TGetResult; override;
function GetRecordSize: Word; override;
procedure InternalAddRecord(Buffer: Pointer; Append: Boolean); override;
procedure InternalClose; override;
procedure InternalCancel; override;
procedure InternalDelete; override;
procedure InternalFirst; override;
procedure InternalGotoBookmark(Bookmark: Pointer); override;
procedure InternalHandleException; override;
procedure InternalInitFieldDefs; override;
procedure InternalInitRecord(Buffer: PChar); override;
procedure InternalLast; override;
procedure InternalOpen; override;
procedure InternalPost; override;
procedure InternalSetToRecord(Buffer: PChar); override;
function IsCursorOpen: Boolean; override;
procedure SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag); override;
procedure SetBookmarkData(Buffer: PChar; Data: Pointer); override;
procedure SetFieldData(Field: TField; Buffer: Pointer); override;
function GetRecordCount: Integer; override;
function GetRecNo: Integer; override;
procedure SetRecNo(Value: Integer); override;
public
function GetFieldData(Field: TField; Buffer: Pointer): Boolean; override;
property Collection: TCollection read FCollection write SetCollection;
published
property FileName: string read FFileName write FFileName;
property Active;
property AutoCalcFields;
property BeforeOpen;
property AfterOpen;
property BeforeClose;
property AfterClose;
property BeforeInsert;
property AfterInsert;
property BeforeEdit;
property AfterEdit;
property BeforePost;
property AfterPost;
property BeforeCancel;
property AfterCancel;
property BeforeDelete;
property AfterDelete;
property BeforeScroll;
property AfterScroll;
property BeforeRefresh;
property AfterRefresh;
property OnCalcFields;
property OnDeleteError;
property OnEditError;
property OnFilterRecord;
property OnNewRecord;
property OnPostError;
end;
procedure Register;
implementation
uses Windows, SysUtils, Forms;
{ TCollectionDataSet }
procedure TCollectionDataSet.InternalOpen;
begin
if Collection = nil then
raise EDatabaseError.Create('Collection is niet gevult !');
FCurRec := -1;
FCollectionCount := Collection.Count;
FLastBookmark := Collection.Count;
FRecInfoOfs := SizeOf(Integer);
FRecBufSize := SizeOf(TRecInfo) + FRecInfoOfs;
BookmarkSize := SizeOf(Integer);
InternalInitFieldDefs;
if DefaultFields then
CreateFields;
BindFields(True);
end;
procedure TCollectionDataSet.InternalClose;
begin
if DefaultFields then
DestroyFields;
FLastBookmark := 0;
FCurRec := -1;
end;
function TCollectionDataSet.IsCursorOpen: Boolean;
begin
Result := Assigned(collection);
end;
procedure TCollectionDataSet.InternalInitFieldDefs;
var
PropList: PPropList;
PropCount: Integer;
ClassTypeInfo: PTypeInfo;
ClassTypeData: PTypeData;
i: integer;
begin
FieldDefs.Clear;
if Collection = nil then
raise EInvalidPointer.create('Collection is nil');
ClassTypeInfo := Collection.ItemClass.ClassInfo;
ClassTypeData := GetTypeData(ClassTypeInfo);
PropCount := ClassTypeData.PropCount - 1;
// reserveer geheugen
GetMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
// Error trap
try
// Vul de prop list
GetPropList(Collection.ItemClass.ClassInfo, tkAny, PropList);
for i := 0 to PropCount do
begin
try
case PropList[i]^.PropType^.Kind of
tkString, tkLString,
tkWString, tkWChar,
tkChar:
begin
FieldDefs.Add(PropList[i]^.Name, ftString, 255, False);
end;
tkInteger,
tkEnumeration:
begin
FieldDefs.Add(PropList[i]^.Name, ftInteger, 0, False);
end;
tkFloat:
begin
FieldDefs.Add(PropList[i]^.Name, ftFloat, 0, False);
end;
tkClass:
begin
end;
tkArray:
begin
end;
end; // end case
except
on e: Exception do
end;
end;
finally
FreeMem(PropList, SizeOf(PPropInfo) * ClassTypeData.PropCount);
end;
end;
procedure TCollectionDataSet.InternalHandleException;
begin
Application.HandleException(Self);
end;
procedure TCollectionDataSet.InternalGotoBookmark(Bookmark: Pointer);
var
Index: Integer;
begin
Index := PInteger(Bookmark)^ - 1;
if Index > -1 then
FCurRec := Index
else
DatabaseError('Bookmark not found');
end;
procedure TCollectionDataSet.InternalSetToRecord(Buffer: PChar);
begin
InternalGotoBookmark(@PRecInfo(Buffer + FRecInfoOfs).Bookmark);
end;
function TCollectionDataSet.GetBookmarkFlag(Buffer: PChar): TBookmarkFlag;
begin
Result := PRecInfo(Buffer + FRecInfoOfs).BookmarkFlag;
end;
procedure TCollectionDataSet.SetBookmarkFlag(Buffer: PChar; Value: TBookmarkFlag);
begin
PRecInfo(Buffer + FRecInfoOfs).BookmarkFlag := Value;
end;
procedure TCollectionDataSet.GetBookmarkData(Buffer: PChar; Data: Pointer);
begin
PInteger(Data)^ := PRecInfo(Buffer + FRecInfoOfs).Bookmark;
end;
procedure TCollectionDataSet.SetBookmarkData(Buffer: PChar; Data: Pointer);
begin
PRecInfo(Buffer + FRecInfoOfs).Bookmark := PInteger(Data)^;
end;
function TCollectionDataSet.GetRecordSize: Word;
begin
Result := SizeOf(Integer); //MaxStrLen;
end;
function TCollectionDataSet.AllocRecordBuffer: PChar;
begin
GetMem(Result, FRecBufSize);
end;
procedure TCollectionDataSet.FreeRecordBuffer(var Buffer: PChar);
begin
FreeMem(Buffer, FRecBufSize);
end;
function TCollectionDataSet.GetRecord(Buffer: PChar; GetMode: TGetMode;
DoCheck: Boolean): TGetResult;
begin
if Collection.Count < 1 then
Result := grEOF
else
begin
Result := grOK;
case GetMode of
gmNext:
if FCurRec >= RecordCount - 1 then
Result := grEOF
else
Inc(FCurRec);
gmPrior:
if FCurRec <= 0 then
Result := grBOF
else
Dec(FCurRec);
gmCurrent:
if (FCurRec < 0) or (FCurRec >= RecordCount) then
Result := grError;
end;
if Result = grOK then
begin
PInteger(Buffer)^ := Integer(FCollection.Items[FCurRec]);
with PRecInfo(Buffer + FRecInfoOfs)^ do
begin
BookmarkFlag := bfCurrent;
Bookmark := FCurRec + 1;
end;
end
else if (Result = grError) and DoCheck then
DatabaseError('No Records');
end;
end;
procedure TCollectionDataSet.InternalInitRecord(Buffer: PChar);
begin
PInteger(Buffer)^ := Integer(FCollection.Add);
end;
function TCollectionDataSet.GetFieldData(Field: TField; Buffer: Pointer): Boolean;
var
Apropinfo: PPropinfo;
AString: string;
AInteger: Integer;
AFloat: Double;
AItem: TCollectionItem;
begin
Result := False;
if Collection.Count = 0 then
exit;
AItem := TCollectionItem(PInteger(ActiveBuffer)^);
Apropinfo := typinfo.GetPropInfo(AItem, Field.FullName);
case Apropinfo.PropType^.Kind of
tkString, tkLString,
tkWString, tkWChar,
tkChar:
begin
AString := GetStrProp(AItem, Apropinfo);
StrLCopy(Buffer, PChar(AString), Length(AString));
Result := PChar(Buffer)^ <> #0;
end;
tkInteger,
tkEnumeration:
begin
AInteger := GetOrdProp(AItem, Apropinfo);
PInteger(Buffer)^ := AInteger;
Result := true;
end;
tkFloat:
begin
AFloat := GetFloatProp(AItem, Apropinfo);
PDouble(Buffer)^ := AFloat;
Result := true;
end;
end; // end case
end;
procedure TCollectionDataSet.SetFieldData(Field: TField; Buffer: Pointer);
var
Apropinfo: PPropinfo;
AString: string;
AInteger: Integer;
AFloat: Double;
AItem: TCollectionItem;
begin
AItem := TCollectionItem(PInteger(ActiveBuffer)^);
Apropinfo := typinfo.GetPropInfo(AItem, Field.FullName);
case Apropinfo.PropType^.Kind of
tkString, tkLString,
tkWString, tkWChar,
tkChar:
begin
AString := PChar(Buffer);
SetStrProp(AItem, Apropinfo, AString);
end;
tkInteger,
tkEnumeration:
begin
AInteger := 0;
if Buffer <> nil then
AInteger := PInteger(Buffer)^;
SetOrdProp(AItem, Apropinfo, AInteger);
end;
tkFloat:
begin
AFloat := 0;
if Buffer <> nil then
AFloat := PDouble(Buffer)^;
SetFloatProp(AItem, Apropinfo, AFloat);
end;
end; // end case
DataEvent(deFieldChange, Longint(Field));
end;
procedure TCollectionDataSet.InternalFirst;
begin
FCurRec := -1;
end;
procedure TCollectionDataSet.InternalLast;
begin
FCurRec := FCollectionCount;
end;
procedure TCollectionDataSet.InternalPost;
begin
if State = dsinsert then
begin
Inc(FCollectionCount);
Inc(FLastBookmark);
if FCurRec <> -1 then
TCollectionItem(PInteger(ActiveBuffer)^).Index := FCurRec;
end;
end;
procedure TCollectionDataSet.InternalAddRecord(Buffer: Pointer; Append: Boolean);
begin
Inc(FLastBookmark);
if Append then
InternalLast;
Inc(FCollectionCount);
end;
procedure TCollectionDataSet.InternalDelete;
begin
Collection.Delete(FCurRec);
Dec(FCollectionCount);
if FCurRec >= Collection.Count then
Dec(FCurRec);
end;
function TCollectionDataSet.GetRecordCount: Longint;
begin
Result := FCollectionCount;
end;
function TCollectionDataSet.GetRecNo: Longint;
begin
UpdateCursorPos;
if (FCurRec <= -1) and (RecordCount > 0) then
Result := 0
else
Result := FCurRec + 1;
end;
procedure TCollectionDataSet.SetRecNo(Value: Integer);
begin
if (Value >= 0) and (Value < FCollectionCount) then
begin
FCurRec := Value - 1;
Resync([]);
end;
end;
procedure TCollectionDataSet.SetCollection(const Value: TCollection);
begin
FCollection := Value;
end;
procedure TCollectionDataSet.InternalCancel;
begin
Collection.Delete(Collection.Count - 1);
end;
end.
Feliratkozás:
Megjegyzések küldése (Atom)
Nincsenek megjegyzések:
Megjegyzés küldése