You are not logged in.
Pages: 1
I have a simple question, but it is very important to me. Does TOrm have to be derived from TSynPersistent? Or could it also be this inheritance order:
{$M+} TSynCustomPersistent = class(TObject) {$M-}
TOrm = class(TSynCustomPersistent)
TSynPersistent = class(TSynCustomPersistent)
Descendants of TSynCustomPersistent can be used with interfaces. With TSynPersistent comes the break.
The background of my question is that I use direct data binding for local data objects. Since TSynPersistent does not work with interfaces, I can no longer use the following:
TSQLBaseRecord = class abstract(TSQLRecord, IInterface, INotifyPropertyChangedEx)
...
TSQLArticle = class(TSQLBaseRecord)
...
published
property Name: RawUtf8
read FName write SetName;
end;
In the form, binding between input elements and data object is then done with a simple statement.
edtName.AddBinding(SourceObject, TSQLArticle.OPN.Name, BindingGroup);
This approach is very practical and a lot of existing controls are prepared for it.
With best regards
Thomas
Last edited by tbo (2021-04-06 08:50:05)
Offline
I don't understand your use case of interfaces with such a non TInterfacedObject type.
It is unsafe and never a good idea to mix the memory models.
If TOrm is an interfaced class, then all existing code requiring Create + Free would break with _AddRef/_Release memory management.
What is your exact use case?
Offline
What is your exact use case?
I use Spring4D and DSharp by Stefan Glienke. The binding between input element and data object is done with the bindings classes from DSharp. To make it a bit more understandable I have to write some source code. I hope it is still ok.
I put together a framework for myself that combines various free and commercial component collections (Spring4D, DSharp, DevExpress, etc.). The goal is to connect form and data object easily, quickly and yet still flexibly.
To make an object bindable with DSharp, I need to write the following:
TSQLBaseRecord = class abstract(TSQLRecord, IInterface, INotifyPropertyChangedEx)
private
FPropertyChanged: Event<TPropertyChangedEvent>;
function GetOnPropertyChanged: IEvent<TPropertyChangedEvent>;
function QueryInterface(const pmcIID: TGUID; out pmoObject): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
protected
procedure NotifyOfPropertyChange(const pmcPropertyName: String; pmUpdateTrigger: TUpdateTrigger = utPropertyChanged);
end;
Definition of the data object:
TSQLArticle = class(TSQLBaseRecord)
strict private
type
TOPNRec = record
const Name = 'Name';
end;
private
FName: RawUtf8;
procedure SetName(const pmcValue: RawUtf8);
public
const OPN: TOPNRec = ();
published
property Name: RawUtf8
read FName write SetName;
end;
procedure TSQLArticle.SetName(const pmcValue: RawUtf8);
begin
FName := pmValue;
NotifyOfPropertyChange(OPN.Name);
end;
The creation of the edit form:
TfrmArticle = class(TBaseOrmRecordEditForm)
edtName: TcxTextEdit;
protected
procedure InitDataBindings; override;
procedure InitFormLayout(const pmcLayoutBuilder: ILayoutBuilder); override;
end;
procedure TfrmArticle.InitDataBindings;
begin
edtName.AddBinding(SourceObject, TSQLArticle.OPN.Name, BindingGroup)
.ValidationRules.Add(TEditMustNotEmptyValRule.Create(TSQLArticle.OPN.Name));
end;
procedure TfrmArticle.InitFormLayout(const pmcLayoutBuilder: ILayoutBuilder);
begin
with pmcLayoutBuilder do
begin
NewGroup;
CreateItem(edtName, 'Name:').CaptionWidth(80).ControlWidth(250);
__EndGroup;
end;
inherited InitFormLayout(pmcLayoutBuilder);
end;
That's all you need to do in a form for data binding and layout. My view is the view of an simple application developer. With the help of this framework, the object inspector and the setting of many properties has largely become obsolete. I hope I could make my intention understandable.
With best regards
Thomas
Offline
Now it makes perfect sense!
I have just introduced the new TObjectWithCustomCreate which is used e.g. as TOrm/TSynPersistent parent class - as a side effect, it will allow TOrm to implement an interface when needed.
Offline
Arnaud, thank you very much for the changes.
My very first still cautiously presented assessment of mORMot2 is: I would like to congratulate you for the excellent work on mORMot2. mORMot1 was fast. But the numbers for mORMot2 are so much better that I want to wait for real operation first, because otherwise I almost can't believe it. It feels noticeably faster. I am very happy with what I see.
I would like to say something more detailed about the background of my change request. I don't know if this is the best solution, but for me it works without problems.
My program uses a local database, but most of the data comes from the server. I describe here what is going on in the client program. To simplify, the data objects (descendant of class TOrm) for the local data gets the binding directly. The data objects representing the data from the server have a lightweight ancestor (descendant of TBom see details later). I achieve this with the following methods.
Data objects are defined with all published properties in a common unit as custom objects.
TOrmCustomArticle = class(TOrmCustomRecord)
...
published
property Name: RawUtf8
read FName write SetName;
end;
The custom object is concretized in a unit for the server and the client.
u_ServerModel.pas
TOrmArticle = class(TOrmCustomArticle)
end;
u_ClientModel.pas
TOrmArticle = class(TOrmCustomArticle)
end;
Different definitions can be used to change the inheritance order. Here is an extract from the BOMCustomRecordH.inc file:
{$IFDEF BOWithBinding}
{$IFDEF BOWithBindingAndORM}
TOrmBaseRecord = class abstract(TOrm, IInterface, INotifyPropertyChangedEx)
{$ELSE}
TBom = class(TObjectWithCustomCreate)
private
FID: TID;
public
constructor Create; override;
property IDValue: TID
read FID write FID;
published
property ID: TID
read FID;
end;
TOrmBaseRecord = class abstract(TBom, IInterface, INotifyPropertyChangedEx)
{$ENDIF}
private
FPropertyChanged: Event<TPropertyChangedEvent>;
...
protected
procedure NotifyOfPropertyChange(const pmcPropertyName: String; pmUpdateTrigger: TUpdateTrigger = utPropertyChanged);
end;
{$ELSE} // ORM pure custom record
TOrmBaseRecord = class abstract(TOrm);
{$ENDIF}
The inheritance structure looks like this:
Data objects representing the local data:
TOrmArticle -> TOrmCustomArticle -> TOrmCustomRecord -> TOrmBaseRecord -> TOrm -> TObjectWithCustomCreate -> TObject
Data objects representing the data from server:
TOrmArticle -> TOrmCustomArticle -> TOrmCustomRecord -> TOrmBaseRecord -> TBom -> TObjectWithCustomCreate -> TObject
Buisiness objects derived from a lightweight class can be equipped with the necessary functions for convenient work in the client. They can be loaded from server via interface based services as follows:
IArticle = interface(IInvokable)
['{2FF6B866-FEE0-454E-A575-29702269285A}']
procedure GetAllItems(out pmoList: TOrmArticleObjArray);
end;
var
service: IArticle;
dataArr: TOrmArticleObjArray;
begin
if dmDB.ServerRestHttp.Resolve(IArticle, service) then
begin
service.GetAllItems(dataArr);
FListView.Presenter.View.ItemsSource := TOrmArticleList.Create(dataArr) as IObjectList;
end;
end;
I hope my explanations were understandable and perhaps helpful for others. And hopefully not too lengthy.
With best regards
Thomas
Offline
Pages: 1