Well, after two very basic and
introductory posts about TParams (post 1, post 2), I think it is time to explain in more detail
why I am so excited about. It is not about high-end programming, nor about
beauty of coding, nor even about state of the art software development. It is
just simplicity!
Now that some basic functionality of
TParams has been showed off I can go further and deep into using it in data
operations.
As I mentioned in other posts and
especially in the first, a typical usage of TParams is to hold the values of
the fields from a single database record. I also introduced some utility
functions that help communication between TParams and the fields of a dataset.
In fact I rarely use such a procedural
approach; I have long time ago crucified COBOL ;) .
Delphi is object oriented and
as such we have the option to create classes and encapsulate such functionality.
Delphi XE2 introduced a nice new feature; class helpers, for extending existing
class functionality, but here we do not want to extend the actual TParams class.
We will create a new branch with usages beyond what Borland originally designed
for this piece of code. Using plain old inheritance we will create a new class,
derived from TParams, which will support the two way communication with
datasets.
The class should –in general- be able to:
- Clone and hold data from a given dataset.
- Process the data without touching the actual dataset ones.
- Work in an offline scene with data.
- Upload data to datasets.
And most importantly, these
accomplishments should be almost encapsulated in class and sets of classes that
can be extended and adapt specific processing needs by using OOP techniques. Such
techniques can be to communicate data between classes, modules, programs and
network, create hierarchical lists and associations and even relationships.
In order to accomplish these tasks the
class should be able to:
- Store and access data much the same -pretty- way a dataset access its fields.
- The class should be able to build its fields based on a given TDataset existing fields structure.
- The class should be able to
transfer field values from a given dataset and back to the same or another
dataset corresponding fields.
Some extended functionality could be to
add a new record or update an existing one to a dataset, or even delete a
record.
Well instead of writing a long blog post,
I think it is better to deep into my code. So I have created a project at
SourceForge.net where you can find the class and a demo project.
Here is the interface part of the class
with descriptive remarks for each member:
{@ TDatasetRecord Class to represent a dataset record in a TParams collection with each TParam serving as a named field/value corresponding to a dataset field} TDatasetRecord = Class( TParams ) private FDataset: TDataset; {@ Dataset associated with the recordclass} FUpdateDataset: TDataset; {@ Dataset to update from recordclass data} FRecordAvailable, {@ Record class has it's fields (TParams) created and bound them} FRecordExists, {@ Record has beed loaded from a dataset } FIncludeAllFields: Boolean; {@ Create all fields from the dataset} function GetParam(Index: integer): TParam; function GetDataSet: TDataSet; procedure SetDataSet(Value: TDataSet); function GetUpdateDataSet: TDataSet; procedure SetUpdateDataSet(Value: TDataSet); function GetFieldValue(const FieldName: string): Variant; procedure SetFieldValue(const FieldName: string; const Value: Variant); protected {@ Initializes recordclass and refreshes current recordfield values from Dataset} procedure Initialize(RDataset: TDataset; UDataset: TDataset=nil); {@ Create recordclass fields } procedure CreateFields; virtual; {@ Used by derived classes to set a predefined list of allowed recordclass fieldnames} class function FieldNames(Index: integer): string; virtual; public {@ Construct recordclass connecting to a Dataset, and optional an update dataset} constructor Create(RDataset: TDataset; UDataset: TDataset=nil); reintroduce; virtual; destructor Destroy; override; function AddField(FldType: TFieldType; const FieldName: string): TParam; {@ Add recordfields from an object (TDataset, TFIelds, TFieldList, TFieldDefs) } function AddFields(Source: TObject): integer; {@ Assign, TParams collection override } procedure Assign(Source: TPersistent); override; {@ Get a new recordclass object cloning self properties, fields & data} function CloneRecord: TDatasetRecord; {@ Set/Unset recordfields Bound property, Bound=False clears all TParam values } procedure BoundRecord(DoBound: Boolean); {@ Get a list of recordfields defined in FieldNames string } procedure GetFieldList(List: TList; const FieldNames: string); {@ Get a FieldNames string stuffed with all recordfield names } function GetFieldNames: string; {@ Test Target Fields against recordfields for equal values } function EqualsTo(Target: TDatasetRecord; UseFields: string = ''): Boolean; {@ Set UpdateDataset field values from recordfields } function SetDatasetFields: integer; overload; {@ Set Target Dataset field values from recordfields } function SetDatasetFields(Target: TDataset): integer; overload; {@ Set recordfield values from corresponding From Dataset fields } procedure SetRecordFields(From: TDataSet; UnBound: Boolean); overload; {@ Set recordfield values from corresponding From recordclass fields } procedure SetRecordFields(From: TDatasetRecord; UnBound: Boolean); overload; {@ Retrieve recordfield values from current dataset record} procedure RefreshRecord(DoOpen: Boolean=True); {@ recordField access method by name } function Field(Value: string): TParam; {@ RecordClass has been filled with recordfield values } function IsAvailable: Boolean; {@ Associated dataset record existed when recordclass filled with values } function IsExisting: Boolean; property Dataset: TDataset read GetDataset; property UpdateDataset: TDataset read GetUpdateDataset write SetUpdateDataset; property IncludeAllFields: Boolean read FIncludeAllFields write FIncludeAllFields default True; property FieldValues[const FieldName: string]: Variant read GetFieldValue write SetFieldValue; default; end;
You can download source code files and a simple Datasnap XE2 demo here: http://users.hol.gr/~georgev/delphi.htm