separate dataset instances using datamodules in delphi
Asked Answered
M

4

3

I am using Delphi6 and have a data module with an ADO DataSet which is used by two forms, formA and FormB. Each form has a Dataset.Open() in OnCreate and Dataset.Close() in OnClose. If both forms are open simultaneously and formB is closed the dataset is closed in formA. How can I prevent this, essentially I need separate instances of the dataset for each form but at the same time use the datamodule.

Marvin answered 20/7, 2011 at 10:53 Comment(5)
did you define your dataset as static?Tisza
the dataset is on the datamodule and its cursor type is set to staticMarvin
Where is the data module created? Do the forms create their own instance or do you want to use a central instance?Stow
the datamodule is auto created at startup and the forms create their own instanceMarvin
You can put two datasets on a data module. I've used Clone on ClientDataSets for probably similar design. ADODataSet seems to have the same functionality..Bifurcate
F
3

The simplest way to achieve what you want is to create an instance of the data module for each form, and pass it to the form so it can be freed when the form is closed:

var
  Data: TDataModule;
begin
  Data := T<YourDataModule>.Create(Self);
  try
    Form := T<YourForm>.Create(Self);
    Form.DataModule := Data;
    Data.Name := '';
  except
    Data.Free;
    raise;
  end;

  Form.Show;
end;

Setting the DataModule's Name to an empty string is done to ensure that the VCL's logic for hooking up data aware controls to their datasource/dataset is done using the newly created instance, instead of the first ever instance.

In the Form's OnClose handler (or its destructor) make sure to free the data module.

Freemon answered 20/7, 2011 at 11:20 Comment(6)
Sorry but I don't understand the assignment "form.datamodule=data". Is form.datamodule a property of the form?Marvin
@mem100: Not one that exists "out of the box" you will have to add it to your forms yourself. If you do not want to do that for every single form in your application, you could consider creating a base form, add the property to that and then descend all your application's forms from that base form. If you need more info on that, that merits another question altogether... :-)Freemon
ok I understand but I have another clarification. what about the datasources on the form? The datasources are binded to the dataset on the autocreated datamodule. How do I change the datasources so that they use the newly created instance and how do I access the datasets on the newly created instance inside the form create event so I can open themMarvin
ok I did a slight variation on what you said.I defined a private var of type Tdatamodule in the form class. I override the constructor and in it I created the datamodule instance data:=T<mydatamodule>.create(self) Then I assign the form's datasource to the dataset on the new instance using a type cast datasource.dataset:=T<mydatamodule>(data).datasetMarvin
I access the datasets the same way T<mydatamodule>(data).dataset.open. Finally I free the data var in the forms destructor. This seems to work so far.. still testing it outMarvin
@mem100: You can keep the "bindings" at design time. The VCL hooks it all up based on names of instances it finds. That's why you need to set the name of the DataModule to an empty string so it won't be used by a second/third/fourth form and they will use a newly created instance of the data module (which then still has its name). It does require the data module being created before the form's constructor streams in the dfm (where all the names are). So you need to create it before the inherited call in the form's constructor or before the form is created. (an OnCreate handler is too late!)Freemon
G
3

Probably you need a separate instance from the datamodule for each form.

If you really want to use the same datamodule instance form both forms, then you have to open and close the dataset from the datamodule, adding some reference counting mechanism.

Tipically you do that by having a procedure for opening the dataset and one for closing it in the datamodule and an integer to count the open and close calls. The procedure which opens the dataset actually opens it only at the first call, at any subsequent call just incremets the counter. The closer procedure decrements the counter at each call, and closes the database when the counter value drops back to 0.

Grenadine answered 20/7, 2011 at 11:6 Comment(0)
F
3

The simplest way to achieve what you want is to create an instance of the data module for each form, and pass it to the form so it can be freed when the form is closed:

var
  Data: TDataModule;
begin
  Data := T<YourDataModule>.Create(Self);
  try
    Form := T<YourForm>.Create(Self);
    Form.DataModule := Data;
    Data.Name := '';
  except
    Data.Free;
    raise;
  end;

  Form.Show;
end;

Setting the DataModule's Name to an empty string is done to ensure that the VCL's logic for hooking up data aware controls to their datasource/dataset is done using the newly created instance, instead of the first ever instance.

In the Form's OnClose handler (or its destructor) make sure to free the data module.

Freemon answered 20/7, 2011 at 11:20 Comment(6)
Sorry but I don't understand the assignment "form.datamodule=data". Is form.datamodule a property of the form?Marvin
@mem100: Not one that exists "out of the box" you will have to add it to your forms yourself. If you do not want to do that for every single form in your application, you could consider creating a base form, add the property to that and then descend all your application's forms from that base form. If you need more info on that, that merits another question altogether... :-)Freemon
ok I understand but I have another clarification. what about the datasources on the form? The datasources are binded to the dataset on the autocreated datamodule. How do I change the datasources so that they use the newly created instance and how do I access the datasets on the newly created instance inside the form create event so I can open themMarvin
ok I did a slight variation on what you said.I defined a private var of type Tdatamodule in the form class. I override the constructor and in it I created the datamodule instance data:=T<mydatamodule>.create(self) Then I assign the form's datasource to the dataset on the new instance using a type cast datasource.dataset:=T<mydatamodule>(data).datasetMarvin
I access the datasets the same way T<mydatamodule>(data).dataset.open. Finally I free the data var in the forms destructor. This seems to work so far.. still testing it outMarvin
@mem100: You can keep the "bindings" at design time. The VCL hooks it all up based on names of instances it finds. That's why you need to set the name of the DataModule to an empty string so it won't be used by a second/third/fourth form and they will use a newly created instance of the data module (which then still has its name). It does require the data module being created before the form's constructor streams in the dfm (where all the names are). So you need to create it before the inherited call in the form's constructor or before the form is created. (an OnCreate handler is too late!)Freemon
D
0

Are you trying to access the same dataset from FormA and FormB at the same time while displaying different data, if so:

Use a TClientDataSet and a TDataSetProvider to load the data from you ADO dataset. Then clone the cursor using ClientDataSet.CloneCursor, you get a seperate cursor to the same data. Then pass them to the forms or assign the controls of FormA to ClientDataSetA and FormB to the clone ClientDataSetB. Reads, writes and updates from both forms change the underlying dataset which can then apply the updates to the database through the ADO dataset later via the DataSetProviders ApplyUpdates.

Look here for some help: http://www.podgoretsky.com/ftp/docs/Delphi/D5/dg/5_ds3.html Or there is a really good book by Cary Jenson: http://www.jensendatasystems.com/cdsbook/ (free plug, but it is a good read)

Dissolve answered 20/7, 2011 at 13:34 Comment(0)
S
0

As you said you need seperate instances, then my solution would be to have a Datamodule variable in each form declaration:

TForm1 = class(TForm)
...
private
  fDatamodule : TDatamodule1;
...
end;

procedure TForm1.FormCreate(Sender : TObject)
begin
  fDatamodule := TDatamodule1.Create(self);
  MyDatasource.Dataset := fDatamodule.MyDataset;
end;

(repeat for Form2 etc)

You have the same datamodule, instanciated twice and thus completely seperate from each other, but utilising the same business logic in each form.

Whilst on the subject, ensure your datamodule code does not make reference to either form. This is bad practice.

Sophiasophie answered 20/7, 2011 at 18:34 Comment(2)
Thank you for your answer. This is what I did but I did it before you posted you answer. Please see my comments to marjan's answer.Marvin
Ah sorry, must've missed that. Glad you got your solution though.Sophiasophie

© 2022 - 2024 — McMap. All rights reserved.