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

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

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)

I’ve reproduced this issue, investigating

Thanks, logged as bugs://81989

bugs://81989 got closed with status fixed.

can you update uRODLToXML.pas as

changes
procedure TRODLToXML.FillServiceGroupExceptionList(aLibrary: TRODLLibrary);

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

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

  procedure _CheckArray(anArray: TRODLArray);
  begin
    _CheckType(anArray.ElementType);
  end;

  procedure _CheckStruct(aStruct: TRODLBaseStruct);
  var
    i: Integer;
  begin
    if aStruct.Ancestor <> '' then _CheckType(aStruct.Ancestor);
    for i := 0 to aStruct.Count -1 do
      _CheckType(aStruct.Items[i].DataType);
  end;

  procedure _CheckService(aService: TRODLBaseService);
  var
    si: TRODLServiceInterface;
    o: TRODLOperation;
    j,k,l: Integer;
  begin
    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
          _CheckType(o.Items[l].DataType);
      end;
      if aService.Ancestor <> '' then begin
        aService := aLibrary.FindService(aService.Ancestor);
        if aService <> nil then  _CheckService(aService);
      end;
    end;
  end;

var
  i: Integer;
  ls: TRODLBaseService;
  ar: TRODLArray;
  st: TRODLStruct;
begin
  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;
      _CheckService(ls);
    end;
  end;

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

  for i := 0 to aLibrary.ExceptionCount - 1 do begin
    RegisterDataType(aLibrary.Exceptions[i].Name);
    _CheckStruct(aLibrary.Exceptions[i]);
  end;

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

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

end;

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

Hi

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.

Thks.

INTF_TASK_WS_Intf.pas (1.1 MB)

how it should be named?

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

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.

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

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).

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)),'');

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.

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

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?

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

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,

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

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

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.

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;
  try
    rec := gen.Generate(aLibrary, cg4l_Delphi, cg4m_Intf);
  • receive generated _Intf file from rec