[SalesForce] Create Child Custom Metadata records through the Metadata API

I have a requirement where I need to create parent and child custom metadata in an org programmatically. Since DML operations are not supported for creating custom metadata records, I am calling the Metadata API via Apex in order to create the parent and child custom metadata records. The parent records are created successfully. However, when I attempt to create the child records and pass the parent record id to the child in Apex, I receive an INVALID CROSS REFERENCE KEY error. I am not sure if this is because setting a parent record when calling Metadata API via Apex is not allowed or if there is something else I am doing wrong.

Please see the code block below with relevant comments. This is the full method I am using to create both parent and child custom metadata records:

    public PageReference createMetadata() {

        // Initialize Web Service
        MetadataService.MetadataWebSvc service = POC_MetadataServiceUtil.createService();

        if(mPageConfigs.containsKey(pageName)) {
            // Retrieve PageConfig object for selected page name
            PageConfig config = mPageConfigs.get(pageName);
            system.debug(config);
            // Intialize Custom Metadata type for Metadata API - should be made dynamic after initial POC is complete so field names are not hard coded
            MetadataService.CustomMetadata newCustomMetadataType = new MetadataService.CustomMetadata();
            newCustomMetadataType.description = '';
            newCustomMetadataType.label = config.name;
            newCustomMetadataType.fullName = 'Screen_Configuration.' + config.screenConfigName;

            MetadataService.CustomMetadataValue pageNameValue = new MetadataService.CustomMetadataValue();
            pageNameValue.field = 'Page_Name__c';
            pageNameValue.value = config.name;

            MetadataService.CustomMetadataValue screenConfigNameValue = new MetadataService.CustomMetadataValue();
            screenConfigNameValue.field = 'Screen_Name__c';
            screenConfigNameValue.value = config.screenConfigName;

            MetadataService.CustomMetadataValue lastSyncDateValue = new MetadataService.CustomMetadataValue();
            lastSyncDateValue.field = 'Last_Sync_Date__c';
            // Populate Last Synce Date using GMT format
            lastSyncDateValue.value = String.valueOf(System.now());

            MetadataService.CustomMetadataValue typeValue = new MetadataService.CustomMetadataValue();
            typeValue.field = 'Type__c';
            // Populate Last Synce Date using GMT format
            typeValue.value = config.type_x;

            newCustomMetadataType.values = new MetadataService.CustomMetadataValue[]{};
            newCustomMetadataType.values.add(pageNameValue);
            newCustomMetadataType.values.add(screenConfigNameValue);
            newCustomMetadataType.values.add(lastSyncDateValue);
            newCustomMetadataType.values.add(typeValue);

            ****** THIS IS WHERE I UPSERT THE PARENT CUSTOM METADATA RECORD *******
            List<MetadataService.UpsertResult> results = service.upsertMetadata(new MetadataService.Metadata[] { newCustomMetadataType });
            *** If parent metadata creation is successful, create child metadata records ***
            if(!results.isEmpty() && results[0].success == true) {
                // Retrieve Id for parent custom metadata record
                String screenName = config.screenConfigName;
                *** I AM SELECTING THE NEWLY CREATED CUSTOM METADATA PARENT RECORD ID ***
                Screen_Configuration__mdt[] lCreatedMetadata = [SELECT Id FROM Screen_Configuration__mdt WHERE Screen_Name__c = :screenName LIMIT 1];
                if(!lCreatedMetadata.isEmpty()) {
                    // Initiate list of custom metadata objects to create
                    MetadataService.Metadata[] lMetadataObjects = new MetadataService.Metadata[]{};
                    Map<Schema.SObjectField,String> mFieldValues = new Map<Schema.SObjectField,String>();
                    // Loop through component list in page config object
                    for(Component cmp : config.components) {
                        // Build temporary map of all custom metadata fields to pass to util method
                        mFieldValues.put(Screen_Component_Configuration__mdt.Component_Name__c, cmp.name);
                        mFieldValues.put(Screen_Component_Configuration__mdt.Component_Namespace__c, 'c');
                        mFieldValues.put(Screen_Component_Configuration__mdt.Order__c, cmp.name);
                        mFieldValues.put(Screen_Component_Configuration__mdt.Component_Name__c, String.valueOf(cmp.sortOrder));
                        mFieldValues.put(Screen_Component_Configuration__mdt.Properties__c, 'test');    
**SET PARENT RECORD IF FOR EACH CHILD CUSTOM METADATA RECORD. I AM USING ID CLASS TO MAKE SURE RECORD ID IS 18 CHARACTERS**
    mFieldValues.put(Screen_Component_Configuration__mdt.Screen_Configuration__c, String.valueOf(Id.valueOf(lCreatedMetadata[0].id)));
                        mFieldValues.put(Screen_Component_Configuration__mdt.Type__c, 'TESTTYPE');

                        // Build field value objects for custom metadata
                        MetadataService.CustomMetadataValue[] lFieldValues = POC_MetadataServiceUtil.createCustomMetadataValues(mFieldValues);
                        // Build custom metadata object
                        MetadataService.CustomMetadata childCustomMetadataType = POC_MetadataServiceUtil.createCustomMetadataObject('Test Description', 'Screen_Component_Configuration.'+cmp.name, cmp.name, lFieldValues);
                        lMetadataObjects.add(childCustomMetadataType);
                    }
                    // Upsert list of child custom metadata records through metadata api
                    List<MetadataService.UpsertResult> childResults = service.upsertMetadata(new List<MetadataService.Metadata>(lMetadataObjects));
                    system.debug(childResults); 
                }
            }
            PageReference p = ApexPages.CurrentPage();
            p.setRedirect(true);
            return p;
        } else {
            return null;
        }
      }

When the upsert on the child records occurs, the SOAP message I receive is :

In field: Screen_Configuration__c – no CustomMetadata named
Screen_Configuration.m0015000000L9vtAAC found.

The status code for the SOAP response is :

INVALID_CROSS_REFERENCE_KEY

Best Answer

I actually just found the answer to this question here.

The documentation states the following:

When setting the value for relationship fields, use the qualified API name of the related metadata, not the ID.

Thanks everyone!