AnyDAC
ContentsIndexHome
PreviousUpNext
Master-Detail Relationship

AnyDAC has flexible support for master-detail relationship between datasets.

Group
Links
General

The master-detail relationship allows automatically filter a detail dataset basing on a current master dataset record. For example, the master dataset has "Order" records, and the detail dataset has "Order Line" records. So the detail dataset shows only lines for the current order. 

Special setup is not required for a master dataset. AnyDAC offers two base methods to setup a detail dataset in a master-detail relationship:

  • Parameter based. The master dataset field values are assigned to the detail TADQuery or TADStoredProc parameters, then detail dataset query is reexecuted.
  • Range based. The master dataset field values are used to apply a range to the detail dataset. The detail dataset may be any AnyDAC dataset with current active index.

These methods may be combined. To choose between them consider the table:

Feature 
Parameter based 
Range based 
The detail query returns a limited number of records. 
 
The detail records are fresh. 
 
Reduced traffic and DBMS workload on each master change. 
 
The cached updates preserved on the master change. 
 
Works in offline mode. 
 
Supports Centralized Cached Updates with propagation 
 

Also, AnyDAC offers two cached updates modes for the datasets in master-detail relationship:

  • Decentralized Cached Updates mode, when each dataset tracks the changes independently on others;
  • Centralized Cached Updates mode, when few datasets in master-detail relationship share a single change log. And master dataset may propagate changes to the detail datasets in cascading fashion, including auto-incrementing field values.

 

Parameters based M/D

To setup the parameters based M/D relationship, perform the following steps:

  1. Drop TADQuery (or any other AnyDAC dataset) on a form. Name it qOrders. That will be a master dataset. Setup it - lets assign the SQL:

 

SELECT * FROM {id Orders}

 

  1. Drop TDataSource on a form. Name it dsOrders. Set it DataSet to qOrders.
  2. Drop TADQuery on a form. Name it qOrderDetails. That will be a detail dataset. Setup it - lets assign the SQL:

 

SELECT * FROM {id Order Details} WHERE OrderID = :OrderID

 

Then set MasterSource to dsOrders. The base setup is finished. 

So, how it works ? AnyDAC builds for qOrderDetails a list of pairs - qOrders fields and qOrderDetails parameters. Elements in each pair:

  • when MasterFields is not specified, then have the same name;
  • otherwise - have the same position, fields in MasterFields list, parameters in Params collection.

When the current qOrders record is changed, AnyDAC assigns for each parameter a corresponding field value. In our case qOrderDetails :OrderID parameter gets the qOrder OrderID field value. After that the qOrders is reexecuted. 

Note, the BeforeOpen and AfterOpen events do not fire for detail dataset. Use OnMasterSetValue instead. 

 

Range based M/D

To setup the range based M/D relationship, perform the following steps:

  1. Drop TADQuery (or any other AnyDAC dataset) on a form. Name it qOrders. That will be a master dataset. Setup it - lets assign the SQL:

 

SELECT * FROM {id Orders}

 

  1. Drop TDataSource on a form. Name it dsOrders. Set it DataSet to qOrders.
  2. Drop TADQuery on a form. Name it qOrderDetails. That will be a detail dataset. Setup it - lets assign the SQL:

 

SELECT * FROM {id Order Details}

 

Then set MasterFields to ORDERID, IndexFieldNames to ORDERID and MasterSource to dsOrders. The base setup is finished. 

So, how it works ? AnyDAC builds for qOrderDetails a list of pairs - qOrders and qOrderDetails fields, where fields in each pair have the same position, master fields in MasterFields, detail fields in IndexFieldNames

When the current qOrders record is changed, AnyDAC applies the range to qOrderDetails, where details fields are equal to corresponding master fields. In our case qOrderDetails OrderID field is equal to qOrder OrderID field. 

 

Combining methods

To combine both methods an application should use both Parameters and Range based setups and include fiDetails into FetchOptions.Cache. Then AnyDAC at first will use range based M/D. And if a dataset is empty, then AnyDAC will use parameters based M/D. The new queried records will be appended to the internal records storage. 

Also the OnMasterSetValues event handler may be used to override M/D behavior. 

 

Editing detail dataset

When a new record is inserted into detail dataset, the fields participating in a M/D relationship will be filled automatically by the corresponding master dataset field values. The detail dataset field list is defined:

  • For parameter based M/D - DetailFields, if specified. Otherwise - the fields with the same names as the parameters.
  • For range based M/D - the index fields.

To insert a detail record, the master dataset must be in the browsing state (dsBrowse). It is impossible to have both master and detail dataset in inserting (dsInsert) or editing (dsEdit) state. 

When master and details datasets are in the cached updates mode, then application may use TADSchemaAdapter to enable Centralized Cached Updates with propagation. For that master and details datasets SchemaAdapter property must point to the same TADSchemaAdapter and detail dataset FetchOptions.DetailCascade is True. 

 

Navigating in M/D

When an application needs to navigate in a master dataset, on each master record change the detail dataset will be refreshed. That consumes the resources and the navigation may be slow. To temporary disable M/D synchronization, application may call DisableControls / EnableControls for the master dataset: 

 

qOrders.DisableControls;
try
  qOrders.First;
  while not qOrders.Eof do begin
    .....
    qOrders.Next;
  end;
finally
  qOrders.EnableControls;
end;

 

To force disabled M/D synchronization, call ApplyMaster method on the master dataset. To temporary disable M/D synchronization for a specific detail dataset, use DisableScroll / EnableScroll methods of the dataset MasterLink property: 

 

qOrderDetails.MasterLink.DisableScroll;
try
  qOrders.First;
  while not qOrders.Eof do begin
    if qOrders.FieldByName('OrderID').AsInteger = 100 then begin
      qOrderDetails.ApplyMaster;
      // read qOrderDetails dataset - it is synchronized with qOrders
    end;
    qOrders.Next;
  end;
finally
  qOrderDetails.MasterLink.EnableScroll;
end;

 

The GUI applications may benefit from delayed M/D synchronization. So, when an user scrolls in a grid, a detail dataset will be refreshed not immediately, but after some delay and if there were no other navigations. To use that set FetchOptions.DetailDelay for a detail dataset. To temporary disable delayed M/D synchronization for a specific detail dataset and use immediate synchronization, use DisableDelayedScroll / EnableDelayedScroll methods of the MasterLink property. 

By default a state change, a non-key field values change, refreshing of master dataset will not lead to detail dataset refreshing. This avoids extra refreshes of detail dataset. If your application expects always refresh detail dataset, then set FetchOptions.DetailOptimize to False.

See the following demos for additional details:

  • AnyDAC\Samples\Comp Layer\TADQuery\MasterDetail;
  • AnyDAC\Samples\Comp Layer\TADMemTable\MasterDetail.
What do you think about this topic? Send feedback!