Just as we have done for .NET, with the latest beta we’re bringing Code-First Servers to Delphi, as well.
To recap, Code-First lets you define your services and related types in Code and have them published on the fly, without having to first define a RODL file Service Builder.
You simply define your service classes and attach a couple of attributes to make them available remotely, and RemObjects SDK will take care of the rest and make sure they become callable. The same goes for any enums and structures these services use.
At runtime, RemObjects SDK will generate a RODL representation of your services based on the types it finds in your app and make that available to download for clients. So to the outside, a Code-First looks identical to a traditional RODL-driven server, and for the use on the client, you can still import the (now auto-generated) RODL from your IDE or the
codegen tool to generate client interface stubs.
Let’s see how Code-First services look like in Delphi:
Converting an Existing Server
You probably already have existing RO servers, so let’s have a look at how to convert one to the new model. To be clear, you don’t have to convert your servers to Code-First in order to use RO9. This is not a breaking change, just a new option.
Just as on .NET, there are really just four steps involved in making the switch, all of them simple, and two of them automated:
- Adjust your existing Service implementations
- Convert our auxiliary types, such as structs, arrays, event sinks and enums
- Remove your RODL file and the auto-regenerated _Intf, _Invk and _Async files generated from it
- Add one statement of code to configure your server for Code-First
Adjusting Your Existing Services
Since your service implementation files consider code you write explicitly, we won’t auto-convert them for you. But switching them over to Code-First is easy. For each of your services, you just follow these steps:
- Add the new
- Add the
[ROServiceMethod]attribute to each service method you want to publish
- Remove the
ServiceNameinterface from the ancestor list
- Change the invoker from
In essence, you’d go from something like
TLoginService = class(TSimpleLoginService, ILoginService) .. fClassFactory := TROClassFactory.Create('LoginService', Create_LoginService, TLoginService_Invoker);
[ROService] TLoginService = class(TSimpleLoginService) .. fClassFactory := TROClassFactory.Create('LoginService', Create_LoginService, TRORTTIInvoker);
You will also need to add two new units to the
uses section of your code,
Note that you don’t necessarily need to convert all services to be Code-First in one go. You can start by converting some of your services and deleting them manually from the RODL, and leaving others RODL-based. Of course in that case you will want to delay the next two steps until you’re ready to switch the last service over.
At runtime, RemObjects SDK will take care of merging what’s in your RODL and what’s defined “Code-First” and make sure it all works together.
The syntax for event sinks has changed slightly for Code-First servers, replacing the auto-generated sink-specific Writer
with the new generic
IROEventWriter<T>. So your event sink code changes from this:
var chateventswriter: IHTTPChatEvents_Writer; .. chateventswriter := (EventRepository as IHTTPChatEvents_Writer); chateventswriter.OnSendMessage(Session.SessionID, thisuserid, aMessageText, aDestination <> '');
var chateventswriter: IROEventWriter<IHTTPChatEvents>; .. chateventswriter := EventRepository.GetWriter<IHTTPChatEvents>(Session.SessionID); chateventswriter.Event.OnSendMessage(thisuserid, aMessageText, aDestination <> '');
Note: the old syntax is still supported for event sinks defined in RODL.
Converting Auxiliary Types
Next, let’s look at all the additional types you may have defined in the RODL to help your services: Enums, Structs, Exceptions or Arrays (although Arrays defined in RODL don’t really reflect in generated code).
In code, these all lie in the _Intf file that you normally don’t touch, because it gets updated from the RODL when that changes. Since the RODL is going away, this auto-updating won’t happen anymore, so you are, in a way, “taking ownership” of these classes now.
You could just keep the _Intf file around, but there’s a ton of extra crud in there that you no longer need, and that would make it hard to maintain/update in the future. So really, it makes sense to clean that up. Just like in .NET, we have a small wizard in the Delphi IDE that takes care of this *and* the next step for you (but that’s not in the current beta drop, yet).
Remove the RODL and Auto-Generated Code
Once we’ve taken care of the auxiliary types, the RODL file and the three source files generated from it (but not the services!) can simply be removed from the server project and deleted.
Bye bye RODL, it was nice to have known you.
As a last step, you can optionally add this line of code to your server to configure the “name” of your server, as it will be exposed externally (in the RODL that clients will download). If it is not set, your .exe name will be used.
uRORTTIServerSupport.RODLLibraryName := 'MyServer';
And with that, your server is converted and ready to run. When you launch and go to http://localhost:8099/bin, you will still see the familiar RODL representation of your server, only now it is driven by the code in your project, not the other way around.
Creating New Services
As you can imagine, creating new services (or a whole new server) will be super easy with the new model, as well.
To add a new service to your existing server, simply add a new class, attach the
[ROService] attribute, and mark the methods you want published with
[ROServiceMethod]. Done. If you need new structs or enums, simply define them in code.
To create a brand new server, you can use the new “Code-First” project templates as a starting point.
We’ve only scratched the surface of Code-First services in this article, but I hope it gave you a good introduction and will help you get started with the feature.
Let us know what you think, how it’s working out for you, and what you would like to see improved.