Multiple Dataset sharing a single cursor?

Discussion of open issues, suggestions and bugs regarding UniDAC (Universal Data Access Components) for Delphi, C++Builder, Lazarus (and FPC)
Post Reply
jbakuwel
Posts: 35
Joined: Tue 02 Feb 2010 04:47

Multiple Dataset sharing a single cursor?

Post by jbakuwel » Thu 20 May 2010 05:53

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

bork
Devart Team
Posts: 649
Joined: Fri 12 Mar 2010 07:55

Post by bork » Thu 20 May 2010 12:20

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.

hughespa
Posts: 81
Joined: Sat 23 Aug 2008 08:36
Location: W. Australia

Post by hughespa » Fri 21 May 2010 08:35

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.

bork
Devart Team
Posts: 649
Joined: Fri 12 Mar 2010 07:55

Post by bork » Fri 21 May 2010 13:07

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.

jbakuwel
Posts: 35
Joined: Tue 02 Feb 2010 04:47

Post by jbakuwel » Fri 21 May 2010 22:56

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

hughespa
Posts: 81
Joined: Sat 23 Aug 2008 08:36
Location: W. Australia

Post by hughespa » Sat 22 May 2010 01:10

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.

bork
Devart Team
Posts: 649
Joined: Fri 12 Mar 2010 07:55

Post by bork » Tue 25 May 2010 09:55

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]

Post Reply