Tokay Posted April 26, 2020 Posted April 26, 2020 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. 2 Quote
Tokay Posted April 27, 2020 Author Posted April 27, 2020 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. Quote
Freeman35 Posted April 27, 2020 Posted April 27, 2020 I'll try send message session to session. maybe can chat application . Quote
eduardosuruagy Posted April 28, 2020 Posted April 28, 2020 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 Quote
picyka Posted April 28, 2020 Posted April 28, 2020 9 minutes ago, eduardosuruagy said: I tried to simulate your example here, but I didn't find this TUniQuery component Unidac. Quote
eduardosuruagy Posted April 28, 2020 Posted April 28, 2020 3 minutes ago, picyka said: Unidac. Is this example complete? Quote
picyka Posted April 28, 2020 Posted April 28, 2020 1 minute ago, eduardosuruagy said: Is this example complete? TUniQuery switch to TFDQuery for example Quote
Tokay Posted April 28, 2020 Author Posted April 28, 2020 37 minutes ago, eduardosuruagy said: At what time do I run the query At what you need. For example: at show time. Quote
eduardosuruagy Posted April 28, 2020 Posted April 28, 2020 18 minutes ago, Tokay said: At what you need. For example: at show time. I tried to make your code work but I was unsuccessful. I changed TUniQuery to TFDQuery and it still didn't work. Quote
Tokay Posted April 28, 2020 Author Posted April 28, 2020 Hmmm, It's strange. I'll try with TFDQuery here. Quote
Tokay Posted April 28, 2020 Author Posted April 28, 2020 I forgot the Execute Method, and have added to the code. Works fine here with TFDQuery too Quote
eduardosuruagy Posted April 29, 2020 Posted April 29, 2020 On 27/04/2020 at 08:18, Freeman35 said: Vou tentar enviar uma mensagem para outra. talvez possa conversar aplicativo. Were you able to chat with another application? Quote
Freeman35 Posted April 29, 2020 Posted April 29, 2020 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. Quote
Freeman35 Posted May 1, 2020 Posted May 1, 2020 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; Quote
Freeman35 Posted May 14, 2020 Posted May 14, 2020 3 hours ago, zilav said: websockets work fine What Mean ? can you explain? 1 Quote
Oliver Morsch Posted December 1, 2024 Posted December 1, 2024 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 !? Quote
Tokay Posted December 3, 2024 Author Posted December 3, 2024 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; Quote
Oliver Morsch Posted December 7, 2024 Posted December 7, 2024 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. Quote
Tokay Posted December 8, 2024 Author Posted December 8, 2024 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 Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.