itwinjs-core icon indicating copy to clipboard operation
itwinjs-core copied to clipboard

`ECClass.getDerivedClasses()` doesn't include classes from referenced schemas

Open grigasp opened this issue 8 months ago • 5 comments

Describe the bug

When an iModel / ECDb contains schemas, whose classes are derived from by classes in referencing schemas, calling ECClass.getDerivedClasses() on a base class form referenced schema doesn't return any classes.

To Reproduce

To reproduce the behavior, run this test:

it.only("`ECClass.getDerivedClasses()` doesn't include classes from referenced schemas", async function () {
  await IModelHost.startup();

  // create a new ECDb
  const ecdbPath = "/home/grigas/test.ecdb";
  IModelJsFs.unlinkSync(ecdbPath);
  const ecdb = new ECDb();
  ecdb.createDb(ecdbPath);

  // import referenced schema
  const schemaXml1 = `
    <?xml version="1.0" encoding="UTF-8"?>
    <ECSchema schemaName="TestSchema1" alias="Test1" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
      <ECEntityClass typeName="X" />
    </ECSchema>
  `;
  const schemaFilePath1 = "/home/grigas/test_schema1.xml";
  fs.writeFileSync(schemaFilePath1, schemaXml1);
  ecdb.importSchema(schemaFilePath1);
  ecdb.saveChanges();

  // import referencing schema
  const schemaXml2 = `
    <?xml version="1.0" encoding="UTF-8"?>
    <ECSchema schemaName="TestSchema2" alias="Test2" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
      <ECSchemaReference name="TestSchema1" version="01.00.00" alias="test1" />
      <ECEntityClass typeName="Y">
        <BaseClass>test1:X</BaseClass>
      </ECEntityClass>
    </ECSchema>
  `;
  const schemaFilePath2 = "/home/grigas/test_schema1.xml";
  fs.writeFileSync(schemaFilePath2, schemaXml2);
  ecdb.importSchema(schemaFilePath2);
  ecdb.saveChanges();

  // set up schema locater
  const locater = new SchemaJsonLocater((name) => ecdb.getSchemaProps(name));
  const context = new SchemaContext();
  context.addLocater(locater);
  const testSchema1 = await context.getSchema(new SchemaKey("TestSchema1"), SchemaMatchType.Latest);
  const classX = await testSchema1!.getItem("X", EntityClass);
  const derivedClasses = await classX!.getDerivedClasses();
  expect(derivedClasses!.length).to.equal(1);

  await IModelHost.shutdown();
});

Expected behavior

  • Calling ECClass.getDerivedClasses() on a base class, returns all its derived classes, including ones from referencing schemas.
  • The test passes.

Desktop (please complete the applicable information):

  • OS: Ubuntu
  • Node.js version 20.18.2
  • iTwin.js version 5.0.0-dev.92

Additional context

I understand it might be problematic to get derived classes from referencing schemas, if those schemas haven't been loaded. If fixing this is not possible, the situation should be clearly explained on affected methods (I suspect that might be more than just getDerivedClasses()), and, ideally, a recommendation on how to get those derived classes should be provided.

grigasp avatar May 06 '25 06:05 grigasp

@ColinKerr what do you think about this? My opinion is that loading derived schemas is not a good idea as that basically translates into "try and load every single schema you can find".

Maybe we can provide some specialized method which does just that, but limiting the schemas loaded to the ones inside of the imodel?

Other than that I'd say like Grigas suggests, we should document the limitation

rschili avatar May 12 '25 06:05 rschili

With incremental loading of Stub classes I think it is fine if we support this. @christophermlawson, @StefanApfel-Bentley @AureMonke please investigate this

ColinKerr avatar Aug 08 '25 15:08 ColinKerr

The problem is derived classes are not a part of the schema. The 'base' schema (TestSchema1) contains no references so the API has no way to know to look for schemas that reference it. If you load both schemas before the getDerivedClasses call, then the correct results are returned. We need to discuss how to support what Grigas is trying to do. As things currently stand, loading all pertinent schemas first resolves the issue. Incremental loading will not address this problem. Derived classes is the only outlier here. All other metadata information is contained withing the schema. I can add documentation to this method to describe this limitation.

christophermlawson avatar Aug 13 '25 11:08 christophermlawson

For the incremental Schema Loading, I could imagine we can query that information on the class stubs and construct the tree internally, even if the items not fully loaded yet. I'd like to do some deeper investigation here, since that would be another complex query on the time sensitive stubs query.

StefanApfel-Bentley avatar Aug 29 '25 11:08 StefanApfel-Bentley

https://github.com/iTwin/itwinjs-core/pull/8795. Hey, I created a PR fixing this issue. Please let me know if I missed anything.

ashmitKmishra avatar Nov 21 '25 21:11 ashmitKmishra