XDataClient in Threads/Tasks

Hello,

I'm not quite sure, where the problem is - in the client or in the server. I have the following scenario:

I'd
like to load multiple tables at application start in generic lists. It
works fine when loading one after the other, but when I do this in tasks
(threads) I get exceptions, memory leaks and error messages. For
multithreading I use the OmniThreadLibrary in the client - but I do not
think that the problem is related to this library.

My Server looks like this:

  SparkleServer := THttpSysServer.Create;

  lConnectionFactory := TDBConnectionFactory.Create(
    function: IDBConnection
    var
      lConn: TUniConnection;
    begin
      lConn := TUniConnection.Create(nil);
      // initialization of the connection values for TUniConnection incl. LoginPrompt := false
      hsCreateIniFile.ReadUniConnection('Ac5Settings', lConn);
      Result := TUniDacConnectionAdapter.Create(lConn, 'MySql', true);
    end
  );

  Module := TXDataServerModule.Create(
    'http://+:2001/ac5/v1/api',
    TDBConnectionPool.Create(5, lConnectionFactory)
  );

An in my client the code for one of the tasks looks like this:

var
  FLookupTableStudium: TList<Tac5Studium>;

procedure Tdm.LoadLookupTableStudium;
begin
  CreateTask(
    procedure(const task: IOmniTask)
    var
      lClient: TXDataClient;
    begin
      lClient := TXDataClient.Create;
      try
        lClient.ReturnedInstancesOwnership := TInstanceOwnership.None;
        lClient.Uri := lIni.ReadString('Ac5RestSettings', 'Url', 'http://localhost:2001/ac5/v1/api');
        FLookupTableStudium := lClient.List<Tac5Studium>;
      finally
        lClient.Free;
      end;
    end)
    .OnTerminated(procedure(const task: IOmniTaskControl)
        ShowMessage(format('Studium geladen - count: %d', [FLookupTableStudium.Count]));
      end)
    .Run;
end;

As I told, everything works fine when I run these tasks with enough time between

LoadLookupTableStudium;
sleep(1000);
LoadLookupTableKlasse; // very similar to LoadLookupTableStudium

But if I run them without sleep, the second one crashes...

Maybe
this information is already enough to show what I am doing wrong? I
experimented already a few hours but am not able to find my error.

I would be really glad if You could give me a hint.

Regards
Harald

Hello Harald,


Before further investigation, could you please try to run the following code before starting the threads and see if the issue goes away?

var E: TMappingExplorer;
C: TClass;
begin


E := TMappingExplorer.Default;


for C in E.Hiearchy.Classes do
begin
  E.GetTable(C);
  E.GetId(C);
  E.GetVersionColumn(C);
  E.GetAssociations(C, False, False);
  E.GetAssociations(C, False, True);
  E.GetAssociations(C, True, False);
  E.GetAssociations(C, True, True);
  E.GetColumns(C, False, False);
  E.GetColumns(C, False, True);
  E.GetColumns(C, True, False);
  E.GetColumns(C, True, True);
  E.GetDiscriminatorColumn(C, False);
  E.GetDiscriminatorColumn(C, True);
  E.GetSequence(C, False);
  E.GetSequence(C, True);
  E.GetClassStateMembers(C, False, False);
  E.GetClassStateMembers(C, False, True);
  E.GetClassStateMembers(C, True, False);
  E.GetClassStateMembers(C, True, True);
  E.GetClassVisibleMembers(C, False);
  E.GetClassVisibleMembers(C, True);
end;
 

Hello Wagner,

thanks for Your answer and the code. I have trouble to run this code. First TMappingExplorer was not found and then I put Aurelius.Mapping.Explorer into uses. But now the code still does not compile E.Hierarchy is not declared.

Regards
Harald

Hello Wagner,

I just discovered the typo in Your code
I changed

and it compiles.
Great - and my errors and AVs are gone. What does this code do? What did I miss?

Regards
Harald

TMappingExplorer loads its information on demand. And such mechanism is not thread-safe. That code makes sure that everything is loaded in advance, then it's thread-safe.


Thanks. Thread problem is solved. Working well.

1 Like

Is this necessary anymore?

I think MappingExplorer is calling LoadMapping once, so I think you could speed it up by using this:

for hClass in MappingExplorer.Hierarchy.Classes do
begin
  MappingExplorer.FindAbstractEntityTypeFromClass(hClass);
  break;
end;

right?

Probably it can be replaced by the mentioned code, yes.

That's fine, because if you have a lot of Entities in this Explorer, this is a lot overhead for doing nothing upon second iteration.