3
votes

I want to create two objects and link them via a parent child relationship in C# using the Metadata API.

I can create objects and 'custom' fields for the objects via the metadata, but the service just ignores the field def for the relationship.

By snipet for the fields are as follows:

CustomField[] fields = new CustomField[] { new CustomField() 
{ 
  type = FieldType.Text,
  label = "FirstName",
  length = 50,
  lengthSpecified = true,
  fullName = "LJUTestObject__c.FirstName__c"
},
new CustomField() 
{
  type = FieldType.Text,
  label = "LastName",
  length = 50,
  lengthSpecified = true,
  fullName = "LJUTestObject__c.Lastname__c"
},
new CustomField() 
{
  type = FieldType.Text,
  label = "Postcode",
  length = 50,
  lengthSpecified = true,
  fullName = "LJUTestChildObject__c.Postcode__c"
},
new CustomField() 
{
  type = FieldType.MasterDetail,
  relationshipLabel = "PostcodeLookup",
  relationshipName = "LJUTestObject__c.LJUTestObject_Id__c",
  relationshipOrder = 0,
  relationshipOrderSpecified = true,
  fullName = "LJUTestChildObject__c.Lookup__r"
}
};

The parent object looks like:

LJUTestObject
  ID,
  FirstName, Text(50)
  LastName, Text(50)

The child objext looks like:

LJUTestChildObject
  ID,
  Postcode, Text(50)

I want to link the parent to the child so one "LJUTestObject", can have many "LJUTestChildObjects".

What values do I need for FieldType, RelationshipName, and RelationshipOrder to make this happen?

3

3 Answers

1
votes

TL;DR:

Use this as a template for accomplishing what you want:

var cf = new CustomField();
cf.fullName = "ChildCustomObject__c.ParentCustomField__c";
cf.type = FieldType.MasterDetail;
cf.typeSpecified = true;
cf.label = "Parent Or Whatever You Want This To Be Called In The UI";
cf.referenceTo = "ParentCustomObject__c";
cf.relationshipName = "ParentOrWhateverYouWantThisToBeCalledInternally";
cf.relationshipLabel = "This is an optional label";

var aUpsertResponse = smc.upsertMetadata(metadataSession, null, null, new Metadata[] { cf });

The key difference:

The natural temptation is to put the CustomField instances into the fields array of a CustomObject, and pass that CustomObject to the Salesforce Metadata API. And this does work for most data fields, but it seems that it does not work for relationship fields.

Instead, pass the CustomField directly to the Salesforce Metadata API, not wrapped in a CustomObject.

Those muted errors:

Turns out that errors are occurring, and the Salesforce Metadata API knows about them, but doesn't bother telling you about them when they occur for CustomFields nested inside a CustomObject.

By passing the CustomField directly to the Metadata API (not wrapped in a CustomObject), the call to upsertMetadata will still return without an exception being thrown (as it was already doing for you), but this time, if something goes wrong, upsertResponse[0].success will be false instead of true, and upsertResponse[0].errors will give you more information.

Other gotchas

  • Must specify referenceTo, and if it doesn't match the name of an existing built-in or custom object, the error message will be the same as if you had not specified referenceTo at all.

  • fullName should end in __c not __r. __r is for relationship names, but remember that fullName is specifying the field name, not the relationship name.

  • relationshipName - I got it working by not including __r on the end, and not including the custom object name at the start. I haven't tested to be sure other ways don't work, but be aware that at the very least, you don't need to have those extra components in the relationshipName.

  • Remember generally that anything with label in its name is probably for display to users in the UI, and thus can have spaces in it to be nicely formatted the way users expect.

Salesforce... really???

(mini rant warning)

The Salesforce Metadata API is unintuitive and poorly documented. That's why you got stuck on such a simple thing. That's why no-one knew the answer to your question. That's why, four years later, I got stuck on the same thing. Creating relationships is one of the main things you would want to do with the Salesforce Metadata API, and yet it has been this difficult to figure out, for this long. C'mon Salesforce, we know you're a sales company more than a tech company, but you earn trazillions of dollars and are happy to show it off - invest a little more in a better API experience for the developers who invest in learning your platform.

0
votes

I've not created these through the meta data API like this myself, but I'd suggest that:

relationshipName = "LJUTestObject__c.LJUTestObject_Id__c

Should be:

relationshipName = "LJUTestObject__c.Id

as Id is a standard field, the __c suffix is only used for custom fields (not standard fields on custom objects). Also, it may be that the relationship full name should end in __c not __r, but try the change above first and see how you go.

-1
votes
SELECT 
    Id,
    OwnerId,
    WhatId,
    Reminder_Date_Time__c, 
    WhoId, 
    Record_Type_Name__c, 
    Task_Type__c,
    Assigned_Date__c, 
    Task_Status__c, 
    ActivityDate, 
    Subject, 
    Attended_By__c,
    Is_Assigned__c 
FROM Task 
WHERE 
    (NOT Task_Status__c LIKE 'Open') AND 
    ActivityDate >= 2017-12-13 AND 
    (NOT Service__r.Service_State__c LIKE 'Karnataka')