CodeFirst and Client-Side interfaces - Generates only one interface file


(EvgenyK) #46

yes, you need to use http://localhost:8099/rodl?servicegroup=LOGIN as parameter for rodl2code

(Smokoveck Florencio) #47

when I try to use the following error occurs:

Processing RODL file http://localhost:8099/rodl?servicegroup=LOGIN
Generating intf
There was a problem loading the RODL.

System.Exception: unknown type: DataParameter
em RemObjects.SDK.CodeGen4.DelphiRodlCodeGen.Intf_generateReadStatement(RodlLibrary library, String aElementType, CGExpression aSerializer, CGCallParameter aName, CGCallParameter aValue, CGTypeReference aDataType, CGCallParameter aIndex)
em RemObjects.SDK.CodeGen4.DelphiRodlCodeGen.Intf_GenerateArray(CGCodeUnit file, RodlLibrary library, RodlArray entity)
em RemObjects.SDK.CodeGen4.DelphiRodlCodeGen.GenerateInterfaceCodeUnit(RodlLibrary library, String aTargetNamespace, String aUnitName)
em RemObjects.SDK.CodeGen4.RodlCodeGen.GenerateInterfaceFile(RodlLibrary library, String aTargetNamespace, String aUnitName)
em RemObjects.SDK.CodeGen4.__Global.Main(String[] __args)

(EvgenyK) #48

I’ve reproduced this issue, investigating

(RemObjects) #49

Thanks, logged as bugs://81989

(RemObjects) #50

bugs://81989 got closed with status fixed.

(EvgenyK) #51

can you update uRODLToXML.pas as

procedure TRODLToXML.FillServiceGroupExceptionList(aLibrary: TRODLLibrary);

  procedure _CheckArray(anArray: TRODLArray); forward;
  procedure _CheckStruct(aStruct: TRODLBaseStruct); forward;

  procedure _CheckType(aDatatype: String);
    ar: TRODLArray;
    st: TRODLStruct;
    ar := aLibrary.FindArray(aDatatype);
    st := aLibrary.FindStruct(aDatatype);
    if (st <> nil) or (ar <> nil) or (aLibrary.FindEnum(aDatatype) <> nil) then begin
      if ar <> nil then _CheckArray(ar);
      if st <> nil then _CheckStruct(st);

  procedure _CheckArray(anArray: TRODLArray);

  procedure _CheckStruct(aStruct: TRODLBaseStruct);
    i: Integer;
    if aStruct.Ancestor <> '' then _CheckType(aStruct.Ancestor);
    for i := 0 to aStruct.Count -1 do

  procedure _CheckService(aService: TRODLBaseService);
    si: TRODLServiceInterface;
    o: TRODLOperation;
    j,k,l: Integer;
    for j:=0 to aService.Count - 1 do begin
      si := aService.Items[j];
      for k := 0 to si.Count - 1 do begin
        o := si.Items[k];
        if o.Result <> nil then _CheckType(o.Result.DataType);
        for l := 0 to o.Count-1 do
      if aService.Ancestor <> '' then begin
        aService := aLibrary.FindService(aService.Ancestor);
        if aService <> nil then  _CheckService(aService);

  i: Integer;
  ls: TRODLBaseService;
  ar: TRODLArray;
  st: TRODLStruct;
  for i := 0 to (aLibrary.ServiceCount - 1) do begin
    ls := aLibrary.Services[i];
    if isFlatten(ls) then begin
      if fExcludePrivateServices and ls.isPrivate then Continue;
      if not ContainsServiceGroup(ls) then Continue;

  for i := 0 to aLibrary.EventSinkCount -1 do begin
    ls := aLibrary.EventSinks[i];
    if isFlatten(ls) then begin
      if not ContainsServiceGroup(ls) then Continue;

  for i := 0 to aLibrary.ExceptionCount - 1 do begin

  for i := 0 to (aLibrary.ArrayCount -1 ) do begin
    ar := aLibrary.Arrays[i];
    if not ContainsServiceGroup(ar) then Continue;

  for i := 0 to (aLibrary.StructCount -1 ) do begin
    st := aLibrary.Structs[i];
    if not ContainsServiceGroup(st) then Continue;


rebuild your server and generate again _Intf with rodl2code, pls?

(Smokoveck Florencio) #52


Very good!! it works!!

Just a little problem, when you generate the _intf the output file name is from another service name.

see attachment file.


INTF_TASK_WS_Intf.pas (1.1 MB)

(EvgenyK) #53

how it should be named?

as for me - everything is correct:
INTF_TASK_WS_Intf.pas vs unit INTF_TASK_WS_Intf;

(Smokoveck Florencio) #54

The service I am generating is LOGIN (servicegroup=LOGIN) , so the unit name should be LOGIN_Intf

LOGIN_Intf.pas and unit LOGIN_Intf;

I do not understand why he writes the name as INTF_TASK_WS_Intf.

(EvgenyK) #55

it generates filename based on your name in RODL, i.e. %rodl_library_name%_Intf.
in your case, you have INTF_TASK_WS as Library Name in RODL

(Smokoveck Florencio) #56

But when it’s based on FirstCode there’s no more rodl, is not it? This already happens when it generates a single interface file for all services, when generated, it puts the name of the _intf file as the last service of my project group (.dll).

(EvgenyK) #57

RODL file is generated on-fly in CodeFirst mode.

you can specify library name/ID as

  uRORTTIServerSupport.RODLLibraryName := LibraryName;
  uRORTTIServerSupport.RODLLibraryID := LibraryUID;

if you didn’t specify it manually, it will be assigned in runtime as

  RODLLibraryName := ChangeFileExt(ExtractFileName(ParamStr(0)),'');

(Smokoveck Florencio) #59

All files * _RODLtypes.pas has the assignment:

uRORTTIServerSupport.RODLLibraryName: = LibraryName;
uRORTTIServerSupport.RODLLibraryID: = LibraryUID;

I think it’s recovering the name of the last service loaded in .dll group project.

(EvgenyK) #60

what value of LibraryName do you have in .exe and in .dll?
do you use runtime packages?
if yes, make sure that uRORTTIServerSupport.RODLLibraryName is set only in .exe

(Smokoveck Florencio) #61

yes, I’m using runtime packages.

Sorry, but I’m not understanding. I have several services in my dll, each service has its file * _RODLTypes.pas that set RODLLibraryName.

When I run to generate the interface (_intf) I think it should generate for the service I specified in the parameter, example of the LOGIN service I’m trying, so, the unit and name should be LOGIN_Intf, could not get from NameSpace?

(EvgenyK) #62

When run-time package are used , the same variable is modified from all packages. as a result, it got value from your last dll.

I can suggest for your scenario (.dll are compiled with runtime packages), change RODLLibraryName only in main code and not in dll

(Smokoveck Florencio) #63

Right, I’ve changed. But think of the following scenario: I have a server application that loads 10 services, ie the rodl2code will generate 10 separate interfaces, I’ll pass the command line 10 times changing only the parameter (url with the servicegroup" ) how will it change the interface names as the servicegroup informed? if the set RODLLibraryName of gets .exe it will only have one service name,

(EvgenyK) #64

why you can put each generated _intf into personal folders, like

  • dll1\NewLibrary_Intf.pas
  • dll10\NewLibrary_Intf.pas

(Smokoveck Florencio) #65

for each dll I have several services and the services use each other’s services, I can not have interfaces (_intf) with the same name, besides, on the client side, uses the services of several dlls (ie transparent to the client side ), for example, uses LOGIN_INTF, DEPART_INTF, LOCATION_INTF these services can be in any dll.

(EvgenyK) #66

I understood your idea.
what I can suggest:

  • get RODLLibrary from http://localhost:8099/rodl?servicegroup=XXXXXX with
    (MyChannel as IROMetadataReader).RetrieveRODL(...)
  • replace Library.Name in generated RODL with new name
  • pass given RODLLibrary into TROCodegen4 (uROCodegen4Helper.pas):
  gen:= TROCodegen4.Create;
    rec := gen.Generate(aLibrary, cg4l_Delphi, cg4m_Intf);
  • receive generated _Intf file from rec