DgnECManager::ObtainInstanceEnabler() returns null

Hello again,

does anyone have a clue, why this method could return null?

DgnECInstanceEnablerP enabler = DgnECManager::GetManager().ObtainInstanceEnabler(*ecClass, *dgnFileP);

  • checked the ecSchema -> correctly imported, not null
  • checked ecClass -> correctly found inside schema, not null
  • well the dgnFile is the active one, so not null

But still I'm not able to return my enabler. Until yesterday it worked (most of the time). Hopefully someone has an idea WHERE I can look further for errors...

Best regards,
Stephan

  • Hi Stephan,

    does anyone have a clue, why this method could return null?

    without knowing context (to see code and to have DGN file), it's hard to guess.

    Until yesterday it worked (most of the time).

    "Most of the time"? It has to work always!

    Hopefully someone has an idea WHERE I can look further for errors...

    You can try alternatives (at least as a way to find what is wrong):

    • To use ObtainInstanceEnablerByName
    • To try to use the same method in NET (which seems to call native method at background)

    With regards,

      Jan

  • Hi Jan,

    thank you for your quick reply. I know that my "example" is a little bit short.

    There is a class that handles EC stuff, assures ecClasses and ecSchemas and gets called by a DgnElementSetTool, so I had no idea how to recreate the context in a meaningful way.

    I have tried to call ObtainInstanceEnablerByName, same result.

    I will strip down the code as much as possible and hope to be able to post what went wrong soon.

  • Hello Jan,

    I guess I got confused with my ecSchemas. Here is a snipped of code. I don't understand why I got the message "SCHEMAUPDATE_SchemaNotFound" though a few lines above it seems to locate the schema succesfully.

    void SpacesHandler::UpdateSchema()
    {
    	LabelloUtil::PrintDebugMessage(L"SpacesHandler::UpdateSchema");
    
    	DgnECManagerR ecMan      = DgnECManager::GetManager();
    	DgnFileP      pDgnFile   = ISessionMgr::GetActiveDgnFile();
    
    	SchemaInfo    schemaInfo = { ECN::SchemaKey(L"IA_OfficeSchema", 1, 0), *pDgnFile };
    	ECN::ECSchemaPtr pSchema = ecMan.LocateSchemaInDgnFile(schemaInfo, ECN::SchemaMatchType::SCHEMAMATCHTYPE_LatestCompatible);
    	if (pSchema == NULL) {
    		LabelloUtil::PrintErrorMessage(L"Schema not found.");
    		return;
    	}
    
    
    	SchemaUpdateStatus status = ecMan.UpdateSchema(*pSchema, *pDgnFile, true);
    
    	switch(status) {
    		case SCHEMAUPDATE_Success : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_Success"); break;
    		case SCHEMAUPDATE_SchemaNotFound : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_SchemaNotFound"); break;
    		case SCHEMAUPDATE_NotValidECSchemaXml : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_NotValidECSchemaXml"); break;
    		case SCHEMAUPDATE_FailedToWriteElement : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_FailedToWriteElement"); break;
    		case SCHEMAUPDATE_FailedToSerializeAsXml : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_FailedToSerializeAsXml"); break;
    		case SCHEMAUPDATE_ProviderDoesNotSupportUpdate : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_ProviderDoesNotSupportUpdate"); break;
    		case SCHEMAUPDATE_ProviderNotFound : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_ProviderNotFound"); break;
    		case SCHEMAUPDATE_FailedToDeserializeXmlFile : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_FailedToDeserializeXmlFile"); break;
    		case SCHEMAUPDATE_SchemaVersionMismatch : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_SchemaVersionMismatch"); break;
    		case SCHEMAUPDATE_ProviderDoesNotSupportInstanceUpdate : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_ProviderDoesNotSupportInstanceUpdate"); break;
    		case SCHEMAUPDATE_FailedToUpdateInstances : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_FailedToUpdateInstances"); break;
    		case SCHEMAUPDATE_FailedToUpdateReferencingSchemas : LabelloUtil::PrintErrorMessage(L"SCHEMAUPDATE_FailedToUpdateReferencingSchemas"); break;
    	}
    }

  • Hi Stephan,

    why do you obtain schema from DGN at first and update the same schema immediately. Moreover, why it's update as external schema (which obviously not, because is located by LocateSchemaInDgnFile).

    I am not very familiar with native EC API (as I use usually C#), but I think it's not correct approach. I am not sure whether UpdateSchema (which makes no sense at all in my opinion) does not invalidate pointer, especially when the updated schema is marked as external one.

    With regards,

      Jan

  • Yes, the function makes no sense at the moment, sorry. It was just a part of code of which I thought it is complete and should work on its own.

    But after posting this I realized by reading the docs that I probably need to create a "new" schema by reading an xml file and use that newly created schema to update the schema that was succesfully located inside the dgn file. However I think it has my errors have something to do with schemas and will concentrate on this.

    Thank you for help!

  • But after posting this I realized by reading the docs that I probably need to create a "new" schema by reading an xml file and use that newly created schema to update the schema that was succesfully located inside the dgn file.

    No, why? It simply does not make the sense!

    When there is EC schema already stored in DGN and you want to attach defined EC class to an element, there is no reason to do anything with the schema. Of course, to update schema, because it has changed, maybe necessary sometimes, but it's different topic and should be avoided (you should ensure the change will not break stored data etc.).

    BTW How the schema was created? Using Bentley Class editor as xml file and imported to DGN file, or dynamically using EC API?

    Regards,

      Jan

  • Yes, you are right. It didn't make any sense at all. It was just a badly choosen example. Below I have a better example about how I read the schemas into the file. And it seems to work now. (Example is stripped down, no error checking, no messages)

    void SpacesHandler::AssureSchemas()
    {
    	DgnECManagerR dgnECManager = DgnECManager::GetManager();
    	DgnFileP      dgnFileP     = ISessionMgr::GetActiveDgnFile();
    
    	WCharCP       iaOfficePath = L"C:\\Program Files\\Bentley\\MicroStation CONNECT Edition\\MicroStation\\Mdlapps\\IA_OfficeSchema.01.00.ecschema.xml";
    
    	// Is schema already stored in that dgn file?
    	if (_iaOfficeSchemaPtr == NULL) {
    		SchemaInfo schemaInfo = { ECN::SchemaKey(L"IA_OfficeSchema", 1, 0), *dgnFileP };
    		schemaInfo.SetStoredSchema(true);
    		
    		_iaOfficeSchemaPtr = dgnECManager.LocateSchemaInDgnFile(schemaInfo, ECN::SchemaMatchType::SCHEMAMATCHTYPE_Identical);
    	}
    
    	// No schema stored, so read and import it.
    	if (_iaOfficeSchemaPtr == NULL) {
    		dgnECManager.ReadSchemaFromXmlFile(_iaOfficeSchemaPtr, iaOfficePath, dgnFileP);
    		dgnECManager.ImportSchema(*_iaOfficeSchemaPtr, *dgnFileP, false)
    	}
    }

    But I don't understand how I can handle updates. Maybe I want to add a class to that schema later. How would the code know that it has to update the schema?

    I created the schema manually inside my text editor to get a better understanding about how these are structured.

  • I don't understand how I can handle updates. Maybe I want to add a class to that schema later.

    Use schema versioning?  Create a new schema with the new class.  The new schema includes all the classes of the existing schema plus the additional class.  That's why options such as ECN::SchemaMatchType::SCHEMAMATCHTYPE_LatestCompatible exist.

     
    Regards, Jon Summers
    LA Solutions

    Answer Verified By: Stephan L. 

  • I created the schema manually inside my text editor to get a better understanding about how these are structured.

    Ohhh ... shi***

    Please, never do that! I cannot imagine how you want to be able to achieve the definition quality. And frankly, creating XML file without knowing formal definition does not help any understanding in my opinion. Moreover, the xml structure is implementation detail that is Bentley responsibility, not ours.

    The only acceptable ways are to use Bentley Class editor (delivered with V8i SDK and some other products) or using API inside DGN file (where it can be exported to xml later).

    But I don't understand how I can handle updates.

    It's your responsibility as developer to design EC schema in such way there will be no need to modify for as long as possible time period. It's similar to database model (schema) update: It's not expected it will be changed often, because it's sensitive and complex operation.

    Thinking about my schemas, I see two typical scenarios:

    • Ad-hoc dynamically generated schemas, similarly to e.g. when SHP file is attached. In these cases no compatibility between design files is required or ensured, every DGN can be different (usually they are pretty similar, but not necessarily the same).
    • Fixed schemas, used by application(s) as standard, imported to DGN files when used. Often these schemas lived for a long time (several months) internally during development and testing phases to ensure they cover all alternatives. Because to change the schema is expensive.
    How would the code know that it has to update the schema?

    It is your responsibility as developer to establish such rules and implement relevant code.

    When a new application version comes with new EC schema version (should happen rarely), it has to check always whether older version exists in DGN file or not. When the update introduces not breaking changes (e.g. new EC class is added, but old remain the same), a simple import would be enough. But when it's compatibility breaking change (existing EC class is modified), the conversion can be complicated and similar to ETL processes.

    EC schemas use xx.yy versioning, but it's more formal than strictly required. But an idea is that xx express breaking changes and yy non breaking evolution.

    BTW iModelJS and BIS schemas use more format and better semantic versioning, that is not backward compatible (because of xx.yy vs xx.yy.zz numbering), but can be used as inspiration.

    With regards,

      Jan

    Answer Verified By: Stephan L. 

  • ObtainInstanceEnabler(*ecClass, *dgnFileP);

    Definitely ensure "enabler" (DgnECInstanceEnablerP), or "ecClass" (ECClassP) if returning nullptr can safely exits your routine.

    Does declaring dgnFile as DgnFileR change your outcome/results any?