Beruflich Dokumente
Kultur Dokumente
R E L A T E D L A N G U A G E S / A N D R O I D,
I O S, M A C , W I N D O W S & L I N U X
P R I N T E D, P D F, & O N L I N E V I E W
DX
COMBINATION: 3 FOR 1
BOOK INCLUDING THE LIBRARY STICK EXCL. SHIPPING
INCLUDING 1YEAR DOWNLOAD FOR FREE 75€
http://www.blaisepascal.eu/subscribers/UK/UK_CD_DVD_USB_Department.html
CONTENTS
ARTICLES
RTTI FOR STARTERS PART 1 (RUNTIME TYPE INFORMATION) PAGE 5
BY MICHAEL VAN CANNEYT
CALLING LINUX COMMANDS FROM DELPHI PAGE 14
BY CRAIG CHAPMA
CREATING A LINUX DAEMON (SERVICE) IN DELPHI PAGE 16
BY CRAIG CHAPMAN
LINUX APPLICATION DEVELOPMENT PAGE 17
CREATING A TODO LIST WITH FIRE DAC MEMTABLE PAGE 19
BY DETLEF OVERBEEK
BLUETOOTH CONTROLLED ROBOT WITH VISUINO AND DELPHI PAGE 29
PART 2 - DELPHI
BY BOIAN MITOV
KBMMW ORM (OBJECT RELATIONAL MODEL) PAGE 37
BY KIM BO MADSEN
V
X CL
M
F
FNC
V
X CL
M
F
FNC
PASCON 2017
19 SEPTEMBER
ADVERTISERS
ANDROID LOGCAT FREE TOOL COMPONENTS4DEVELOPERS PAGE 27
BARNSTEN PAGE 11/12/13
PASCON 2017 DELPHI & PASCAL EVENTS PAGE 18
COMPONENTS4DEVELOPERS PAGE 44
SPECIAL OFFER PAGE 2
VISUINO PAGE 28
Wim Van Ingen Schenau -Editor Peter van der Sman Rik Smit
wisone @ xs4all.nl sman @ prisman.nl rik @ blaisepascal.eu
www.romplesoft.de
Editor - in - chief
Detlef D. Overbeek, Netherlands Tel.: +31 (0)30 890.66.44 / Mobile: +31 (0)6 21.23.62.68
News and Press Releases email only to editor@blaisepascal.eu
Editors
Peter Bijlsma, W. (Wim) van Ingen Schenau, Rik Smit,
Correctors
Howard Page-Clark, James D. Duff
Trademarks
All trademarks used are acknowledged as the property of their respective owners.
Caveat Whilst we endeavour to ensure that what is published in the magazine is correct, we cannot accept responsibility for any errors or omissions.
If you notice something which may be incorrect, please contact the Editor and we will publish a correction where relevant.
Subscriptions ( 2013 prices )
1: Printed version: subscription € 85.-- Incl. VAT 6 % (including code, programs and printed magazine,
10 issues per year excluding postage).
2: Electronic - non printed subscription € 50.-- Incl. VAT 21% (including code, programs and download magazine)
Subscriptions can be taken out online at www.blaisepascal.eu or by written order, or by sending an email to office@blaisepascal.eu
Subscriptions can start at any date. All issues published in the calendar year of the subscription will be sent as well.
Subscriptions run 365 days. Subscriptions will not be prolonged without notice. Receipt of payment will be sent by email.
Subscriptions can be paid by sending the payment to:
ABN AMRO Bank Account no. 44 19 60 863 or by credit card: Paypal
Name: Pro Pascal Foundation-Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal)
IBAN: NL82 ABNA 0441960863 BIC ABNANL2A VAT no.: 81 42 54 147 (Stichting Programmeertaal Pascal)
Subscription department Edelstenenbaan 21 / 3402 XA IJsselstein, The Netherlands / Tel.: + 31 (0) 30 890.66.44 / Mobile: + 31 (0) 6 21.23.62.68
office@blaisepascal.eu
Copyright notice
All material published in Blaise Pascal is copyright © SOPP Stichting Ondersteuning Programeertaal Pascal unless otherwise noted and may
not be copied, distributed or republished without written permission. Authors agree that code associated with their articles will be made
available to subscribers after publication by placing it on the website of the PGG for download, and that articles and code will be placed on
distributable data storage media. Use of program listings by subscribers for research and study purposes is allowed, but not for commercial
purposes. Commercial use of program listings and code is prohibited without the written permission of the author.
begin
IDX:=CBCOmponent.ItemIndex;
if (IDX=-1) then
Raise Exception.Create(
'Select a component first');
C:=CBCOmponent.Items.Objects[IDX] as TComponent;
SetProperty(C,EName.Text,EValue.Text);
end;
Figure 1/2 : Setting a property
begin
With CBName,Items do
begin
try
BeginUpdate;
Clear;
Sorted:=False;
if Assigned(C) then
GetPropertyNames(C,CBName.Items);
Sorted:=True;
finally
EndUpdate;
end;
end;
end;
Var
Ctx : TRTTIContext;
P : TRttiProperty;
begin
Ctx:=TRTTIContext.Create;
try
For P in ctx.GetType(C.ClassInfo).GetProperties do
AList.Add(P.Name);
finally
Ctx.free;
end;
end;
The CTX.GetType call is the same as used in the code This can be done because all controls on the form
to set a property name: it returns a TRTTIType descend from TControl and they all inherit the
descendent. Enabled property.
The GetProperties method of the TRTTIType
class returns an array of TRttiProperty instances: But imagine another scenario:
one for each property in the class. only Data-Aware controls must be disabled.
This array is traversed, and the names of each of the Data-Aware controls have been created as descendants
properties is added to the list. of regular controls which implement Data-related
The resulting code results in a form that looks as in properties (DataField and DataSource).
figure 5/6. That means that there is no property at the TControl
level which can be checked to see if the property is
data-aware.
Without RTTI, the only method to achieve this would
have been to check the type of the control:
Var
I : Integer;
C : TControl;
begin
For I:=0 to ControlCount-1 do
begin
C:=Controls[i];
if (C is TDBText)
or (C is TDBGrid)
// check all other types
or (C is TDBEdit) then
C.Enabled:=False;
end;
Figure 5/6 :
Selecting a property of a selected component It is clear that this is error-proof and slow.
With RTTI, we can do this a lot easier.
SETTING A KNOWN PROPERTY ON SEVERAL When the Data-Aware controls were made, care was
CLASSES taken that they all use the same property names to
The code demonstrated till now was informative, link to a datasource and indicate a field
but hardly practical. Let's turn to a more practical (DataField and DataSource).
example. Using this fact, we can just check whether the control
To disable all controls of a form, their 'enabled' has the Datasource property to know if it is data
property can be set to false. this can be done with a aware, and disable it. Using the typinfo unit, this
simple loop: is done as follows: (next page)
Var Conclusion
I : Integer; The RTTI of Delphi is a powerful tool. For simple
C : TControl; projects, it's mostly hidden in the form streaming
code, but as projects become more advanced, it can
begin be useful to know how RTTI functions and what can
For I:=0 to ControlCount-1 do be done with it. In this article, a start has been
begin made, showing some simple things like how to set
C:=Controls[i]; an arbitrary property using RTTI, how to get a list of
if isPublishedProp(C,'DataSource') then properties, and how to check if a class has a certain
C.Enabled:=False; property. RTTI can do many more things, such as
end;
examine attributes or invoke arbitrary methods.
end;
These topics will be treated in a future article.
begin
Ctx:=TRTTIContext.Create;
try
For I:=0 to ControlCount-1 do
begin
C:=Controls[i];
for P in Ctx.GetType(C.ClassInfo).GetDeclaredProperties do
if SameText(P.Name,'DataSource') then
begin
C.Enabled:=False;
Break;
end;
end;
finally
Ctx.free;
end;
end;
50%
off
Deal 2:
Expanding your Delphi development team using Delphi Professional?
Buy now your additional licenses and benefit from a discount of 50% on your
second license
Learn more at: http://embt.co/2pEO09O
THIS OFFER IS ALSO VALID ON C++BUILDER AND RAD STUDIO
PROFESSIONAL LICENSES
www.barnsten.com .
11 Issue Nr 3 2017 BLAISE PASCAL MAGAZINE
• development tools • training
barnsten • consultancy
• components
• hands-on workshops
• support
DX RX CX
The Ultimate APPlication
Development Platform for
Windows 10, Mac, Mobile and IoT
www.barnsten.com .
12 Issue Nr 3 2017 BLAISE PASCAL MAGAZINE
• development tools • training
barnsten • consultancy
• components
• hands-on workshops
• support
We based the content of the sessions on the most frequently asked questions
we receive from developers:
1. How can I give my application a modern look and feel and how can I
optimize the underlaying code.
2. How can I use RAD Server for developing / migrating my multi-tier
applications.
3. What is the best way to develop cross platform.
4. How can I add Linux support in my Development environment.
www.barnsten.com .
13 Issue Nr 3 2017 BLAISE PASCAL MAGAZINE
CALLING LINUX COMMANDS FROM DELPHI PAGE 1/2
WORK
BY CRAIGWITH WINEHQ,
CHAPMAN WHAT IS IT? PAGE 3/4
BEFORE YOU CAN MAKE USE *NOTE* You should see the
of the following articles you must at least have complete video.
installed Delphi’s version Tokyo and follow the In this video I am assuming you are
installing requirements. These requirements already configured to deploy an
are mentioned on page 17. application to Linux and understand
how to launch it from the Linux command line.
INTRODUCTION: If this is not the case, see my earlier posts
One of the great features of Linux is that you can do
covering these subjects:
just about anything from the command line.
If we’re able to gain access to command line http://chapmanworld.com/2017/02/28/
instructions from our Delphi applications, this will embarcadero-delphi-linux-bootcamp/
give us a very powerful API for the system.
In this video I’m going to show you how to access http://chapmanworld.com/2016/12/29/con
the Linux command line from your Delphi figure-delphi-and-redhat-or-ubuntu-
applications. for-linux-development/
uses
System.SysUtils,
Posix.Base,
Posix.Fcntl;
type
TStreamHandle = pointer;
/// <summary>
/// Man Page: http://man7.org/linux/man-pages/man3/popen.3.html
/// </summary>
function popen(const command: MarshaledAString;
const _type: MarshaledAString):
TStreamHandle;
cdecl; external libc name _PU + 'popen';
/// <summary>
/// Man Page: http://man7.org/linux/man-pages/man3/pclose.3p.html
/// </summary>
function pclose(filehandle: TStreamHandle): int32;
cdecl;
external libc name _PU + 'pclose';
/// <summary>
/// Man Page: http://man7.org/linux/man-pages/man3/fgets.3p.html
/// </summary>
function fgets(buffer: pointer; size: int32;
Stream: TStreamHAndle): pointer;
cdecl;
external libc name _PU + 'fgets';
/// <summary>
/// Utility function to return a buffer of ASCII-Z data as a string.
/// </summary>
function BufferToString( Buffer: pointer;
MaxSize: uint32 ): string;
var cursor: ^uint8; EndOfBuffer: nativeuint;
begin
Result := '';
if not assigned(Buffer) then begin
exit;
end;
cursor := Buffer;
EndOfBuffer := NativeUint(cursor) + MaxSize;
while (NativeUint(cursor)<EndOfBuffer)
and (cursor^<>0) do begin
Result := Result + chr(cursor^);
cursor := pointer( succ(NativeUInt(cursor)) );
end;
end;
uses You could now replace the infinite while loop with
System.SysUtils; whatever application logic you need within your
daemon.
begin
try HOW DOES THIS WORK?
while true do sleep(100);
except The key to this working is the “fork()” method, which
on E: Exception do can be found in the unit “Posix.Unistd”. The fork
Writeln(E.ClassName, ': ', E.Message); method is a part of the standard posix API, who’s
end; documentation may be found here:
end. https://linux.die.net/man/3/fork
The return value of the call to fork tells us which INSTALLING LINUX SDK
instance of the process we are executing in. A return To install Linux SDK on Ubuntu,
first you have to add the Repository.
value of zero tells us that we are executing in the child 1. Right-click your desktop and select
process (the copy), and any other value tells us that we Open Terminal.
are executing in the parent process (the originator). In 2. To add a repository, type sudo
fact, when the return of fork is not zero, it is the process add-apt-repository ppa:
ID of the newly created child process. We simply call ubuntu-sdk-team/ppa in the terminal.
‘exit’ to exit the application when we determine that we 3. Press Enter. Now, your repository is added.
4. To install Linux SDK , type sudo apt install
are in the parent process, allowing the child copy to
ubuntu-sdk in the terminal.
proceed on to the infinite while loop. 5. Press Enter and wait until the SKD is
Conclusion installed.
At this point you are able to create daemon applications. It may take a few minutes to complete.
You may wish to research the Linux (posix) API’s in a little
more detail to see how to handle ‘signals’ which may be TO INSTALL LINUX SDK ON RED HAT:
used to gracefully terminate your application, rather than 1. Right-click your desktop and select
using the brute force termination of a kill instruction. You Open Terminal.
might also want some way to communicate with your 2. Type yum groupinstall 'Development Tools'
daemon process. There are many ways to communicate in the terminal.
with the process, but possibly the most familiar to a Delphi 3. Press Enter and wait until the SKD is
developer would be to use TCP/IP. Turning your service installed. It may take a few minutes to
into a TCP/IP server would allow you to send data and complete.
instructions to it.
PREPARING YOUR LINUX MACHINE
Regardless of what you decide to do with your daemon, I To create Linux applications, you need to add
hope you found this brief instructional useful. a virtual machine PAServer:
1. Find the LinuxPAServer19.0.tar.gz file
in the following location:
C:\Program Files(x86)\Embarcadero\
Thanks for reading! Studio\19.0\PAServer
2. Unpack the LinuxPAServer19.0.tar.gz
file.
V
X CL
M
F
FNC
V
X CL
M
F
FNC
PASCON 2017
19 SEPTEMBER
TFDMEMTABLE USABILITY
Whether you’re looking for a single-file, single-
user database or if you need to store some
application specific data but don't want to use a
larger equivalent or use something like the
registry or even an Ini-File, this sort of Data
registering table is just what you need:
it’s easy to handle and maintain.
You do not need to invest in database
knowledge to be able to do what you want:
register some details, make them protected
(binary) – if necessary, or quickly and easily create a
list of files, articles or even save pictures.
Of course there are lots of other useful
things to do. In the first episode I explained the
basics for creating a DataSet, so we can repeat
this way of working, though there are some
differences in the FireDac MemTable that may
lead to faster data access and display.
It might be of interest that even a larger
database could be designed by using this as a
model design.
One of the great features is FireDAC’s ability to
use SQL. That is really quite an improvement To find the FDMemTable we are looking for select
relative to ClientDataSets: you can only filter the FireDAC tab or simply type in the Tool
them and from experience I noticed that then it is Palette-search box “memtable”. If you want to see
difficult to get the result you want. what is available on the FireDAC-tabs: first choose
If you want to have a deep dive into more FireDAC.
knowledge about this you could try Cary
Jensen: he's writing a book about FireDac and
you probably can study that. He also created a
video about this subject:
https://www.youtube.com/
watch?v=iNgHJakYWkU
Since we are interested in the basics of
this all: here you see the vast number of
components that are available
for FireDAC as a whole.
I think the founder and author Dmitry
Arefiev has done a great Job. (He even made it
possible to leave the BDE and update your BDE
projects to the future).
Now that both FDMemTables have their structure I opened the DataEditor and added a line with
(of course both equal) we must go into details. some trial data. Now before we go any further we
Already here is a difference from the must run the program:
ClientDataSet: You can now edit the DataSet We want to make sure that ClientDataSet1 will
which is great. contain all the data.
Here you can (without running your project) add To take care of that we need to run the application and
your real data, which means you don’t have to then push the Button “VIEW ARCHIVE ON/OFF” .
run the program and test it. After that Click the button “RESTORE FROM ARCHIVE”
You can simply do it here. That is a great advantage. to make sure you haven't lost any details.
If the field sizes you have created are too big The Dataset has now all the content and all we need to
you can widen the editor and then correct the do is write that to the FDMemTable.
field’s width, so you can view all the fields you There is as we know no other solution than to
have created, and can make the width of the add all the content, because we could not read the
editor much smaller. Just try. ClientDataSet file where it all was registered.
Of course you can also clear all content. We will have to create a procedure for that and
Close the editor and we will find out what this after that save the file into the new files we are
looks like in your project. going to create for the FDMemTable.
DateTimePicker1.DateTime := (Now);
DateTimePicker2.DateTime := (Now)
end;
After running the program. I found out that even as Figure 17: The storage does not work with
this format
the file specification is given as xml files,
it creates an error: Invalid binary storage
To make sure the other file extensions work I have Figure 18: This is the actual list of the file
tried to save and load them each separately. formats that actualy are available but do
Again an invalid binary storage when I chose not work. Probably an unsoved item
.json. I didn’t find any documentation on this. So the
upshot is that you can only use .fds . (A binary file).
This means that the file extension for saveto and read
must be changed for the OnCreate:
FDMemTable1.LoadFromFile(ExtractFilePath(Application.exename)+'ToDoList_FD_UTF.fds');
FDMemTable2.LoadFromFile(ExtractFilePath(Application.exename)+'ToDoList_FD_BackUpUTF.fds');
TField.DataType = ftAutoInc
(TFDAutoIncField) when dtInt32 or
dtUInt32;
otherwise one of the numeric ftXxxx data types;
TField.Required = False;
TField.ReadOnly = True;
TField.ProviderFlags = [pfInWhere],
or [pfInWhere, pfInKey] when the column is
part of a primary key.
end;
Now we have the data inserted to the new Dataset. In the next article I will give you the details
That means we can have a look at how to about KBMMW.
create SQL statements on the DataSet. I made contact with Kim Madsen and he told me
Before you do that you should delete all the testing the following:
items like Button1 and the extra grid. Eventually you
can exclude the procedure of the button and remove the You can use both filter and SQL.
ClientDataSets etc. But be cautious, make a copy of You would use TkbmMemSQL if you want to query
your project!!! using SQL.
Simply deleting the ClientDataSets is an option. In FPC which does not have any filter TDataset
The IDE will warn you many times, and then you can mechanism, kbmMemTable automatically uses
alter the statements of whatever is wrong. SQL syntax (WHERE clause) for filtering
If you want to do it carefully you could iterate through You can do regular SQL queries on kbmMemTable.
the Code and call F3, so you will find all the instances It doesn’t support joins though, but otherwise
without having them deleted. most other SELECT, INSERT, DELETE and UPDATE
statements
NOW TO THE PART OF THE SQL STATEMENTS.
I have tried to use the local SQL.
But you have to install SQLLite for it and there is no
way to use the SQLLite on the FDMemTable itself.
You have to create or use a real - even if it is only small -
Database. You can query that one and put the results
into your FDMemtable
So we are left with completing the filter method .
And I have said enough about that in the first project.
So the SQL on your FDMemTable does not work.
You can import from a database but not the other way
around.
COMPONENTS
DEVELOPERS 4 Android logcat tool
People developing for Android know that checking it’s logfiles is a must, debugging any new
application. One can do it directly using adb.exe with the logcat command, but it outputs data to the
console and is generally not really very useful due to the typical high amount of data produced by it.
Android Studio contains a log viewer application, but alas that requires installation of the complete
Android Studio, which perhaps is overkill to check the contents of the log.
Some 3rdparty options also exists. Unfortunately they typically require Java or .Net to be available, and
most of them are trials or limited commercials.
So when nothing exists that fulfills the requirements, the option is to make it yourself.
Thats what I did. May I present kbmLogCat!
su
sb
sc
r
F ibe at h
R ttps:
E //com
E pon
e
T nts4de
O velop
O ers.w
L ordpre
s. s
27
Issue Nr 2 2017 BLAISE PASCAL MAGAZINE
co 27
Issue Nr 3 2017 BLAISE PASCAL MAGAZINE
m
/
28 Issue Nr 2 2017 BLAISE PASCAL MAGAZINE
BLUETOOTH CONTROLLED ROBOT WITH VISUINO AND DELPH PAGE 1 / 8
PART 2 - DELPHI BY BOIAN MITOV
starter expert
- + We will need 4 buttons to control the movement
of the robot. In the search box of the “Tool
In the previous article, I showed you how you Palette” type “button”. Select the TButton
can use Visuino to program Elegoo Robot to be component:
controlled over Bluetooth Classic. In this article
I will show you how you can use Delphi to write
Android app to control the Robot. The App
should also work on iOS, MAC OS, and Windows
systems with Bluetooth.
Start RAD Studio, and create a new Multi-Device
Application Delphi. Select Blank application:
-
+
First we need to add a TBluetooth to connect to
the “HC-06” module. In the search box of the “Tool
Palette” type “blue”. Select the TBluetooth
component:
29
BLUETOOTH CONTROLLED ROBOT WITH VISUINO AND DELPHI - +
PART 2 - DELPHI PAGE 2 / 8
We will add a label to display a message while In the event handler call
connecting to the HC-06 module. “Bluetooth1.DiscoverDevices” and specify some
In the search box of the “Tool Palette” type time interval, as example 5 seconds:
“label”. Select the TLabel component:
procedure TForm1.FormCreate(Sender: TObject);
begin
Bluetooth1.DiscoverDevices( 5000 );
end;
Issue Nr 3 31
BLUETOOTH CONTROLLED ROBOT WITH VISUINO AND DELPHI - +
PART 2 - DELPHI PAGE 4 / 8
Switch to the Form Editor. In the “Object Inspector” In the event handler write the following code to
select the OnMouseUp and double click on the value send the “r” character through the socket:
editor to generate event handler: procedure TForm1.Button3MouseDown(Sender: TObject;
Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
FSocket.SendData( TEncoding.UTF8.GetBytes( 'r' ));
end;
Switch to the Form Editor and select the Button3 In the event handler write the following code to
(Right) component. In the “Object Inspector” select send the “R” character through the socket:
the OnMouseDown and double click on the value procedure TForm1.Button3MouseUp(Sender: Tobject;
editor to generate event handler: Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
FSocket.SendData( TEncoding.UTF8.GetBytes( 'R' ));
end;
Switch to the Form Editor. In the “Object In the RAD Studio IDE press the Ctrl+Shift+F11 on
Inspector” select the OnMouseUp and double click the keyboard to open the Project Options dialog.
on the value editor to generate event handler: In the “Project Options Dialog” select the “User
Permissions”. For target select “All configurations
– Android platform”. Set the “Bluetooth”, and
“Bluetooth admin” permissions to True:
The app will try to connect to the Robot: Now you can use the 4 buttons to drive the robot.
Pressing the Forward button will tell the robot to
move forward:
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes,
System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics,
FMX.Dialogs, System.Bluetooth, System.Bluetooth.Components,
FMX.Controls.Presentation, FMX.StdCtrls;
type
TForm1 = class(TForm)
Bluetooth1: TBluetooth;
Button1: TButton;
Button2: TButton;
Button4: TButton;
Button3: TButton;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure Bluetooth1DiscoveryEnd(const Sender: TObject;
const ADeviceList: TBluetoothDeviceList);
Pressing the Right button will tell procedure FormDestroy(Sender: TObject);
the robot to turn Right: procedure Button1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Button1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Button2MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Button2MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Button3MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Button3MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Button4MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
procedure Button4MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
private
FSocket : TBluetoothSocket;
end;
var
Form1: TForm1;
And pressing the Back button will
tell the robot to move backward: implementation
{$R *.fmx}
Now… this is all nice and easy.. but what if I want to This is here where kbmMWNullable and
search for all people with a specific age? TkbmMWDateTime comes into play. Regular native
var types like string, integer etc. can’t hold the value Null,
p:TObjectList<TPerson>; and they also can’t hold any information about if their
... value has been changed. But if you wrap the types in
p:=orm.QueryList<TPerson>(['age'],[65]); kbmMWNullable, or use TkbmMWDateTime instead
of TDateTime, you suddenly have those abilities
Now only people of the age 65 is returned. which the framework can take advantage of.
Looking at the FullName property in our
var
p:TObjectList<TPerson>; TPerson object, you can see that it was wrapped in
... kbmMWNullable. That means we at any time can
p:=orm.QueryList<TPerson>(['age'],[65],mwoqoGT); request information about if it’s null, or modified like
this: (next page)
Now only people older than 65 is returned. If you only if p.FullName.IsNull then
wanted one of them you could have used ...
Query<TPerson> … instead of QueryList. if p.FullName.Modified then
You can specify multiple fields and values in your ...
search this way, but all are controlled by the given And we can clear its modified flag anytime if we need to
operator (default mwoqoEQ = equal). p.FullName.Modified:=False;
If you just wanted a count
And we can set its value to null
var i:integer;
p.FullName.Clear;
...
i:=orm.QueryCount<TPerson>(['age'],[65],mwoqoGT);
By default, all properties are marked as non modified
You can use various aggregates, like QueryCount, when they have been returned as a result of one of the
QuerySum, QueryAvg, QueryMin, QueryMax Query methods.
and QueryStdDev or the generic QueryAggregate This way, Persist and other UpdateIfChanged
method. Or if you really want the data as a TDataset / InsertIfChanged methods, will only resolve data
instead, you can do back to the data storage if any changes have been made
var to the data in the object. If you are using regular non
ds:TDataset; wrapped properties, then kbmMW ORM can only
... assume that the data may have changed, and it will
ds:=orm.QueryDataset<TPerson>(['age'],[65],mwoqoGT); attempt to update all provided objects regardless.
Most of the time, you really want to make more
This will return a dataset to, which you can use as any complex searches when querying for data. We have
other dataset. The dataset is however not directly chosen to build in a uniform SQL based query language
connected with the data storage behind, so changes in into kbmMW ORM which is not only easy to understand
the dataset needs to be either resolved using kbmMW’s for most, but is automatically translated on the fly, to
regular resolving mechanism (which is nice, but not the query language used by the data storage.
ORM), or by converting the dataset back to a set of
var
objects you can persist. o:TObjectList<TPerson>;
The field name given is the name your property is ...
known as. So if you would have searched on a name, o:=orm.QueryList<TPerson>('SELECT * FROM
you would have specified ‘FullName’ as the field name, uData.TPerson WHERE age>65 ORDER BY
rather than ‘name’ as it’s given in the data storage. FullName'));
With kbmMW ORM you must generally always refer
to the object’s property names.
40 COMPONENTS
DEVELOPERS 4 Issue Nr 3 2017 BLAISE PASCAL MAGAZINE
KBMMW ORM (OBJECT RELATIONAL MODEL) PAGE 5/7 COMPONENTS
DEVELOPERS 4
[kbmMW_Service('name:ORMDEMO')]
TkbmMWCustomService2 = class(TkbmMWCustomSmartService)
public
// This lists stores changes (but not removed items) in the list of persons.
[kbmMW_Method]
procedure StorePersons(const APersons:TObjectList<TPerson>);
// This lists deletes persons in the list.
[kbmMW_Method]
procedure DeletePersons(const APersons:TObjectList<TPerson>);
// This method returns a list of persons.
[kbmMW_Method]
function GetPersons:TObjectList<TPerson>;
end;
implementation
uses
Unit1;
{$R *.dfm}
function TkbmMWCustomService2.GetPersons:TObjectList<TPerson>;
begin // Return all persons (people).
Result:=Form1.orm.QueryList<TPerson>;
end;
initialization
TkbmMWRTTI.EnableRTTI(TkbmMWCustomService2);
This is really really simple isn’t it? Only business logic code and nothing else.
In the FormCreate event handler of the main form Form1, where we have also put the orm creation, we
add // Register all services automatically.
// Services will only be autoregistered if they have a kbmMW_Service attribute.
server.AutoRegisterServices;
In the event handler of the Listen button on Form1 we write server.Active:=not server.Active;
That’s all!
NOW TO THE CLIENT.
function GetID:string;
procedure SetID(AValue:string);
function GetName:string;
procedure SetName(AValue:string);
function GetAddress:string;
procedure SetAddress(AValue:string);
function GetAge:integer;
procedure SetAge(AValue:integer);
published
[kbmMW_Field('name:id, primary:true, generator:shortGuid',ftString,40)]
property ID:kbmMWNullable<string> read FID write FID;
[kbmMW_Field('name:name',ftWideString,50)]
property FullName:kbmMWNullable<string> read FName write FName;
[kbmMW_Field('name:address',ftWideString,50)]
property Address:kbmMWNullable<string> read FAddress write FAddress;
[kbmMW_Field('name:age',ftInteger)]
property Age:kbmMWNullable<integer> read FAge write FAge;
{ One way of livebinding to kbmMWNullable or kbmMWDateTime fields is to add an alternative access to them.
LiveBinding only supports basic data types. Remember to kbmMW_Ignore the properties to tell kbmMW not to attempt to
stream them.
As kbmMWNullable and TkbmMWDateTime values retain information about nullability and modification,
it may be preferred to use these types over regular string, int, TDateTime etc. types.
If the number of properties of such types is large, writing many alternative setters and getters may become tedious.
Instead you can (as this sample shows in unit1.pas), convert the objects to a dataset (a proxy) which is easy to databind with.}
[kbmMW_Ignore]
property LBID:string read GetID write SetID;
[kbmMW_Ignore]
property LBName:string read GetName write SetName;
[kbmMW_Ignore]
property LBAddress:string read GetAddress write SetAddress;
[kbmMW_Ignore]
property LBAge:integer read GetAge write SetAge;
end;
42 COMPONENTS
DEVELOPERS 4 Issue Nr 3 2017 BLAISE PASCAL MAGAZINE
KBMMW ORM (OBJECT RELATIONAL MODEL) PAGE 7/7 COMPONENTS
DEVELOPERS 4
Notice that the extra properties are not wrapped with And in the Get Persons buttons event handler put
kbmMWNullable. They have also been marked with orm.ToDataset<TPerson>(mtPersons,GetPersons,true);
the attribute kbmMW_Ignore, to tell the kbmMW
Now the grid is populated with data, and you can work
framework that they are not to be marshalled
the data in any way you want. As the data is available
(serialized).
in a kbmMemTable , sorting, searching , filtering and
Their only purpose is to allow for easy object live
other things are very easy to do.
binding and is basically boiler plate code that we want
At some point, the data may have been modified,
to avoid.
and it should be sent to the server for updating.
There is another way that require less typing, and
In the Store Persons buttons event handler put
converting the objects to a dataset and back again
when needed. var
kbmMW ORM provides easy support for that. persons:TObjectList<TPerson>;
begin
In this case, we have already put a mtPerson
persons:=orm.ListFromDataset<TPerson>(mtPersons,[
kbmMemTable on the form at designtime, to make it
usDeleted]);
easy to do designtime live binding. if persons.Count>0 then
All we need is to populate it with some data. This DeletePersons(persons)
happens in the Get persons buttons eventhandler. else
But first we need to prepare the smart client. persons.Free;
This can happen in the Connect buttons event handler. persons:=orm.ListFromDataset<TPerson>(mtPersons);
// A form field StorePersons(persons);
c:IkbmMWSmartClient; end;
...
// Connect button's event handler
c:=TkbmMWSmartRemoteClientFactory.GetClient(Transport,'ORMDEMO');
COMPONENTS
DEVELOPERS 4 43
Digging for wORMs
kbmMW Object Relational Model
kbmMW Smart services
kbmMW Smart clients
COMPONENTS
DEVELOPERS 4 DX
EESB, SOA,MoM, EAI TOOLS FOR INTELLIGENT SOLUTIONS. kbmMW IS THE PREMIERE N-TIER PRODUCT FOR DELPHI /
C++BUILDER BDS DEVELOPMENT FRAMEWORK FOR WIN 32 / 64, .NET AND LINUX WITH CLIENTS RESIDING ON WIN32 / 64, .NET, LINUX, UNIX
MAINFRAMES, MINIS, EMBEDDED DEVICES, SMART PHONES AND TABLETS.