Multiple Dataset sharing a single cursor?
Multiple Dataset sharing a single cursor?
Hi,
I have a form that creates a TDataModule when it is created. The TDataModule contains all the datasets (TUniTable and TUniQuery) and is a private property of the form. All datasets (across the application) share a single UniConnection.
When I open two (identical) forms, I would expect that each form works independently however when I scroll through the records with a db navigator on one form, the data on the 2nd form follows suite.
I've used the debugger to verify that each form indeed uses a different instance of the TDataModule.
I'm puzzled.... suggestions are welcome.
kind regards,
Jan
I have a form that creates a TDataModule when it is created. The TDataModule contains all the datasets (TUniTable and TUniQuery) and is a private property of the form. All datasets (across the application) share a single UniConnection.
When I open two (identical) forms, I would expect that each form works independently however when I scroll through the records with a db navigator on one form, the data on the 2nd form follows suite.
I've used the debugger to verify that each form indeed uses a different instance of the TDataModule.
I'm puzzled.... suggestions are welcome.
kind regards,
Jan
Hello
TUniQuery and TUniTable are inherited from TDataSet and their functionality is restricted by the base class TDataSet. TDataSet only has one cursor and doesn't have any mode for multiply cursors. You can try to use TClientDataSet and its method CloneCursor (more detailed information you can find in the Delphi help). But you should keep in mind that TClientDataSet works with large volume of data too slowly.
TUniQuery and TUniTable are inherited from TDataSet and their functionality is restricted by the base class TDataSet. TDataSet only has one cursor and doesn't have any mode for multiply cursors. You can try to use TClientDataSet and its method CloneCursor (more detailed information you can find in the Delphi help). But you should keep in mind that TClientDataSet works with large volume of data too slowly.
The way you describe it, it seems to me that each form has its own datasets and a connection on its own data module.
You need to set the datamodule name to something unique too otherwise it won't work.
This should give you independent views of the tables.
How do you connect the navigators datasource? Is it possible it is controlling the wrong instance of a dataset?
I know that the standard dataset actions (if you use those) have given me trouble in the past because they are supposed to work on the datasource of the focused controls but they do not always work as planned.
You could add a simple button that does a dataset.next and see if it behaves differently.
Regards, Paul.
You need to set the datamodule name to something unique too otherwise it won't work.
This should give you independent views of the tables.
How do you connect the navigators datasource? Is it possible it is controlling the wrong instance of a dataset?
I know that the standard dataset actions (if you use those) have given me trouble in the past because they are supposed to work on the datasource of the focused controls but they do not always work as planned.
You could add a simple button that does a dataset.next and see if it behaves differently.
Regards, Paul.
When you add a new DataModule unit then Delphi generates the following code:
Where DataModule2 is a global variable. It means that for all created DataModules it will have the same value.
If you try to create a new DataModule by calling Application.CreateForm(TDataModule2, DataModule2) then pointer to the created DataModule will be saved to the DataModule2 global variable. And if you try to create second DataModule then variable DataModule2 will be overridden by the pointer to the second DataModule and pointer to the first DataModule will be lost. And if you get DataSets from your DataModules by the DataModule2 global variable then you get DataSets from the last created DataModule in all cases, and all your forms will use the same DataSets.
I recommend to remove the following declaration at all:
And save the pointer to the created DataModule in the property of the form that created it. And don't try to save the pointer to DataModule in the global variable in the form's unit because you will get the same result as with the global variable in DataModule.
Code: Select all
unit Unit2;
interface
uses
SysUtils, Classes;
type
TDataModule2 = class(TDataModule)
private
{ Private declarations }
public
{ Public declarations }
end;
var
DataModule2: TDataModule2;
implementation
{$R *.dfm}
end.
If you try to create a new DataModule by calling Application.CreateForm(TDataModule2, DataModule2) then pointer to the created DataModule will be saved to the DataModule2 global variable. And if you try to create second DataModule then variable DataModule2 will be overridden by the pointer to the second DataModule and pointer to the first DataModule will be lost. And if you get DataSets from your DataModules by the DataModule2 global variable then you get DataSets from the last created DataModule in all cases, and all your forms will use the same DataSets.
I recommend to remove the following declaration at all:
Code: Select all
var
DataModule2: TDataModule2;
Hi,
Thanks for the feedback. Problem solved but I'm now even more puzzled!
I have a form, and the datamodule for that form is a private property of that form. In the OnCreate event of the form, I create the datamodule:
dmEmployee := TdmEmployee.Create(Self);
The variable automatically added by Delphi in the unit of the datamodule was removed to avoid the compiler getting confused and using that single global variable (I tend to stick to using properties in classes only and not use any variables in units anyway).
The DataSets on the dmEmployee datamodule all share a single connection that is defined in another datamodule (dmGeneral): a single (instance of a) datamodule that is used for shared (across all forms) DataSets, the shared connection & provider components to the database etc.
Paul: thanks very much for suggestion to use a different name for each instance of the TDataModule; I likely would never have thought of that option! The bit of code that makes it all work beautifully is:
dmEmployee.Name := 'DM' + FormatDateTime('yyyymmddHHMMSS', Now());
in the OnCreate event of the form, just after creating the dmEmployee instance (for the moment assuming the user won't be able to create two forms within 1 second... perhaps I should add a sleep for 1.1 second or add milliseconds
).
Problem solved
but now I'm even more curious as to why this is the case? Is this a bug in Delphi compiler or the UniDAC components? Surely the name of a component shouldn't have this effect?
best regards,
Jan
Thanks for the feedback. Problem solved but I'm now even more puzzled!
I have a form, and the datamodule for that form is a private property of that form. In the OnCreate event of the form, I create the datamodule:
dmEmployee := TdmEmployee.Create(Self);
The variable automatically added by Delphi in the unit of the datamodule was removed to avoid the compiler getting confused and using that single global variable (I tend to stick to using properties in classes only and not use any variables in units anyway).
The DataSets on the dmEmployee datamodule all share a single connection that is defined in another datamodule (dmGeneral): a single (instance of a) datamodule that is used for shared (across all forms) DataSets, the shared connection & provider components to the database etc.
Paul: thanks very much for suggestion to use a different name for each instance of the TDataModule; I likely would never have thought of that option! The bit of code that makes it all work beautifully is:
dmEmployee.Name := 'DM' + FormatDateTime('yyyymmddHHMMSS', Now());
in the OnCreate event of the form, just after creating the dmEmployee instance (for the moment assuming the user won't be able to create two forms within 1 second... perhaps I should add a sleep for 1.1 second or add milliseconds

Problem solved

best regards,
Jan
Try to use dmEmployee.Name := 'DM' +IntToStr(getTickCount) instead of dmEmployee.Name := 'DM' + FormatDateTime('yyyymmddHHMMSS', Now()). The getTickCount function returns the number of elapsed milliseconds that have elapsed since the system was started.[/quote]in the OnCreate event of the form, just after creating the dmEmployee instance (for the moment assuming the user won't be able to create two forms within 1 second... perhaps I should add a sleep for 1.1 second or add milliseconds Smile ).