Sep 01
My yesterday post about this busy time, have raised some interest about DORM, the Delphi ORM.
So, also if I still haven’t released any files, wish to expose some internals about DORM.
DORM is an implementation of the DataMapper design pattern written having Hibernate in mind.
It’s completely unit tested and have the following features:
- External file mapping. (JSON format)
- Persistence ignorance (every TObject can be persisted)
- Support for One-One and One-Many relations (still no many-many)
- Support for LazyLoading (you can enable§/disable lazyloading by file or by code by per-basis needs)
- Support for IdentityMap
- Support for custom “finder” (you can still use complex SQL if you want)
- Complete support for CRUD
- Transactions
- Built in logging system to log *EVERY* sql or action performed by the framework
- Opened to multiple data access strategies (interfaced based, not inheritance based) for use with different database (now I’ve developed the firebird one using DBX)
- Caching for RTTI (the TSession object have a single TRttiContext holding ALL metadata)
Code is still under heavely development.
Those are 2 test-method to show the use of DORM:
-
TPerson = class(TObject)
-
…
-
property Phones: TdormCollection…. //implements IList
-
end;
-
-
TPhone = classs(TObject)
-
…
-
end;
-
-
//and now the unit tests
-
-
procedure TTestDORMHasMany.Setup;
-
begin
-
Session := TSession.Create;
-
Session.Configure(TStreamReader.Create('dorm.conf'));
-
end;
-
-
procedure TTestDORMHasMany.TearDown;
-
begin
-
Session.Free;
-
end;
-
-
procedure TTestDORMHasMany.TestHasManyLazyLoad;
-
var
-
p: TPerson;
-
t: TPhone;
-
guid: string;
-
begin
-
p := TPerson.NewPersona; //static method. Return a fully populated TPerson object
-
try
-
t := TPhone.Create;
-
p.Phones.Add(t);
-
Session.Save(p);
-
guid := p.guid; //GUIDs, or other PK types, are generated automagically by DORM. Obviously there is a specific class loaded to do this specified in the dorm.conf file)
-
finally
-
Session.Commit;
-
end;
-
Session.StartTransaction;
-
-
// Test with lazy load ON
-
Session.SetLazyLoadFor(TypeInfo(TPerson), 'Phones', true);
-
p := Session.Load(TypeInfo(TPerson), guid) as TPerson;
-
try
-
CheckEquals(0, p.Phones.Count);
-
finally
-
Session.Commit;
-
end;
-
-
Session.StartTransaction;
-
// Test with lazy load OFF
-
Session.SetLazyLoadFor(TypeInfo(TPerson), 'Phones', false);
-
p := Session.Load(TypeInfo(TPerson), guid) as TPerson; // Without commit, AV becouse IdentityMap doesn't work properly
-
try
-
CheckEquals(1, p.Phones.Count); // Child objects are loaded
-
finally
-
Session.Commit;
-
end;
-
end;
-
-
procedure TTestDORMHasMany.TestLoadHasMany;
-
var
-
list: IList;
-
t, t1: TPhone;
-
p: TPerson;
-
guid: string;
-
begin
-
p := TPerson.NewPersona; //static method. Return a fully populated TPerson object
-
try
-
t := TPhone.Create;
-
t.Numero := '555-7765123';
-
t.Kind := 'Casa';
-
p.Phones.Add(t);
-
-
t1 := TPhone.Create;
-
t1.Number := '555-7765123';
-
t1.Kind := 'Casa';
-
p.Phones.Add(t1);
-
Session.Save(p); // save Person and Phones
-
guid := p.guid;
-
finally
-
Session.Commit;
-
end;
-
-
Session.StartTransaction;
-
p := Session.Load(TypeInfo(TPerson), guid) as TPerson;
-
try
-
CheckEquals(2, p.Phones.Count);
-
finally
-
Session.Commit;
-
end;
-
end;
Mapping, contained in a file called “dorm.conf”, is similar to the following:
-
{
-
"persistence": {
-
"database_adapter": "dorm.adapter.Firebird.TFirebirdPersistStrategy",
-
"database_connection_string":"127.0.0.1:C:\\MyProjects\\DORM\\experiments\\dorm.fdb",
-
"username": "sysdba",
-
"password":"masterkey"
-
},
-
"config": {
-
"package": "dorm.bo.Person",
-
"logger_class_name": "dorm.loggers.FileLog.TdormFileLog"
-
},
-
"mapping":
-
{
-
"TPerson":
-
{
-
"table": "people",
-
"id": {"name":"guid", "field":"guid", "field_type":"string", "size": 100, "default_value": ""},
-
"fields":[
-
{"name":"firstname", "field":"first_name", "field_type":"string", "size": 100},
-
{"name":"lastname", "field":"last_name", "field_type":"string", "size": 100},
-
{"name":"age", "field":"age", "field_type":"integer"},
-
{"name":"borndate", "field":"born_date", "field_type":"date"}
-
],
-
"has_many":[{
-
"name": "Phones",
-
"class_name":"TPhone",
-
"child_field_name":"guid_person",
-
"lazy_load": false
-
}],
-
"has_one": {
-
"name": "car",
-
"class_name":"TCar",
-
"child_field_name":"guid_person"
-
}
-
},
-
"TPhone":
-
{
-
"table": "phones",
-
"id": {"name":"guid", "field":"guid", "field_type":"string", "size": 100, "default_value": ""},
-
"fields":[
-
{"name":"number", "field":"number", "field_type":"string", "size": 100},
-
{"name":"kind", "field":"kind", "field_type":"string", "size": 100},
-
{"name":"guid_person", "field":"guid_person", "field_type":"string", "size": 100}
-
]
-
}
-
}
-
}
The PODO (Plain Old Delphi Objects) can be binded to the VCL controls with a set of MediatingView (Model-GUI-Mediator Pattern) with an Observer mechanism to mantain things in synch.
Any comments? Someone interested?
Jun 12
Also this year I was at Delphi Day with many others bit Time co-workers.
This year I did a speech about Continuous Integration and Unit Test with Delphi.
I showed some of the methodologies and tools that we used in bitTime for long and for whom we also offer training.
Many Delphi programmers and companies should consider adoption of this tecnique for do more and better with the same effort (and the same money).
The event was interesting and was also an opportunity to meet some of the Italian Delphi programmers with which to meet and exchange ideas.
Marco wrote a nice summary of the event in his blog.
May 17
My interview at PHP Day 2010 has been published on php.html.it.
This is the link to the article.
This is the video on youtube.
The interview is only in italian language.
May 13
This time a very fast post…
For the third year in a row, I will talk at the PHP Day in Italy organized, as usual, by the friends of GRUSP.
My speech will be about Continuous Integration and PHP, with a little introduction to Hudson.
This is the main conference website.
This is my speaker/speech page.
Enough fast?
May 06
Some month ago I wrote a simple article about an MVP variant called PassiveView.
That example was very simple. Now I’ll present a more “advanced” version of that example.
The main problem with first example was the following method:
-
procedure TfrmCalculatorView.FormCreate(Sender: TObject);
-
begin
-
//Link controls with related interface
-
IFirstOperand := TGUIEdit.Create(EditFirstOp);
-
ISecondOperand := TGUIEdit.Create(EditSecondOp);
-
ICalcResult := TGUIEdit.Create(EditResult);
-
IOperators := TGUISelectableList.Create(ComboOperators);
-
IError := TGUIEdit.Create(EditError);
-
-
//link view and presenter
-
FPresenter := TCalculatorPresenter.Create(Self); //<<– THIS IS THE BAD LINE
-
end;
The “BAD” line links the View with the Presenter but it’s in the view code, so this is meaning that View KNOWS the presenter… and this is not a good thing becouse the view is not so “passive”.
In a more advanced (and complex) version the View should be completely ignorant about the class that implement the presenter and the service.
In the main dpr file now the code now looks like the following.
-
var
-
MainPresenter: ICalculatorPresenter;
-
CalculatorView: TForm;
-
begin
-
Application.Initialize;
-
Application.MainFormOnTaskbar := True;
-
//SETUP THE MAIN APPLICATION FORM FOR VCL PURPOSE
-
Application.CreateForm(TfrmCalculatorView, CalculatorView);
-
//SETUP ALL THE LINKS BETWEEN THE MVP TRIAD
-
MainPresenter := TCalculatorPresenter.Create(CalculatorView as ICalculatorView, TCalculatorService.Create);
-
//LETS START!
-
Application.Run;
-
end.
Now the presenter take care about all links between the MVP triad.
-
constructor TCalculatorPresenter.Create(CalculatorView: ICalculatorView;
-
CalculatorService: ICalculatorService);
-
begin
-
inherited Create;
-
FCalculatorService := CalculatorService;
-
FCalculatorView := CalculatorView;
-
FCalculatorView.SetPresenter(self);
-
InitView; //does the links
-
end;
There is another addition to the previous example. Now there is only one constructor in the presenter, and using dependency injection take 2 interface for the view and the service.
-
constructor Create(CalculatorView: ICalculatorView; CalculatorService: ICalculatorService);
Another plus is the possibility to open the same form a number of times without change the code for create it.
This is the GUI for this simple application.

3 instance of the same view with different presenter and service
As bonus, unit tests and mock object arent changed.
As usual the source code is here.
Apr 18
Yes, I’ve just committed last version of DelphiStompClient, a STOMP client for Delphi 2010 (should works in 2007 and 2009 too) and FreePascal 2.4.
DelphiStompClient has been tested with Apache ActiveMQ 5.2 and 5.3 but should works with every STOMP compliant server.
You can find the code here, but if you need more info with example and other stuff, read entire article.
The FreePascal version is mantained by Daniel Gaspary.
Apr 08
I’ve been very busy lately with some Delphi related events here in Italy
so I havent too much time for my personal open source project. But now I’ve released new beta version for DSFC.
This release address a problem with the cypher filters.
Cypher filters arent completely compatible with the previuous version, so you need to rebuild the server and the client if you want to use this new version of DSFC.
There are one known issue:
- Cannot have multiple connections in a client with the same filters but different cipher keys connecting to different servers.
Some users asked to me a little tutorial to setup a demo application with the cypher filters. Now there is a project group illustrating how do it.
There is also a new net speed test.
I’m waiting for some testers feedback comment before of 1.1 version.
As usual you can find the source code in the GoogleCode SVN repository
http://dsfc.googlecode.com
Happy testing!
Jan 06
As wikipedia says:
“Dependency injection (DI) in computer programming refers to the process of supplying an external dependency to a software component. It is a specific form of inversion of control where the concern being inverted is the process of obtaining the needed dependency. The term was first coined by Martin Fowler to describe the mechanism more clearly.”
Many of us have already read this historical article from Martin Fowler about dependency injection pattern, but actually there isn’t a real framework for implement dependency injection in Delphi.
There are already the following implementation for DI in Delphi
- Emballo (work with pre-D2010 too, but the implementation require changes in the service classes. I really hate it)
- Delphi Spring Framework (very nice, but still not realeased)
So, I decided to write my own simple DI framework.
You can find the code at google code project here: http://code.google.com/p/delphidicontainer/
This is the first public version and come with sample, documentation and unit tests.
Folow some sample code.
-
program Test01;
-
{$APPTYPE CONSOLE}
-
-
uses
-
SysUtils,
-
DIContainer in '..\..\src\DIContainer.pas',
-
ServiceTestObjectsU in '..\..\UnitTest\ServiceTestObjectsU.pas';
-
-
var
-
DIContainer: TDIContainer;
-
s1: TService1;
-
s2: TService2;
-
s3: TService3;
-
s6: TService6;
-
s7: TService7;
-
begin
-
try
-
DIContainer := TDIContainer.Create;
-
try
-
// AddComponent with TClass with and InitType = Singleton
-
DIContainer.AddComponent(TService1, TDIContainerInitType.Singleton);
-
// AddComponent with QualifiedName and InitType = Singleton
-
DIContainer.AddComponent('ServiceTestObjectsU.TService2',
-
TDIContainerInitType.Singleton);
-
// AddComponent with QualifiedName and InitType = CreateNewInstance
-
DIContainer.AddComponent('ServiceTestObjectsU.TService3',
-
TDIContainerInitType.CreateNewInstance);
-
-
// GetComponent with QualifiedName
-
s1 := DIContainer.GetComponent('ServiceTestObjectsU.TService1')
-
as TService1;
-
s1.Message := 'I''m the first message';
-
WriteLn(s1.Message);
-
-
// GetComponent with TClass
-
s2 := DIContainer.GetComponent(TService2) as TService2;
-
s2.Message := 'I''m the second message';
-
WriteLn(s2.Message);
-
-
// GetComponent with a dependent service (TService3 depends upon TService1 and TService2)
-
s3 := DIContainer.GetComponent('ServiceTestObjectsU.TService3')
-
as TService3;
-
WriteLn(s3.GetCompoundMessage);
-
// s3 is not created as Singleton, so after use it I must free it
-
s3.Free;
-
-
// AddComponent with QualifiedClassName, a custom initializer, an alias.
-
// Component will be created as singleton (single instance managed by Container)
-
-
DIContainer.AddComponent(DIContainerUtils.GetQualifiedClassName
-
(TService6),
-
function: TObject
-
begin
-
Result := TService6.Create(DIContainer.Get(TService1) as TService1,DIContainer.Get(TService1) as TService1);
-
end,
-
'srv6',
-
TDIContainerInitType.Singleton);
-
-
s6 := DIContainer.Get('srv6') as TService6;
-
WriteLn(s6.ToString);
-
s6 := DIContainer.Get('srv6') as TService6;
-
WriteLn(s6.ToString);
-
-
// AddComponent with QualifiedClassName, a custom initializer, an alias.
-
// Component will be created as singleton (single instance managed by Container)
-
DIContainer.AddComponent(DIContainerUtils.GetQualifiedClassName
-
(TService7),
-
function: TObject
-
begin
-
Result := TService7.Create(DIContainer.Get(TService1) as TService1,DIContainer.Get(TService1) as TService1);
-
end,
-
'srv7intf',
-
TDIContainerInitType.Singleton);
-
-
s7 := DIContainer.Get('srv7intf') as TService7;
-
WriteLn(s7.ToString);
-
finally
-
DIContainer.Free;
-
end;
-
except
-
on E: Exception do
-
WriteLn(E.ClassName, E.Message);
-
end;
-
readln;
-
end.
DelphiDIContainer also handle constructor injection with automatic dependency resolution for service.
Enjoy and stay tuned.
Jan 05
DataSnap Filters Compendium has been used by many users so far. I’m planning to do some change in hash filters (that do not will affect already written code) but until new version will ready I have 3 nice news about DSFC:
1. Now the project is on Google Code.
2. Franco Perana, the first contributor to the project, has fixed and optimized the LZO filter.
3. Have you used DSFC and do you want to see your application listed in the “Application Using DSFC”? If yes, leave a comment on this post with some details of your application amd I’ll add it in the proper list.
Nov 25
During last ITDevCon in Verona (ITALY) I talked in a session with title “Marshal and UnMarshal in Delphi 2010″ (we have published some photos about this great conference here).
In the first part of the session I’ve talked about JSON and many attendees were very interested in JSON and his application in everyday programming. I’m a real JSON fan so I decided to write this post about JSON support in Delphi2010. In my session I talked about serialization and deserialization, in this post I’ll show an example of code to do some JSON generation.
-
program JsonTypes;
-
{$APPTYPE CONSOLE}
-
uses
-
SysUtils,
-
IOUtils,
-
DBXJSON;
-
var
-
Obj, RestoredObject, JSON: TJSONObject;
-
arr: TJSONArray;
-
begin
-
json := TJSONObject.Create;
-
try
-
WriteLn('Empty JSON Object: ' + sLineBreak,json.ToString);
-
json.AddPair('FirstName', TJSONString.Create('Daniele'));
-
WriteLn('Simple JSON Object with property: ' + sLineBreak, json.ToString);
-
json.AddPair(TJSONPair.Create('LastName', 'Teti'));
-
WriteLn('Simple JSON Object with 2 property: ' + sLineBreak, json.ToString);
-
arr := TJSONArray.Create;
-
arr.Add(9871238712);
-
arr.Add('peter.parker@bittime.it');
-
arr.Add('Via Roma, 12');
-
json.AddPair(TJSONPair.Create('Contacts', arr));
-
WriteLn('JSON Object with a property array: ' + sLineBreak, json.ToString);
-
obj := TJSONObject.Create;
-
obj.AddPair(TJSONPair.Create('John','Doe'));
-
json.AddPair('MyUndefinedPersonObject', obj);
-
WriteLn('JSON Object with a nested Object: ' + sLineBreak,json.ToString);
-
Write('Writing JSON object to file [jsonfile]…');
-
TFile.WriteAllText('jsonfile',json.ToString);
-
WriteLn('DONE!');
-
RestoredObject := TJSONObject.ParseJSONValue(
-
TEncoding.ASCII.GetBytes(TFile.ReadAllText('jsonfile')),0
-
) as TJSONObject;
-
WriteLn('Readed JSON object from file: ' + sLineBreak, RestoredObject.ToString);
-
finally
-
json.Free; //Every contained object is automatically freed
-
end;
-
readln; //If you want to see the output while in Delphi IDE
-
end.
JSON is a lightweight data-interchange format and in Delphi 2010 you have full support for it with the built-in DBXJSON unit.
Enjoy!
Recent Comments