Jump to content

Recommended Posts

Posted

I was constantly missing feature of find defined session. It very important in some cases. I show you idea (if I understand all properly):

unit Main;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics,
 Controls, Forms, Uni, uniGUITypes, uniGUIAbstractClasses,
 uniGUIClasses, uniGUIRegClasses, uniGUIForm;

//this case shows how to run a long query without stopping the interface
//additional thread for query
type
 THelpThread = class(TThread)
 private
  FQuery:       TUniQuery;
  FSessionName: string;
 protected
  procedure Execute; override;
 public
  property Query: TUniQuery Read FQuery Write FQuery;
  property SessionName: string Read FSessionName Write FSessionName;
 end;

type
 TMainForm = class(TUniForm)
  UniQuery1:      TUniQuery;
  UniDataSource1: TUniDataSource;
  UniDBGrid1:     TUniDBGrid;
 private
  FThread: THelpThread;
  procedure Thread1Finish(Sender: TObject);
  procedure StartCalcThread(const s: string);
  procedure AssignDataSet;
  { Private declarations }
 public
  { Public declarations }
 end;

function MainForm: TMainForm;

implementation

{$R *.dfm}

uses
 uniGUIVars, MainModule, uniGUIApplication;

function MainForm: TMainForm;
begin
 Result := TMainForm(UniMainModule.GetFormInstance(TMainForm));
end;

{ THelpThread }

procedure THelpThread.Execute;
begin
 inherited;
 if Query.State <> dsInactive then
  Query.Close;
 if not Query.Transaction.Active then
  Query.Transaction.StartTransaction;
 Query.Open;
end;

{ TMainForm }

procedure TMainForm.StartCalcThread(const s: string);
begin
 //startin of query thread
 if not Assigned(FThread) then
 begin
  //the query
  UniQuery1.SQL.Clear;
  UniQuery1.SQL.Add('select MAPS.MAPS_SOURCE,');
  UniQuery1.SQL.Add(s);
  UniQuery1.SQL.Add('from MAPS');
  UniQuery1.SQL.Add('group by MAPS_SOURCE');
  UniQuery1.SQL.Add('union');
  UniQuery1.SQL.Add('select "all",');
  UniQuery1.SQL.Add(s);
  UniQuery1.SQL.Add('from MAPS');
  //off the grid DataSource
  UniDBGrid1.DataSource := nil;
  FThread := THelpThread.Create(True);
  FThread.Query := UniQuery1;
  FThread.OnTerminate := Thread1Finish;
  FThread.FreeOnTerminate := True;
  //store the session ID {!!!!}
  FThread.SessionName := UniSession.SessionId;
  //start querying
  FThread.Start;
 end;
end;

procedure TMainForm.Thread1Finish(Sender: TObject);
begin
 //sync assigned of dataset with the grid
 TThread.Synchronize(nil, AssignDataSet);
 FThread := nil;
end;

procedure TMainForm.AssignDataSet;
var
 Session: TUniGUISession;
 lMainModule: TUniMainModule;
begin
 //find session by ID {!!!!}
 Session := UniServerModule.GetSession(FThread.SessionName);
 //get defined MainModule of session
 lMainModule := TUniMainModule(Session.UniApplication.UniMainModule);
 //get defined MainForm of session
 with TMainForm(lMainModule.GetFormInstance(TMainForm)) do
 //assigne dataset and DataSource with grid
  UniDBGrid1.DataSource := UniDataSource1;
end;

initialization
 RegisterAppFormClass(TMainForm);

end.

 

  • Like 2
Posted

If I understand correctly, it could fix old and ugly issue "Attempt to access nil session reference". At least partially. You can store your current session ID and get it later in any code.

Posted
On 4/26/2020 at 6:38 PM, Tokay said:

I was constantly missing feature of find defined session. It very important in some cases. I show you idea (if I understand all properly):


unit Main;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics,
 Controls, Forms, Uni, uniGUITypes, uniGUIAbstractClasses,
 uniGUIClasses, uniGUIRegClasses, uniGUIForm;

//this case shows how to run a long query without stopping the interface
//additional thread for query
type
 THelpThread = class(TThread)
 private
  FQuery:       TUniQuery;
  FSessionName: string;
 protected
  procedure Execute; override;
 public
  property Query: TUniQuery Read FQuery Write FQuery;
  property SessionName: string Read FSessionName Write FSessionName;
 end;

type
 TMainForm = class(TUniForm)
  UniQuery1:      TUniQuery;
  UniDataSource1: TUniDataSource;
  UniDBGrid1:     TUniDBGrid;
 private
  FThread: THelpThread;
  procedure Thread1Finish(Sender: TObject);
  procedure StartCalcThread(const s: string);
  procedure AssignDataSet;
  { Private declarations }
 public
  { Public declarations }
 end;

function MainForm: TMainForm;

implementation

{$R *.dfm}

uses
 uniGUIVars, MainModule, uniGUIApplication;

function MainForm: TMainForm;
begin
 Result := TMainForm(UniMainModule.GetFormInstance(TMainForm));
end;

procedure TMainForm.StartCalcThread(const s: string);
begin
 //startin of query thread
 if not Assigned(FThread) then
 begin
  //the query
  UniQuery1.SQL.Clear;
  UniQuery1.SQL.Add('select MAPS.MAPS_SOURCE,');
  UniQuery1.SQL.Add(s);
  UniQuery1.SQL.Add('from MAPS');
  UniQuery1.SQL.Add('group by MAPS_SOURCE');
  UniQuery1.SQL.Add('union');
  UniQuery1.SQL.Add('select "all",');
  UniQuery1.SQL.Add(s);
  UniQuery1.SQL.Add('from MAPS');
  //off the grid DataSource
  UniDBGrid1.DataSource := nil;
  FThread := THelpThread.Create(True);
  FThread.Query := UniQuery1;
  FThread.OnTerminate := Thread1Finish;
  FThread.FreeOnTerminate := True;
  //store the session ID {!!!!}
  FThread.SessionName := UniSession.SessionId;
  //start querying
  FThread.Start;
 end;
end;

procedure TMainForm.Thread1Finish(Sender: TObject);
begin
 //sync assigned of dataset with the grid
 TThread.Synchronize(nil, AssignDataSet);
 FThread := nil;
end;

procedure TMainForm.AssignDataSet;
var
 Session: TUniGUISession;
 lMainModule: TUniMainModule;
begin
 //find session by ID {!!!!}
 Session := UniServerModule.GetSession(FThread.SessionName);
 //get defined MainModule of session
 lMainModule := TUniMainModule(Session.UniApplication.UniMainModule);
 //get defined MainForm of session
 with TMainForm(lMainModule.GetFormInstance(TMainForm)) do
 //assigne dataset and DataSource with grid
  UniDBGrid1.DataSource := UniDataSource1;
end;

initialization
 RegisterAppFormClass(TMainForm);

end.

 

I tried to simulate your example here, but I didn't find this TUniQuery component

Posted
22 hours ago, eduardosuruagy said:

//find session by ID {!!!!} 
Session := UniServerModule.GetSession(FThread.SessionName); 
//get defined MainModule of session 
lMainModule := TUniMainModule(Session.UniApplication.UniMainModule);

 

You have enough info and access code. I'm not test yet.

Posted

small test, in trail 1526

Not tested in hyperserver, but this will not work. 'cos node not access to another nodes, but if work on this, can work too. via db share. and next century :) server farm

procedure TMainForm.BTN_1Click(Sender: TObject);
var
 SS: TUniGUISession;
 lMainModule: TUniMainModule;
 SId : string;
 Hooked: TMainForm;
begin
//find session by ID {!!!!}
 SId := ClientDataSet1.FieldByName('sessionid').AsString;
 if (SId = '') or (SId = UniSession.SessionId) then Exit;

 SS := UniServerModule.GetSession(SId);
 //get defined MainModule of session
 lMainModule := TUniMainModule(SS.UniApplication.UniMainModule);
 //get defined MainForm of session
 Hooked:= TMainForm(lMainModule.GetFormInstance(TMainForm, False));
 if Hooked<>nil then Hooked._InComeMessage:= '[' +UniSession.SessionId +'  To: ' +SId +']-->' +EDT_1.Text;
end;

procedure TMainForm.Set_InComeMessage(const Value: string);
begin
  F_InComeMessage := Value;
  MEM_1.Lines.Add(FormatDateTime('hh:mm:ss',now) +'  InCome message : '+ F_InComeMessage);
//MEM_1: TUniMemo;  just screen not updated, son on screenshot, I moved form. just visual problem
end;

 

GetSession.gif

  • 2 weeks later...
  • 4 years later...
Posted

3 comments / questions:

1. In "Thread1Finish" you use "TThread.Synchronize" but because of "FThread.OnTerminate := Thread1Finish;" this is running already in the MainThread by default, so you would not need the syncronize !?

2. In "AssignDataSet" you use the field FThread of the MainForm, but then you could directly use the fields "UniDBGrid1" and "UniDataSource1" and have no need to go over session etc. !?

3. Since you use a query (and db connection !) of the user session, you should avoid that somethings happens over this db connection at another place at the same time the thread runs, e.g. the user starts another action which uses the database !?

Posted

That is code from my real project. I hope it could answer for some questions:

procedure TUniForm2.StartCalcThread(const s: string);
begin
 FDBQuery.DBQuery.SQL.Clear;
 FDBQuery.DBQuery.SQL.Add(GetSQLStr);
 UniDBGrid1.DataSource := nil;
 FThread := THelpThread.Create(True);
 FThread.Query := FDBQuery.DBQuery;
 FThread.OnFinished := Thread1Finish;
 FThread.FreeOnTerminate := True;
 FThread.SessionName := UniSession.SessionId;
 FThread.Start;
end;

procedure TUniForm15.Thread1Finish(Sender: TObject);
var
 Session: TUniGUISession;
 MainModule: TUniMainModule;
begin
 if Assigned(FThread) then
  Session := UniServerModule.GetSession(FThread.SessionName)
 else
  Exit;
 FThread := nil;
 MainModule := TUniMainModule(Session.UniApplication.UniMainModule);
 with TUniForm15(MainModule.GetFormInstance(TUniForm15)) do
  UniDBGrid1.DataSource := UniDataSource1;
end;

type
 THelpThread = class(TThread)
 private
  FOnFinished:  TNotifyEvent;
  FQuery:       TFDQuery;
  FSessionName: string;
 protected
  procedure Execute; override;
 public
  property OnFinished: TNotifyEvent Read FOnFinished Write FOnFinished;
  property Query: TFDQuery Read FQuery Write FQuery;
  property SessionName: string Read FSessionName Write FSessionName;
 end;


{ THelpThread }

procedure THelpThread.Execute;
begin
 inherited;
 Query.FetchOptions.Unidirectional := False;
 MakeQuery(Query, Query.SQL.Text, True);
 if Assigned(FOnFinished) then
  OnFinished(Self);
end;

 

Posted

Why do you use here your own event "OnFinished" which is not snchronized in your code instead of using "OnTerminate" which is synchronized by default?

It could be that in "TUniForm15.Thread1Finish" the form is already freed when user session terminated in the meantime.

And when you are using other dbComponents with the same db connection that is also not really threadsafe.

Posted
22 hours ago, Oliver Morsch said:

Why do you use here your own event "OnFinished" which is not snchronized in your code instead of using "OnTerminate" which is synchronized by default?

 

Because OnTerminate does not work under Linux

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...