Post) use the rtti of Delphi to implement simple object of the dataset

Source: Internet
Author: User
In the article "powerful Delphi rtti -- and talk about the need to know a variety of development languages", I spoke about it. I used the rtti of Delphi to implement simple object-based data sets. This article will introduce in detail my implementation methods.

Let's start with a simple example: Suppose there is an adodataset control that connects to the arms database. The SQL is:

Select * from employee

Now we need to display the four fields of employee ID, firstname, lastname, and birthdate in its content to listview. The traditional code is as follows:

    With ADODataSet1 Do    Begin        Open;        While Not Eof Do        Begin            With ListView1.Add Do            Begin                Caption := IntToStr( FieldByName( 'EmployeeID' ).AsInteger );                SubItems.Add( FieldByName( 'FirstName' ).AsString );                SubItems.Add( FieldByName( 'LastName' ).AsString );                SubItems.Add( FormatDateTime( FieldByName( 'BirthDate' ).AsDateTime ) );            End;            Next;        End;        Close;    End;

There are several problems:

1. First, there are a lot of code that is very lengthy. For example, fieldbyname and asxxx, especially asxxx, must always remember the type of each field, which is easy to make mistakes. In addition, if some incompatible types cannot be automatically converted, errors can only be found at runtime.

2. You need to process the movement of the current record in the loop. Otherwise, an endless loop will occur when you forget the next. Although this problem is easy to find and handle, programmers should not be entangled in such small details.

3. The most important thing is that the field name is passed through the string parameter. If an error is written, it will not be found until the runtime, increasing the possibility of bugs, especially if the test does not completely cover all fieldbynames, it is very likely that this problem will occur only when the customer is there. This type of wrong field name can easily happen. Especially when the program uses multiple tables, it is easy to confuse the field names of different tables.

In this era ruled by Oo, when we encounter operations related to datasets, we still have to fall into the details of these relational databases. Of course, there are ways to get rid of them now, that is, O/R Mapping, but O/R Mapping is too different from traditional development methods after all, especially for some small applications, there is no need to make such an exaggeration. In this case, all we need is a simple Dataset Visualization solution.

Inspired by Java and other dynamic languages, I came up with the powerful rtti of Delphi to implement this simple dataset objectization solution. The following is the Dataset Visualization application code that implements the same functions as traditional code:

Type    TDSPEmployee = class(TMDataSetProxy)    published        Property EmployeeID : Integer Index 0 Read GetInteger Write SetInteger;        Property FirstName  : String  Index 1 Read GetString  Write SetString;        Property LastName   : String  Index 2 Read GetString  Write SetString;        Property BirthDate  : Variant Index 3 Read GetVariant Write SetVariant;    end;procedure TForm1.ListClick(Sender: TObject);Var    emp : TDSPEmployee;begin    emp := TDSPEmployee.Create( ADODataSet1 );    Try        While ( emp.ForEach ) Do        With ListView1.Items.Add Do        Begin            Caption := IntToStr( emp.EmployeeID );            SubItems.Add( emp.FirstName );            SubItems.Add( emp.LastName );            SubItems.Add( FormatDateTime( 'yyyy-mm-dd', TDateTime( emp.BirthDate ) ) );        End;    Finally        emp.Free;    End;end;

It is easy to use. The most important thing is to first define a proxy class, in which all fields are defined by published attributes, including its type, and then the dataset can be operated as an object. This proxy class is derived from tmdatasetproxy. In this class, rtti is used to map attribute operations to field operations. You only need to uses the corresponding units for use. The implementation units of this class are described in detail below.

On the surface, a proxy class that defines a dataset seems to have more code, but this is a permanent task, especially when the program needs to reuse a dataset of the same structure multiple times, this will greatly reduce the amount of code. What's more, the definition of this proxy class is very simple. It just defines a series of attributes based on the field name and field type, without any implementation code. The property access functions getxxx/setxxx are implemented in the basic class tmdatasetproxy.

Now let's look at the loop corresponding to the original code:

1. fieldbyname and asxxx do not need to be changed to an attribute operation for the proxy class, and the type of the attribute corresponding to each field has been defined before, you don't have to worry about the type every time you use it. If an error type is used, an error is reported during compilation.

2. Use a foreach to traverse records, so you don't have to worry about the endless loop caused by next.

3. The biggest benefit is that the field name is changed to an attribute, so that you can enjoy the advantages of field name verification during compilation, unless the field name is wrong when the proxy class is defined, otherwise, it can be found during compilation.

Now we will discuss tmdatasetproxy. The implementation code is as follows:

(*************************************** * *************************** Use the data set proxy implemented by rtti, you can simply visualize a dataset. Copyright (c) 2005 by mental studio. author: Bird Date: jan.28-05 ************************************* *****************************) unit mdspcomm; interfaceuses classes, DB, typinfo; Type tmproplist = Class (tobject) Private fpropcount: integer; fproplist: pproplist; protected function getpropname (aindex: integer): bytes string; function getprop (aindex: integer): ppropinfo; Public constructor create (aobj: tpersistent); destructor destroy; override; property propcount: integer read fpropcount; property propnames [aindex: integer]: optional string read getpropname; property props [aindex: integer]: ppropinfo read getprop; end; tmdatasetproxy = Class (tpersistent) Private fdataset: tdataset; fproplist: tmproplist; flooping: Boolean; protected procedure beginedit; Procedure endedit; function getinteger (aindex: integer): integer; virtual; function getfloat (aindex: integer): Double; virtual; function getstring (aindex: integer ): string; virtual; function getvariant (aindex: integer): variant; virtual; Procedure setinteger (aindex: integer; avalue: integer); Virtual; Procedure setfloat (aindex: integer; avalue: double); Virtual; Procedure setstring (aindex: integer; avalue: string); Virtual; Procedure setvariant (aindex: integer; avalue: variant); Virtual; Public constructor create (adataset: tdataset); destructor destroy; override; Procedure afterconstruction; override; function foreach: Boolean; property Dataset: tdataset read fdataset; end; implementation {tmproplist} constructor tmproplist. create (aobj: tpersistent); begin fpropcount: = gettypedata (aobj. classinfo) ^. propcount; fproplist: = nil; If fpropcount> 0 then begin getmem (fproplist, fpropcount * sizeof (pointer); getpropinfos (aobj. classinfo, fproplist); end; destructor tmproplist. destroy; begin if assigned (fproplist) Then freemem (fproplist); inherited; end; function tmproplist. getprop (aindex: integer): ppropinfo; begin result: = nil; If (assigned (fproplist) then result: = fproplist [aindex]; end; function tmproplist. getpropname (aindex: integer): Repeated string; begin result: = getprop (aindex) ^. name; end; {tmrefdataset} constructor tmdatasetproxy. create (adataset: tdataset); begin inherited create; fdataset: = adataset; fdataset. open; flooping: = false; end; destructor tmdatasetproxy. destroy; begin fproplist. free; if assigned (fdataset) Then fdataset. close; inherited; end; Procedure tmdatasetproxy. afterconstruction; begin inherited; fproplist: = tmproplist. create (Self); end; Procedure tmdatasetproxy. beginedit; begin if (fdataset. state <> dsedit) and (fdataset. state <> dsinsert) Then fdataset. edit; end; Procedure tmdatasetproxy. endedit; begin if (fdataset. state = dsedit) or (fdataset. state = dsinsert) Then fdataset. post; end; function tmdatasetproxy. getinteger (aindex: integer): integer; begin result: = fdataset. fieldbyname (fproplist. propnames [aindex]). asinteger; end; function tmdatasetproxy. getfloat (aindex: integer): Double; begin result: = fdataset. fieldbyname (fproplist. propnames [aindex]). asfloat; end; function tmdatasetproxy. getstring (aindex: integer): string; begin result: = fdataset. fieldbyname (fproplist. propnames [aindex]). asstring; end; function tmdatasetproxy. getvariant (aindex: integer): variant; begin result: = fdataset. fieldbyname (fproplist. propnames [aindex]). value; end; Procedure tmdatasetproxy. setinteger (aindex, avalue: integer); begin beginedit; fdataset. fieldbyname (fproplist. propnames [aindex]). asinteger: = avalue; end; Procedure tmdatasetproxy. setfloat (aindex: integer; avalue: Double); begin beginedit; fdataset. fieldbyname (fproplist. propnames [aindex]). asfloat: = avalue; end; Procedure tmdatasetproxy. setstring (aindex: integer; avalue: string); begin beginedit; fdataset. fieldbyname (fproplist. propnames [aindex]). asstring: = avalue; end; Procedure tmdatasetproxy. setvariant (aindex: integer; avalue: variant); begin beginedit; fdataset. fieldbyname (fproplist. propnames [aindex]). value: = avalue; end; function tmdatasetproxy. foreach: Boolean; begin result: = Not fdataset. EOF; If flooping then begin endedit; fdataset. next; Result: = Not fdataset. EOF; if not result then begin fdataset. first; flooping: = false; end else if result then flooping: = true; end.

The tmproplist class encapsulates partial functions of rtti attribute operations. The function is to use some rtti functions defined by Delphi in the typinfo unit to maintain the attribute list of published for a tpersistent derived class. The proxy class obtains the attribute name through this attribute list, and finally operates with the corresponding field in the dataset through this attribute name.

Tmdatasetproxy is the base class of the dataset proxy class. The most important part is to create an attribute list in afterconstruction.

Here, only integer, double, float, string, and Variant data types are implemented. If necessary, you can derive your own Proxy Base class on this basis to implement other data types, and these attribute operation implementations of the implemented types are defined as virtual functions, you can also replace it with your own implementations in the derived base class. However, for a type that is not commonly used, we recommend that you define the actual proxy class before implementation. For example, in the previous example, if tdatetime is not a common type, you can do this:

    TDSPEmployee = class(TMDataSetProxy)    protected        function  GetDateTime(const Index: Integer): TDateTime;        procedure SetDateTime(const Index: Integer; const Value: TDateTime);    published        Property EmployeeID : Integer Index 0 Read GetInteger Write SetInteger;        Property FirstName  : String  Index 1 Read GetString  Write SetString;        Property LastName   : String  Index 2 Read GetString  Write SetString;        Property BirthDate  : TDateTime Index 3 Read GetDateTime Write SetDateTime;    end;{ TDSPEmployee }function TDSPEmployee.GetDateTime(const Index: Integer): TDateTime;begin    Result := TDateTime( GetVariant( Index ) );end;procedure TDSPEmployee.SetDateTime(const Index: Integer;  const Value: TDateTime);begin    SetVariant( Index, Value );end;

In this way, you can directly use birthdate as the tdatetime type.

In addition, this can also provide unified operations for some custom special data types.

In addition, beginedit is called before all setxxx to avoid running errors caused by forgetting to use dataset. Edit.

Foreach is implemented to be reusable. After each foreach completes a traversal, the current record is moved to the first record for the next loop. In addition, endedit is called before next to automatically submit the modifications.

This dataset objectization solution is a very simple solution. The biggest problem currently exists is that the index parameter of the attribute must strictly follow the order in which the attribute is defined; otherwise, the wrong field will be obtained. This is because Delphi is still a native development language after all. The only way to call different attributes of the same type of getxxx/setxxx time zone is through index, this index parameter is passed to the function at compilation, and there is no dynamic table to record. Therefore, we can only use this method.

Birds of prey jan.28-05

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.