Page 1 of 1

Multiple Dataset sharing a single cursor?

Posted: Thu 20 May 2010 05:53
by jbakuwel
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

Posted: Thu 20 May 2010 12:20
by bork
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.

Posted: Fri 21 May 2010 08:35
by hughespa
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.

Posted: Fri 21 May 2010 13:07
by bork
When you add a new DataModule unit then Delphi generates the following code:

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

Code: Select all

var
  DataModule2: TDataModule2;
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.

Posted: Fri 21 May 2010 22:56
by jbakuwel
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

Posted: Sat 22 May 2010 01:10
by hughespa
I'm not sure of the reason why but there are lots of things that don't work correctly when they are contained by a data module that has no name. It's not specific to UniDAC.

Wherever you create a datamodule dynamically it's best to always name it.

Regards, Paul.

Posted: Tue 25 May 2010 09:55
by bork
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 ).
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]