#1 Re: mORMot 1 » XE4 and FireMonkey » 2013-05-10 15:38:04

Hi Arnaud, my architecture is that I have "Model" classes that are linked into both the server and the client. By use of compiler directives, I separate the server-side methods for each class that read and write the fields from/to the database so that means that by definition the client and server always remain in sync and that way I know the streaming will work.
The client apps are self-updating so as soon as I update the server with an interface change or persistent class change then the clients will go into self-update mode. I don't really mind what is used for the serialisation as it is only a means for freezing objects on server and transporting to clients and vice-versa so if Delphi serialisation doesn't work I'll replace with JSON but it needs to work with deep copies as my parent objects hold child objects in TCollections and life is too short to write code to do deep copying when TPersistent does that for you!

I'll take a look at SMS but there is no way I want to move to a scripted or non-native implementation like PhoneGap. That is why I don't do web development either - it is just such a compromise for the developer and the user.

Delphi is such a productive environment and produces the most amazing native Windows applications. The client app that I have produced for NHS in UK is 15MB in size and is very fast, very attractive and would be 50x more costly for me to create as a browser app. Now I can easily port that app to run native on Mac, iPad and in a few short months Android tablet too.

I suppose each to their own but I'm proud of the architecture I've built utilising your client/server persistent object framework and I'm looking forward to bringing that over to FireMonkey and using the same agility I enjoy on Windows now to implementing truely cross-platform versions using FireMonkey now that it seems to be maturing.

Paul

#2 Re: mORMot 1 » XE4 and FireMonkey » 2013-05-09 19:45:08

Hmm, I didn't realise there would be an issue in moving it to run in firemonkey on iOS - I guess that since I'm only using the client/server interface with encryption and object serialisation I ought now move to my own implementation of these as you suggest using Indy, and object serialisation and a standard encryption mechanism.
As I'll never need to share objects with other clients using this interface, I think I'll avoid JSON and just use Delphi's serialisation of a TPersistent if I find that portable between VCL and FMX.
I've found your server processes to be very reliable so I hope my Indy servers will be as stable!
I've found your architecture to be very simple and clean so hopefully I can continue in the same vein.
Thanks for all the great code.
Paul

#3 mORMot 1 » XE4 and FireMonkey » 2013-05-09 10:17:59

paulh
Replies: 56

Hi Arnaud, apologies if this has already been answered - I did a search for XE4 but found no hits.
I'm getting components in place to upgrade my application development environment to Delphi XE4. I tried a compile yesterday and was getting a compilation error in Syncommon. I have a fairly old version of Mormot that I'm running against (about 9 months old) - and was previously compiled with XE2 - has the framework been tested against XE4 and if so do I just need to get a latest version?

Also, now that FireMonkey in XE4 looks usable for me to write an iPad subset of my Windows app, I will want to make calls to Mormot interface from FMX client - has this been tested and anything I need to do different to the VCL implementation?

Regards

Paul

#4 mORMot 1 » Getting the client PC's public IP address in server interface methods » 2012-12-17 17:56:54

paulh
Replies: 1

Hi, I have a server interface with lots of methods that are working great to marshal data to and from the server using JSON serialised objects.
Are there any properties I can use in these server-side methods to determine the calling client's IP details? I can easily look this up on the client side and include in the call to the server if necessary but if that information is already available to me on the server-side it would save me needing to break my interfaces and add to all relevant methods.

#5 Re: mORMot 1 » Browser call to verify connectivity » 2012-12-17 17:48:00

Hi Arnaud, I did debug the code and found the URI it was using: http://myservername.uk.com/root/ERefServerInterface

I found that I would get the following message displayed in the browser when you use this base URL and have connectivity to the server:

{
"ErrorCode":403,
"ErrorText":"Forbidden"
}

So I have told the user site's IT people to use that URI to test connectivity.

However, it would be great if they could do something like: http://myservername.uk.com/root/ERefSer … estConnect
and I displayed a message like:

"Welcome to eRefer web service - this confirms that your PC has connectivity to allow the eRefer application to run successfully."

Is there a way to override the default "{ "ErrorCode":403 ..." error message or implement a function that bypasses authentication for pinging?

Thanks

Paul

#6 mORMot 1 » Browser call to verify connectivity » 2012-12-17 14:12:13

paulh
Replies: 3

Hi Arnaud, we have a site that is failing to connect to our server at Amazon using our client app.
It will be a permissions issue on their side because it is working fine from other user sites. However what I used to do when I had my own web service was to send a message back to browsers to show that they had successfully made contact with the web service even though the normal calls from the client application required the agent to be set correctly in HTTP calls and the packets were encrypted.
Is there a call that our users can make using a browser on the machines that they are trying to run our software to enable them see when it is able to successfully connect to the web service?
The way that our server is instantiated is as follows in case the test varies by object setup type:

    aServer := TSQLRestServerFullMemory.Create(aModel,jsonStr,false,true);
    try
      try
        aServer.ServiceRegister(TServiceERefServer,[TypeInfo(IERefServerInterface)],sicShared).ContractExpected := EREFER_CONTRACT;
        aHTTPServer := TSQLite3HttpServer.Create(COMMS_PORT,[aServer]);

Please advise what URL to use in a browser to allow us test connectivity? Let me know if anything I need to implement or turn on at the server side to make this work?

Regards

Paul

#7 mORMot 1 » XE3 and FMX » 2012-11-06 16:59:38

paulh
Replies: 3

Hi Arnaud, I notice from the ReadMe.txt with the latest source that it says the framework is for Delphi 6 to XE2.
I'm looking to upgrade to XE3 and also to trial FMX. I only use your client/server calling architecture and JSON to/from Object. Will these work with XE3 and FMX? If not, when do you expect these to work?
Regards
Paul

#8 Re: mORMot 1 » How avoid "contract differs from client's" to allow updating? » 2012-10-13 11:40:02

I can't take the latest version from Fossil - this goes live next week and there would be too much regression testing to change the underlying code at this stage! However our posts crossed - I have this working for me now.

If I ever make any money out of this project, how can I remunerate you for this very useful software you provide?

#9 Re: mORMot 1 » How avoid "contract differs from client's" to allow updating? » 2012-10-13 11:33:27

LOL - not sure why it happens but I have solved the problem.
I noticed that when it reported the error that the contracts were different, the hash code it said it was expecting had double quotes around it.
I changed the value of my ContractExpected constant to have double quotes around it - e.g. from 'synopse' to '"synopse"' and it works!
So there seems to be a 'feature' that the ContractExpected value must be enclosed with double quotes...

#10 Re: mORMot 1 » How avoid "contract differs from client's" to allow updating? » 2012-10-13 11:23:56

Hi Arnaud, that is not working for me.

My Server code is:
        aServer.ServiceRegister(TServiceERefServer,[TypeInfo(IERefServerInterface)],sicShared).ContractExpected := CONTRACT_NAME;

My Server appears to be starting fine but when I call the following client method:
      FClient.ServiceRegister([TypeInfo(IERefServerInterface)],sicShared,CONTRACT_NAME);

It goes into the following and fails by raising the exception at the bottom of the code segment:
constructor TServiceFactoryClient.Create(aRest: TSQLRest;
  aInterface: PTypeInfo; aInstanceCreation: TServiceInstanceImplementation;
  const aContractExpected: RawUTF8);
var i, siz: integer;
    P: PCardinal;
    Error, RemoteContract: RawUTF8;
begin
  // extract RTTI from the interface
  if not aRest.InheritsFrom(TSQLRestClientURI) then
    EServiceException.CreateFmt('%s interface needs a Client connection',
      [aInterface^.Name]);
  inherited Create(aRest,aInterface,aInstanceCreation);
  // check if this interface is supported on the server
  if aContractExpected<>'' then
    fContractExpected := aContractExpected; // override default contract
  if not CallClient(SERVICE_PSEUDO_METHOD[imContract],@Error,'',@RemoteContract) then
    raise EServiceException.CreateFmt('"%s" interface or %s routing not supported by server%s',
      [fInterfaceURI,GetEnumNameTrimed(TypeInfo(TServiceRoutingMode),fRest.ServicesRouting),Error]);

The error message is:
'"ERefServerInterface" interface or REST routing not supported by server'

I suspect the problem is with the server because if I invoke the original call on the server:
        aServer.ServiceRegister(TServiceERefServer,[TypeInfo(IERefServerInterface)],sicShared);

Then it correctly reports that the contracts are different when I make the client call passing my constant as the ExpectedContract.

Any ideas?

Paul

#11 Re: mORMot 1 » How avoid "contract differs from client's" to allow updating? » 2012-10-12 06:04:13

Hi Arnaud, that's not that helpful sad
I spent an hour looking through the code for it last night - I wouldn't ask if I could just find the property and that is why I included the code I'm using. I can see there is a property for a different version of the ServiceRegister procedure but it is not present for the version I'm using here based on your example code.
Is this a new property you've added? The version of your code I have to use is about 6 months old.
If you can show me which server side method I need to use with the property I can move on with getting this project finished.
Thanks
Paul

#12 Re: mORMot 1 » How avoid "contract differs from client's" to allow updating? » 2012-10-11 20:37:42

Arnaud, I have been able to add the ContractExpected parameter on the client side but I can't see how to specify it on the server side?

My server side is:


  aModel := TSQLModel.Create([],ROOT_NAME);
  try
    aServer := TSQLRestServerFullMemory.Create(aModel,FILE_NAME,false,true);
    try
      aServer.ServiceRegister(TServiceERefServer,[TypeInfo(IERefServerInterface)],sicShared);
      aHTTPServer := TSQLite3HttpServer.Create(COMMS_PORT,[aServer]);

How do I specify the ContractExpected on the server side?

Paul

#13 Re: mORMot 1 » How avoid "contract differs from client's" to allow updating? » 2012-10-11 10:51:44

Great, I'm sorted on the optional parameter - it must never be backwards compatible (or otherwise the client could never download the new version from the server) so I will make that parameter a constant value.

With the Production and Test versions, I think that gives me the information I need. I still need to ponder how best to use it to give me the functionality I require but as always you've given me the tools to implement what I need.

Regards

Paul

#14 Re: mORMot 1 » How avoid "contract differs from client's" to allow updating? » 2012-10-11 10:30:26

Hi Arnaud, thanks for the quick response.

That is great news that I can provide an optional parameter to override this default behaviour. I just want to double-check - I never want a client to get a rejection from a server as the server should always be backwards compatible so does that mean that I should just hard code a static value into that optional parameter and never change it? If I used a version number, I would have thought that when I update the version number of the server that any clients calling passing a different value in that optional parameter would fail is it does now if the interface is updated?

Also, I would like it if the server could support a test and production version of the interface concurrently. Can I configure a single service to support two interfaces concurrently or have two services running alongside each other on same IP and port but responding to different interfaces?

Regards

Paul

#15 mORMot 1 » How avoid "contract differs from client's" to allow updating? » 2012-10-10 20:33:47

paulh
Replies: 16

Hi Arnaud, I'm trying to implement automatic updates to my client apps. I have a method the client calls to check if it needs to download an update. The problem is that when I make an update to my server version to add some new functionality, it breaks the clients from communicating with the server even though of course I ensure that my changes are only ever additive.

How do I turn off the checking of contract between client and server to allow the client make calls to a new server that has additional methods?

Thanks in advance

Paul

Debug Output: 21:25:23 294 Error in TClientToServer.doConnect: Server's ERefServerInterface contract differs from client's: expected ["107924CEE69317C3"], received ["4FD82C7AF11DB892"] Process eRefer.exe (4488)

#16 mORMot 1 » Asynchronous HTTP calls and/or fast timeout » 2012-09-10 09:13:30

paulh
Replies: 1

Hi Arnaud, I'm getting closer to deploying my application using your object to json technology and remote client/server access.
When I lose an internet connection, it takes quite some time for a timeout to occur and in the meantime my application is blocked. Ordinarily I don't mind the application being blocked as most calls are getting or posting data and these are generally quick and need to be synchronous to ensure transactional integrity within the app.
I was considering putting in a thread that sends a pingtest to the server every few seconds so I could show an LED indicator as red or green depending on connectivity. Is there a way to get that to return almost immediately if the comms are down but yet keep a reasonable timeout for the normal server calls which can take a bit longer to respond?
If I were to use an Indy component to ping the server would I need to deploy a separate indy web service on a different port on the server or is there a way I can use the mormot server from indy or can that be avoided altogether by overriding the normal timeout for these 'pingtest' calls?
Thanks
Paul

#17 Re: mORMot 1 » Proxy configuration settings in client » 2012-09-04 21:39:24

P.S. I did check and the contracthash is the same in the client and the server for both the old and the new code - so it is not due to a difference in the hash.

#18 Re: mORMot 1 » Proxy configuration settings in client » 2012-09-04 21:25:44

Arnaud, I couldn't see anything obvious but it is taking too long to try and resolve the issue so I've applied the additional code you'd added for passing the proxy settings into the old code base that I have and that is working for me now. It just means that I'm isolated with the old code base but it is working fine so I'm not too perturbed by that just now.
Thanks for the assistance as ever. At some point I'll try again to find what's causing it not to work...

#19 Re: mORMot 1 » Proxy configuration settings in client » 2012-09-04 20:05:27

Hi Arnaud, I can't run the integration tests just now but I have been able to narrow this down quite considerably by testing building the client with the new version of your code against the server built with the old version and that works so it looks like the incompatibility is on the server side.
If I build the server with the new code it compiles okay but is rejecting the client.
I shall look at your interface-based service samples to see if anything is different...

#20 Re: mORMot 1 » Proxy configuration settings in client » 2012-09-04 07:38:41

Arnaud, I double-checked and is failing with the latest version and latest .obj files and building both client and server.
If I change the path to the original Synopse folder and recompile both they work fine.
Any other ideas of how I can find the issue?
Thanks
Paul

#21 Re: mORMot 1 » Proxy configuration settings in client » 2012-09-04 07:25:32

Hi Arnaud, yes, absolutely - they are part of a group so I always recompile both. Furthermore if I change the path to the older Synopse source folder then it all worked.
One thing I must check though is whether I made sure the newer sqlite3.obj files are in the path...
Paul

#22 Re: mORMot 1 » Proxy configuration settings in client » 2012-09-04 01:10:29

P.S. This is the code I'm using to connect which causes the code to fail:

procedure TClientToServer.doConnect;
var
  I:  IERefServerInterface;
begin
  // Run procedure to get the proxy info into FProxyInfo
  extractProxyInfo;

  if FClient=nil then begin
    if FModel=nil then
      FModel := TSQLModel.Create([],ROOT_NAME);
    FClient := TSQLite3HttpClient.Create(SERVER_IP,COMMS_PORT,FModel,false,FProxyInfo.ProxyURL,FProxyInfo.ProxyBypass);
    FClient.SetUser(MOR_USER,MOR_PASS);
    try
      FClient.ServiceRegister([TypeInfo(IERefServerInterface)],sicShared);
    except
      on e: exception do
        logmsg('Error in TClientToServer.doConnect: '+e.message);
    end;
  end;
  if FClient.Services['ERefServerInterface'].Get(I) then
  begin
    FMainInterface := I;
  end;
end;


I reverted to the older version of the Mormot code and it runs just fine so this is some incompatibility that has been introduced sad

Paul

#23 Re: mORMot 1 » Proxy configuration settings in client » 2012-09-04 00:48:31

Hi Arnaud, I've upgraded to the latest version and now all of a sudden I'm getting an error trying to establish a connection from the client to the server.

It fails with the following exception:
Error in TClientToServer.doConnect: "ERefServerInterface" interface or REST routing not supported by server: {
"ErrorCode":403,
"ErrorText":"Forbidden"
}

This code is raised by:
constructor TServiceFactoryClient.Create(aRest: TSQLRest;
  aInterface: PTypeInfo; aInstanceCreation: TServiceInstanceImplementation;
  const aContractExpected: RawUTF8);
var i, siz: integer;
    P: PCardinal;
    Error, RemoteContract: RawUTF8;
begin
  // extract RTTI from the interface
  if not aRest.InheritsFrom(TSQLRestClientURI) then
    EServiceException.CreateFmt('%s interface needs a Client connection',
      [aInterface^.Name]);
  inherited Create(aRest,aInterface,aInstanceCreation);
  // check if this interface is supported on the server
  if aContractExpected<>'' then
    fContractExpected := aContractExpected; // override default contract
  if not CallClient(SERVICE_PSEUDO_METHOD[imContract],@Error,'',@RemoteContract) then
    raise EServiceException.CreateFmt('"%s" interface or %s routing not supported by server%s',
      [fInterfaceURI,GetEnumNameTrimed(TypeInfo(TServiceRoutingMode),fRest.ServicesRouting),Error]);

What has changed that is not backwards compatible?

Thanks

Paul

#24 Re: mORMot 1 » Proxy configuration settings in client » 2012-08-29 21:35:28

Hi Arnaud, that's great, I'll get the latest version and try it out - it's just what I need.
Regards
Paul

#25 mORMot 1 » Proxy configuration settings in client » 2012-08-26 21:22:46

paulh
Replies: 14

Hi Arnaud

Thanks for your software, my application uses it extensively for transmitting data to and from client and server over HTTP and it has helped save me write serialisation and send/receive code.
I need to ensure my application will work behind corporate firewalls where a proxy server is in place.
I note the following from your documentation:

Note that even if WinHTTP does not share by default any proxy settings with Internet Explorer, it can import the current IE settings. The WinHTTP proxy configuration is set by either proxycfg.exe on Windows XP and Windows Server 2003 or earlier, either netsh.exe on Windows Vista and Windows Server 2008 or later; for instance, you can run "proxycfg -u" or "netsh winhttp import proxy source=ie" to use the current user's proxy settings for Internet Explorer. Under 64 bit Vista/Seven, to configure applications using the 32 bit WinHttp settings, call netsh or proxycfg bits from %SystemRoot%\SysWOW64 folder explicitly.

I want to include in my client application the ability to detect the proxy settings if possible and if necessary to allow the user to enter the proxy details in my application and have it use those. I'm connecting to the server (an EC2 instance on Amazon cloud) using:

    if FModel=nil then
      FModel := TSQLModel.Create([],ROOT_NAME);
    FClient := TSQLite3HttpClient.Create(SERVER_IP,COMMS_PORT,FModel);
    FClient.SetUser(UPARAM1,UPARAM2);
    FClient.ServiceRegister([TypeInfo(IERefServerInterface)],sicShared);

How do I specify the proxy server details in my code if netsh/proxycfg can't get details?

Regards

Paul

#26 Re: mORMot 1 » Getting Started Guide? » 2012-07-08 20:58:01

Hi Arnaud, I can't just pass the silo variable as a TCollection as you'd said above that I would need to subclass it and add methods for adding TCollectionItem subclasses to the collection. That's just too much work for all the types of containers I need to pass across and I don't really understand why Mormot requires this when the serialisation/deserialisation of TCollections works just fine.

I'd not come across RawByteString but when I tried to define an interface between the client and server with a RawByteString parameter it causes the server to crash immediately on startup. I don't have time to investigate why.

I'm finding performance to be acceptable so it is not a problem for me to base64 encode/decode it if it makes it work as it does but it would be great if you can find and fix the bug to avoid me having to!

Paul

#27 Re: mORMot 1 » Getting Started Guide? » 2012-07-08 03:01:02

Hi Arnaud, I found that the problem with passing back the string was not due to the size of the string, it was the content of the string.
My function on the server side returns a string which is an encoding of a TCollection full of objects by calling "ObjectToJSON(silo)" where silo is the TCollection and the string returned by the ObjectToJSON is passed back to the client using a "var" parameter.

There is one object in the collection of 17,000 objects which causes the call from the client to the server to fail if that object is included in the collection that is encoded. If I exclude the object the call works.

If I do an ObjectToJSON on the object, it is the ReferralId property that causes the problem - they are binary GUIDS and this particular one has a double quote character in it which ObjectToJSON is escaping but yet it still manages to cause the problem:
{"ReferralId":"殯￶\"ㅉ壴⇮䚰","ReferralRef":"692.8476","ReferralDate":"2011-09-12T00:00:00","PatientRef":"8439.692","PatientName":"Paul,Jones","NHSNo":"123 238 6123","ReferredBy":"","RefPractice":"Vision Opticians","RefReason":"Glaucoma","ActionRqd":"Routine","PatAddress":"1, A Road, A Town, ","DOB":"1928-01-01T00:00:00","AreAttachments":false,"ObjectId":0}

I have been able to get around the issue by Base64 encoding the string on the server side and decoding it on the client side but you may want to look into this as I'm sure your code is probably encoding as well and other than this particular object the other 17,000 all work fine so it must be a small bug.

Thanks for the information about the compression and encryption.

Paul

#28 Re: mORMot 1 » Getting Started Guide? » 2012-07-04 19:09:43

Hi Arnaud, I have another issue.

When I call a function on the server and pass back a string, if the string is 2460657 characters it returns okay but as soon as I try and return a string with 2461055 characters I get an error raised:
'Error calling ERefServerInterface.getSiloData remote method'.

The function has following structure:
    function getSiloData(const userId: RawUTF8; var inSiloS: RawUTF8): integer;

There must be some limitation on the inSiloS string length? I'm using it to encode my collection of objects using "ObjectToJSON" which is quite happy returning strings 20M characters. How do I overcome this issue to use the client/server calls to return longer strings?

Paul

#29 Re: mORMot 1 » Getting Started Guide? » 2012-07-04 12:09:52

Hi Arnaud, in answer #21 you said:

ab wrote:

There is already SynLZ or Deflate/GZip compression built-in for HTTP, and it would be very easy to add such an encryption.

I'd like to use my own compression and encryption of string values that I transmit between client and server - how to I turn off and on the "SynLZ or Deflate/GZip compression" compression that you mention is built-in?

Thanks in advance.

Paul

#30 Re: mORMot 1 » Getting Started Guide? » 2012-06-14 14:39:54

Hi, but I'm testing it with the n2 parameter having been created with the TCollTest and just trying to invoke the call from client to server causes the error even without trying to add items on the server side.
The same error occurs if I pass a dynamic array - even if I don't try and manipulate the array on the server side.
I can use the ObjectToJSON/JSONToObject but it can't be anything too tricky to resolve as all I'd imaging you're doing here is using those methods to serialise and deserialise the parameters before passing the data across the HTTP connection?
Anyway, I'm not stuck but let me know if you have a workaround for the issue I can use.
Thanks
Paul

#31 Re: mORMot 1 » Getting Started Guide? » 2012-06-14 14:23:00

Arnaud, I have implemented the TCollection subclass as you requested and I am still getting exactly the same error as before.
Do you want me to send you my project code?
Or perhaps you could send me your amended sample project code you tested as working passing these TCollections?
Thankfully the ObjectToJSON and JSONToObject do work with TCollection [without having to subclass ;-) ] so I can serialise and deserialise myself between calls so I'm not stuck but given that you feel this should work it would be nice to use the higher level methods.
Paul

#32 Re: mORMot 1 » Getting Started Guide? » 2012-06-14 13:55:45

Hi Arnaud, in Delphi you don't need to subclass from TCollection and implement Add and Item methods to work with collections - just passing the TCollectionItem subclass to the Create method of TCollection allows it to add items of the correct subclass when you invoke the Add method on TCollection.
I'm happy to do it your way if that is what is required to make it work with mormot but it is not the standard way. The article you reference subclasses TCollection (THairs) as a convenience so that the Add and Item methods return the correct subclass but they are NOT mandatory to allow TCollection to work and the code I sent you just using a plain TCollection is correctly instantiating the correct subclass when you invoke its Add method.
I shall use this now on the assumption that you've tested this and it works. However, I also got a pretty similar error when using your recommended approach with records and dynamic arrays - it might be useful for anyone who does prefer records/arrays to know how to make that work correctly?
Thanks again for the prompt responses - great support.
Paul

#33 Re: mORMot 1 » Getting Started Guide? » 2012-06-14 01:36:00

Arnaud, I have tried to get your suggestion working of using either a TCollection or an array of records and in both cases when the client calls the server passing either a collection or array I get a runtime error. I've emailed you a modification of your sample project 14 that demonstrates the issue.
What I'm trying to do is get the client to request from the server a set of objects/records. It will typically pass an empty container and needs the server to populate the container and pass the information back.
Can you please let me know what I need to do to make this work. Please email me back the modified project with code that you have made work as this is getting fairly time critical now for me to deliver a working solution.
Paul

#34 Re: mORMot 1 » Getting Started Guide? » 2012-06-07 17:04:49

Hi Arnaud, I need to use HTTPS rather than HTTP for the communication between client and server - do you have a reference of how I configure that?
Paul

#35 Re: mORMot 1 » Getting Started Guide? » 2012-06-07 16:59:33

Hi Arnaud, other than for a test app I could never think of a time when an auto-generated UI could be preferable to one that is created specifically for the application. If you looked at the richness of the applications I'm creating you'd see that it would be a waste of time to try and create an auto-generated UI.

Live bindings is SO simple. You design your form as you want things to be, you associate the controls with the fields in your class and then you can have your app retrieve an object and the fields immediately are displayed in the UI. If they select a different value in a combo and select the "Ok" button to apply the UI to the object there is a single call to do that. You then validate the object (not the UI) but flag errors back into the UI.

There is now never a need to use data aware controls now that live bindings exists - any control becomes object field aware and you never have to worry about keeping fields and components in sync - you can focus on getting a really nice UI and rich business logic.

#36 Re: mORMot 1 » Getting Started Guide? » 2012-06-07 14:42:45

BTW, have you used live bindings for linking user interface controls to objects?
I'm using bidirectional bindings with a few helper methods that allow me to map object fields to controls like the following and hey presto I have a UI and object kept in sync:

procedure TfrmNewReferral.doBindings;
begin
  // Create a binding expression.
  bindTogether(self, bsPatient, 'FirstName', aePatientFirstName);
  bindTogether(self, bsPatient, 'Surname', aePatientSurname);
  bindTogetherPopulateCombo(self, bsPatient, 'Title', cbPatientTitle, TPersonTitle);
  bindTogether(self, bsPatient, 'Gender', cbPatientGender);
  cbPatientGender.Items.Text := TGender.combolist;
  bindTogether(self, bsPatient, 'Marital', cbPatientMaritalStatus);
  cbPatientMaritalStatus.Items.Text := TMaritalStatus.combolist;
  bindTogether(self, bsPatient, 'Dob', dpPatientDOB, 'Date');
  bindTogether(self, bsPatient, 'HomePhone', mePatientHomePhone);
  bindTogether(self, bsPatient, 'MobilePhone', mePatientMobilePhone);
  bindTogether(self, bsPatient, 'Email', aePatientEmail);
  bindTogether(self, bsPatient, 'NhsNumber', mePatientNHS);
  bindTogether(self, bsPatient, 'Postcode', aePatientPostcode);
  bindTogether(self, bsPatient, 'Address1', aePatientAddress1);
  bindTogether(self, bsPatient, 'Address2', aePatientAddress2);
  bindTogether(self, bsPatient, 'Town', aePatientTown);
  bindTogetherPopulateCombo(self, bsPatient, 'County', cbPatientCounty, TCounty);
  bindTogetherPopulateCombo(self, bsPatient, 'Country', cbPatientCountry, TCountry);
end;

#37 Re: mORMot 1 » Getting Started Guide? » 2012-06-07 09:03:24

Awesome Arnaud - that will be great to be able to use the TPatientList subclass of TCollection.
I'm kind of OO old-fashioned in that I never use records and arrays - always classes as even if you think you won't add behaviour to your data record, usually at some point you do end up doing so and nothing beats a class for encapsulation.

Well, I think I'm all set now to finish the implementation with mORMot's interface-based services. I do hope for a later project I can expand to learn the ORM implementation too.

Your documentation is extensive and well written. However, some constructive criticism is that it is written like a research paper that explains much more the theory than the practice. It is great to have the theory but in order to make a decision whether to use some technology or not, one needs a practical "Getting Started" guide that shows the user how to get up and running with the technology (the how) rather than the why. I'm glad that I can use this technology for my project but if I'm to learn the ORM side I think it will take a lot of trial and error.

Thanks for the very proactive and comprehensive support as well - it is reassuring to know you are close at hand and your English is so good!

#38 Re: mORMot 1 » Getting Started Guide? » 2012-06-07 02:51:50

Hi Arnaud, okay, that is fine - thanks for the help - it is working now.

The other pattern that I have is that I want to have the client request the server for a collection of patients. Which is the best mormot calling structure to have the server return a collection/array/list of patient objects back?

for example:

procedure TServiceCalculator.GetMalePatients( forRegion: integer; out patientList: TList);

or perhaps:

function TServiceCalculator.GetMalePatients( forRegion: integer ): TList;

Paul

#39 Re: mORMot 1 » Getting Started Guide? » 2012-06-06 10:26:07

Hi Arnaud, I can send you the code - it is a tiny extension to your Sample 14 projects and would make a good example for your users if you make it work. Please email me an address I can send it to and you can test it yourself - will be quicker than reading and responding to all these comments ;-)

My TPatient is a subclass of TPersistent - just publishing a couple of RawUTF8 fields for the patient's name.

If I remove the var/out parameters your code works in that the object gets encoded and transported from the client to the server. When I add the var option it parses the parameters and correctly as per the code rejects it because there is an object parameter with an "out" label. That is what you have coded it to do it seems.

I can manually serialise the object using ObjectToJSON at client and recreate at Server with JSONToObject, make the changes and then save back using ObjectToJSON - however I'm sure that is what you had intended to implement and would be a lot cleaner than me adding additional code to do this...

Please email me and I'll send you a zip of the sample projects.

Regards

Paul

#40 Re: mORMot 1 » Getting Started Guide? » 2012-06-05 18:55:23

Hi Arnaud, when I tried to use effectively:

function TServiceCalculator.TP(var n1: TPatient; var n2: TPatient): integer;

or

function TServiceCalculator.TP(out n1: TPatient; out n2: TPatient): integer;

I get the following error raised from your code:
Invalid "Calculator" interface call: returned object.

And besides, an object parameter is a pointer to a pointer so it should be superfluous to declare an object parameter as var as if you want to change the object instance you change what the outer pointer points to.

I'm running the latest version of the code like you told me to. Do you want me to email you a version of the sample 14 project that is broken so you can see this first hand? If you email me paul @ westsw.com I can email you back the simple code... This is a very basic and simple requirement so it must have been implemented? I can serialise the object as a string myself and pass it that way if needed but I'm sure that is unnecessary???

Paul

The code that raises the exception is:

function TInterfacedObjectFake.FakeCall(var aCall: TFakeCallStack): Int64;

    if ValueDirection in [smdVar,smdOut,smdResult] then begin
      V := Value[a];
      case ValueType of
      smvObject: begin
        R := JSONToObject(V^,R,valid);
        if not valid then
          RaiseError('returned object');
        IgnoreComma(R);
      end;

(AB note: I edited your question since we do not need the whole framework code here - just he name of the method is enough)

#41 Re: mORMot 1 » Getting Started Guide? » 2012-06-05 17:05:17

Arnaud, I have modified the sample project 14 to have another function that passes two subclasses of TPersistent named TPatient in order to try and have the server update the objects.
The client code is as follows (a small modification to the sample code to include call to my new function:

  if Client.Services['Calculator'].Get(I) then
  begin
    lblResult.Caption := IntToStr(I.Add(a,b));
    // My new code to get a test object and pass to server
    aPatient := TPatient(TPatient.testObject);
    bPatient := nil;
    c := I.TP(aPatient, bPatient);
    showMessage('Patient 1 name='+aPatient.FirstName);
    if assigned(bPatient) then
      showMessage('Patient 2 name='+bPatient.FirstName+' '+bPatient.Surname)
    else
      showMessage('Patient retrieval failed!');
  end;

The server code is as follows:

function TServiceCalculator.TP(n1: TPatient; n2: TPatient): integer;
begin
  n2 := TPatient(TPatient.testObject);
  TPatient(n1).FirstName := 'Bert';
  result := 1;
end;

Note that the server is trying to update a field of the first object and to assign the second object.

The code runs, the server TP method gets invoked but the objects are unchanged after the call in the client.

How do I get my client to call the server and update and return new objects? These objects are simple subclasses of TPersistent with published simple properties.

Thanks

Paul

#42 Re: mORMot 1 » Getting Started Guide? » 2012-06-05 15:50:38

Hi Arnaud, I'm trying to test the framework as you described just using interface-based services.
I looked at the sample code for project 14 but it is too simplistic just invoking an Add method returning an integer.
I want to test this passing objects in both directions. I don't need the ORM as you identified, so what sample code can I see to simply demonstrate passing an object from client to server and a call from client requesting objects to be sent back?
If I can quickly get a simple example that works I'd be delighted to use the framework.
Thanks in advance.
Paul

#43 Re: mORMot 1 » Getting Started Guide? » 2012-05-12 20:51:38

I've now downloaded the framework and I can see there are actually 10 sample projects which hopefully will give me what I need. Sorry for the premature request for help!
Paul

#44 mORMot 1 » Getting Started Guide? » 2012-05-12 17:53:52

paulh
Replies: 37

Hi, I'm an experienced Delphi developer used to writing client/server Delphi applications that run on LAN with an Oracle back-end.
I need to write an ultra-thin client app with a central server for thousands of concurrent users to access. The app is not very sophisticated - mainly data entry and viewing - typical CRUD application.
My natural inclination is to create the app using DevExpress or TMS components (I need to use a rich grid control with filtering, column resizing, reordering, filtering etc) and to transmit the data to/from the client using JSON to an ISAPI DLL I'd write in Delphi. The ISAPI DLL would implement the server-side code with the database access/persistence to Oracle. I already have the database - it is from a legacy app my app will replace but I want to keep the underlying database.
I came across your Marmot SDK when looking for JSON components. I have to deliver the application before the end of May so I don't have time to learn a difficult framework. I like the overview parts of the 720 page doc but I don't have time to go through the doc - I just need a "Getting Started", some good demos and a reference to the classes.
I can't see any obvious "Getting Started" guide or suite of sample apps - seems to be a "Main Demo" but that doesn't seem to be documented as part of a "Getting Started" style doc? If you have these then please point me to them but if not then don't worry I will reuse existing ISAPI code and add a JSON component.
Please advise.
Regards
Paul
pheffernan@gmail.com

Board footer

Powered by FluxBB