TDADatatable.Locate

hi,
the following problem:
i need to locate a child dataset on 2 fiels
so master value is 1 and child field1 could be 1 or field2 could be 1 or both
so i set up a devexpress quantumgrid with the given master and as detailkeyfieldnames field1 and field2
now this crashes in
procedure TDAMemoryDataset.InitMemLocateStruct(AStruct: PMemLocateStruct;
const KeyValues: Variant);

more specifically:
if AStruct^.lFields.Count = 1 then
AStruct^.lValues[i].Value := KeyValues
else
AStruct^.lValues[i].Value := KeyValues[i];

since i’m passing 2 fields it seems it expects a variant array with 2 field values…
this is not the case nor can i influence this cause it’s devexpress…

imho i should be able to locate 1 value in 2 tfields of this child dataset?

Hi,

Try to declare index that contain both fields.

How I can reproduce this w/o DevEx?
Can you provide a call-stack?

callstack: image

creating index does not seem to work, but i could be doing wrong:
t:tindexdef
t := table.indexdefs.addindexdef;
t.fields := ‘f1; f2’;

creating the index does not seem to work, isCanUsed fails

hmm, I can’t reproduce this case with simple testcase 18962.zip (52.6 KB)

Can you modify it for reproducing original issue?

  1. is my code to create the index on 2 fields correct? i need to use ; to seperate the fields?
  2. the table is a child hooked up to a master, so it gets filled with every master change, does the index still works in that case
  3. do i need to create the index before the table is opened? or does the addindexdef takes care of that?

yes

it depends on your code. you may have fetch details records only for one master row
check the All in one fetch article.
index take attention to M/D relation so it will work

you can create index after table is opened.
the easiest solution - create index in design-time via Object Inspector

all is done in code in our app, no RAD :slight_smile:
i’ll modified your source
that will be more clear i guess
basically i need to display the childs of a given master depending wether child.F1 OR child.F2 equals the master.F1

and the devex grid tries to locate the detail on fields F1;F2 for 1 given master F1

sample ro.zip (57.3 KB)

to be honest, your changes aren’t acceptable :slight_smile:

  b:=false;
  for j := 1 to 5 do
  begin
    if b then
      DAMemDataTable1.AddRecord(['f1'],[j]) //<<< this will create record with empty child_ID, like [j, NULL]
    else   
      DAMemDataTable1.AddRecord(['f2'],[j]); //<<< this will create record with empty master_ID, like [NULL,j]
    b := not b;
  end;
  DAMemDataTable2.Dataset.Locate('f1;f2',3,[]); //<<< master table has only `f1` field

It isnt exactly what right
I’ll change it a bit more on monday

But i guess i only can demonstratie it using devex

Verstuurd mobiel

ok hope this will clear things (see ro.rar)
in fact there is a granddad involved, so both tables in the demo are childs of a granddad
granddad in this case isn’t important
the damaster in the demo is in fact a union of 2 childs of the granddad, and thus they have a ‘childkey’ field and based on there is a marker which child it is, in this case called ‘isF1’
so here’s the deal
in devex i hook both damaster and dachild table in 1 grid, so the details are shown below each master
now devex lets me filter the childs that need to be hooked to the damaster, so i can filter out the recs that do not belong to the damaster based on ‘isF1’ in this case
apparently devex is doing this using locate, so it has to locate on both fields
like this

Accept := False;
AMasterRecord := ADataController.GetMasterRecordIndex;
AMasterDC := ADataController.GetMasterDataController;
if AMasterDC <> nil then
begin
AParentID := AMasterDC.Values[AMasterRecord, detprimkey.Index];
AType := AMasterDC.Values[AMasterRecord, verkooptype.Index];
if AType = 1 then
Accept := AParentID = ADataController.Values[ARecordIndex, dbAnnracvdpprimkey.Index];
if AType = 2 then
Accept := AParentID = ADataController.Values[ARecordIndex, dbAnnracvdmprimkey.Index];
end;

so my demo won’t be able to show the desired behaviour since i can’t filter out the detail data
it has to look at the master for which kind of detail it is and then select that key based on child F1 or F2

ro.rar (52.5 KB)

filter works:

//DAchild.Filtered = True;
procedure TForm90.DADataSource2DataChange(Sender: TObject; Field: TField);
begin
  if DAMaster.Fields[1].IsNull then begin
    DAchild.Filter := '';
  end
  else begin
    if DAMaster.Fields[2].AsBoolean then
      DAchild.Filter := 'f1 = '+DAMaster.Fields[1].AsString
    else
      DAchild.Filter := 'f2 = '+DAMaster.Fields[1].AsString;
  end;
end;

but it doesn’t reproduce original callstack :frowning:

can you reproduce original error with TDAMemDataset.Locate?

hmmm that is a different approach
will see how devex handles that

only reproducable with devex…

doesn’t seem to work with devex
i still have to define the relation in devex to…
and nor tried both child fields as detail fields but then he tries to locate again on ([field1, field2],keyvalue)

and the RO code fails cause it expects an array of variant:
if AStruct^.lFields.Count = 1 then
AStruct^.lValues[i].Value := KeyValues
else
AStruct^.lValues[i].Value := KeyValues[i];

how I can reproduce this error in DA without DevEx?

can you try to pass the same values to TDAMemDataset.Locate as DevEx does?

TDAMemDataset can be accessed via TDAMemDataset(memtable.Dataset)

should be doable
will modify sample

in filterdata event do this
DAchild.Locate(‘f1; f2’,11,[]);

it will break in InitMemLocateStruct

something is wrong with DevEx then.
number of values have to equal to number of fields.
will add this check into Locate method

Thanks, logged as bugs://82162