Whats more exciting than using the Force.com platform to build great solutions? Extending it!
Custom Metadata (GA in Summer’15) came about through Salesforce’s realisation that there is a new breed of solutions being built on the platform that aim to extend its capabilities for use by admins to help them build even more complex solutions without code. Hence its original code name ‘Platform on Platform‘ (which i am still quite fond of btw). You can read more about my thoughts on such applications A Declarative Rollup Summary Tool for Force.com Lookup Relationships and my blog on FinancialForce.com Declarative Thinking: Apps to build Apps.
Such solutions leverage Custom Objects or Custom Settings as a means to store configuration. The point here, is this is really configuration not end user data. So when it came time to migrate such configuration from a Sandbox to Production, Change Set deployments don’t see record data. Administrators then have the choice of manually re-entering configuration or using Data Loader tools, which actually does work with Custom Settings btw.
Through the proof of concept i’ve written for this blog, i’ve experienced how Custom Metadata integrates with Change Sets and pleased to say it works very well. You can read more about such use cases in Introducing custom metadata types: the app configuration engine for Force.com.
For those building AppExchange packages that leverage other packages exposing Custom Metadata, such packages can even package configuration and avoid the need to post install scripts to inject configuration. These benefits are just the start as it happens! Salesforce has an exciting roadmap for Custom Metadata, including the ability eventually to physically extend the available Custom Field types with those expressed via Custom Metadata types. Read more in How to use custom metadata types to save years of development on app configurations.
Is Custom Metadata something i should be considering right now?
Its certainly early days for this very promising feature, depending on the complexity of your configuration information, this may still fall into the ‘one to watch‘ category. The good news is there appears to be a strong roadmap and in the meantime quite few resources (i’ll list them all at the bottom of this blog) and an active Chatter Group with Salesforce PM’s and Developers to help you out!
In order to get a better understanding of it, i’ve rolled up my sleeves and spent this weekend digging in! I decided to use the Declarative Lookup Rollup Summary open source package as a use case to perform a small proof of concept outlined in this blog and the next, to help me understand the feature better and plan what might be needed to update the rollup tool to use it. The rollup tool, as described above, has indeed received a number of queries and requests over its time for a better way to migrate its configure between Sandbox and Production. This blog and the next, contains my findings and recommendations.
Defining Custom Metadata
A Custom Metadata type or MDT for short, is a collection of Custom Field‘s that represent your desired configuration. MDT’s are in fact expressed using the existing Custom Object metadata type, much like the approach Salesforce took with Custom Settings. In this case however you inform the platform that your expressing an MDT by using the file suffix, __mdt, for example LookupRollupSummary__mdt.object. You can read more here.
The following is a cut down version of the .object file i created for my first MDT. I started by copy and pasting the existing Custom Object definition from the rollup tool into it. Then removing unsupported stuff like layouts and action overrides to get it to upload. I also converted Picklist fields to Text, as these are also not supported at present (check out the implementation guide for supported field types).
It took me only a few minutes to create my first MDT object, pretty cool! You can view the full version here.
<?xml version="1.0" encoding="UTF-8"?> <CustomObject xmlns="http://soap.sforce.com/2006/04/metadata"> <fields> <fullName>Active__c</fullName> <defaultValue>false</defaultValue> <deprecated>false</deprecated> <externalId>false</externalId> <inlineHelpText>For Realtime rollups can only be set when the Child Apex Trigger has been deployed.</inlineHelpText> <label>Active</label> <trackTrending>false</trackTrending> <type>Checkbox</type> </fields> <fields> <fullName>AggregateOperation__c</fullName> <deprecated>false</deprecated> <externalId>false</externalId> <inlineHelpText>Rollup operation.</inlineHelpText> <label>Aggregate Operation</label> <trackTrending>false</trackTrending> <type>Text</type> <length>32</length> </fields> <fields> <fullName>AggregateResultField__c</fullName> <deprecated>false</deprecated> <externalId>false</externalId> <inlineHelpText>API name of the field that will store the result of the rollup on the Parent Object, e.g. AnnualRevenue</inlineHelpText> <label>Aggregate Result Field</label> <length>80</length> <required>true</required> <trackTrending>false</trackTrending> <type>Text</type> <unique>false</unique> </fields> <label>Lookup Rollup Summary</label> <pluralLabel>Lookup Rollup Summaries</pluralLabel> <visibility>Public</visibility> </CustomObject>
Unlike Custom Object’s and Custom Setting’s there is currently no UI under the Setup menu or facility under the Schema Builder to create these. Once you have defined your MDT through a .object file, you need to deploy it (within the /objects folder). You can zip it up and use Developer Workbench, as per the description in the Custom Metadata Implementation Guide, however i’ve used MavensMate as I wanted to continue editing it afterwards.
DEPLOYMENT NOTE: If your uploading the file with MavensMate (or Force.com IDE for that matter). Make sure your using the latest API version 34.0 in your editors configuration. This is important to ensure you can express the correct level of visibility for the MDT, much like Custom Settings the options are protected and public.
One you have uploaded it, much like a Custom Object, the object will appear in your orgs schema. Note that there is currently no standard UI (tabs, layouts etc) for editing MDT records (its on the roadmap though!). You have to develop your own UI using Visualforce or Lightning. Initially I took a peak through Developer Workbench at what i had got, a LookupRollupSummary__mdt object!
You can see the usual Id field, but also few other new Standard fields have been created in addition to those custom fields described in the .object file. I found that the DeveloperName and its more fully qualified version QualifiedAPIName are good ways to reference records in MDT objects. You can read more about the standard fields for MDT’s here.
NOTE: The custom fields are prefixed by cmdpoc (Custom Metadata POC) in the screenshot above as this is the namespace used when i packaged the MDT object, something i will cover in more detail in the next blog.
Writing to Custom Metadata Objects
Your MDT objects are available within Apex as an SObject, like any other Custom Object would be. However at present you cannot use DML to write to it. Instead Salesforce have provided create, update and delete operations via their SOAP Metadata API and a new generic Metadata type called CustomMetadata. This holds a name value pair list of your MDT objects field values when inserting and updating records.
I was pleased and proud to see Salesforce themselves leveraging the Apex Metadata API open source library to work around this current limitation in their own code samples. FinancialForce.com (provider of the library) even get a credit in the implementation guide! So until, the much awaited ability to update Metadata natively in Apex arrives (up vote here), Apex Metadata API will come to the rescue!
As mentioned above the generic Metadata API’s CustomMetadata type can be used to wrap configuration to be written or updated for MDT objects. Yet Salesforce have given us a wonderfully type safe SObject in Apex. Even though we cannot use DML on it, i was determined to stretch out my use of it! Thus the CustomMetadataService class was born!
The following code sample taken from a controller I developed to allow the user to edit MDT records, will insert or update a MDT record from Apex for the given MDT’s SObject’s. In the next blog I’ll go into more detail about this.
public with sharing class ManageLookupRollupSummariesController { public LookupRollupSummary__mdt LookupRollupSummary {get;set;} public PageReference save() { try { // Insert / Update the rollup custom metadata if(LookupRollupSummary.Id==null) { CustomMetadataService.createMetadata( new List<SObject> { LookupRollupSummary }); } else { CustomMetadataService.updateMetadata( new List<SObject> { LookupRollupSummary }); } } catch (Exception e) { ApexPages.addMessages(e); } return null; } }
The CustomMetadataService class, wraps the MetadataService class, that is supplied as part of the Apex Metadata API library. As you can see from the comments at the top of the class it still needs some more work, but was enough for now for my proof of concept. My aim is to model as much around the DML methods on the Database class as possible, and hopefully aid one day a smoother transition.
I have added updateMetadata and deleteMetadata methods to this utility class as well. You can see it in action through this Visualforce Controller i wrote, more on this later though!
Reading from Custom Metadata Objects
SOQL is supported for reading records within MDT objects and works pretty much as you would expect when querying Custom Objects. Though much as Salesforce has done with reading Custom Settings (via getInstance), your MDT SOQL queries are free from the watchful eye of the governor (though the 50k row limit still applies). Thank you Salesforce!
// Listing List<SelectOption> options = new List<SelectOption>(); options.add(new SelectOption('[new]','Create new...')); for(LookupRollupSummary__mdt rollup : [select DeveloperName, Label from LookupRollupSummary__mdt order by Label]) options.add(new SelectOption(rollup.DeveloperName,rollup.Label)); // Querying LookupRollupSummary__mdt lookupRollupSummary = [select Id, Label, Language, MasterLabel, NamespacePrefix, DeveloperName, QualifiedApiName, ParentObject__c, RelationshipField__c, ChildObject__c, RelationshipCriteria__c, RelationshipCriteriaFields__c, FieldToAggregate__c, FieldToOrderBy__c, Active__c, CalculationMode__c, AggregateOperation__c, CalculationSharingMode__c, AggregateResultField__c, ConcatenateDelimiter__c, CalculateJobId__c, Description__c from LookupRollupSummary__mdt where DeveloperName = :selectedLookup];
Custom Metadata Packaging, Custom UI’s and using with Change Sets
This is already quite a large blog post, so i’m going to continue my walk through of my POC in a follow up blog. In this blog we will take a closer look at packaging MDT’s and Visualforce page and controller i built to allow users to edit the records. Finishing up with a walk through of how Change Sets where used to migrate the MDT data between my test Sandbox and Enterprise orgs. Catch you next time!
Ok…. in the meantime, have a wonder through a Gist of the full POC code.
Other Great Custom Metadata Resources!
Here are some resources i found while doing my research, please let me know if there are others, and i’ll be happy to add them to the list!
- Introducing custom metadata types: the app configuration engine for Force.com
- How to use custom metadata types to save years of development on app configurations
- Testing Custom Metadata Types
- SFDCConfig (Sample Configuration App by Salesforce)
- Custom Metadata Loader
- Sample application for custom metadata types: “Reusable picklists” framework and tooling.
- Community Chatter Group
- Implementation Guide
- Summer’15 Release Notes
June 27, 2015 at 1:39 pm
Reblogged this on SutoCom Solutions.
Pingback: Custom Metadata, Custom UI’s, Packaging and Change Sets | Andy in the Cloud
Pingback: Declarative Lookup Rollup Summary Tool and Custom Metadata | Andy in the Cloud
January 28, 2016 at 6:16 pm
Hi,
I am trying to create records in a Custom MetaDataType using https://github.com/financialforcedev/apex-mdapi. But when I create a list to use a method CustomMetadataService.createMetadata I get an error Compile Error: Field is not writeable: NAMESPACE__TestRetention__mdt.DeveloperName at line 53
Here is my code
List newList = new List();
for(NAMESPACE__TestRetention__mdt s: newList)
{
s.developername=’New’;
s.label=’New’;
s.masterlabel=’New’;
s.qualifiedapiname=’New’;
newList.add(s);
}
CustomMetadataService.createMetadata(newlist);
How do I create data for inserting it into the method if I cant insert it to a list? Can you help?
January 29, 2016 at 4:22 am
Yes as per the blog they cannot be assigned. You can bind to them on a vf page though, as I have done in the dlrs tool code. I also created a method that took a sobjectfield token and value map that could be passed, however didn’t get time to complete that, can look into it if you like.
February 26, 2016 at 10:45 am
Hi, Andy. Where can I find this method? I can try finishing it. I really need to programmatically create mdt’s.
Thanks!
February 28, 2016 at 9:53 am
I can try to write it this weekend or Monday (I am on holiday so should be able to help here)
February 29, 2016 at 11:23 am
Hi, Andy.
Thanks for the reply!
I figured out how to do this. I’m using https://github.com/haripriyamurthy/CustomMetadataLoader as a reference.
Just a couple of minutes ago I finally succeeded in creation mdt records. I will share the code once I’m finished.
Probably tomm or on Wedbesday.
February 29, 2016 at 12:13 pm
That’s great the community will appreciate the contribution!
February 26, 2016 at 10:32 am
Have you figured out how to do this? 🙂
March 7, 2016 at 10:53 am
Hi, Andy. (and others who need this)
I forked the repo and added the new method.
However I can’t find the option to create a pull request.
Anyway, here is my fork – the code is there https://gist.github.com/bermager/50478e3419bbbd335267
Look for createMetadataFromValuePairs()
Pingback: How to leverage custom metadata types - FinancialForce blog
February 2, 2017 at 5:00 pm
Hi Andy,
This code is awesome! Thanks for the write-up.
I tried moving your MetadataService and CustomMetadataService files into my sandbox but hit the 3M character limit (those two files themselves are over 1M characters). Will I still hit this limit if I deploy directly from GitHub to sandbox?
Thanks for your help,
Jeremy
February 2, 2017 at 10:40 pm
Thank you! Sadly yes same process under the hood. It’s possible to breakdown the metadataservice but tedious, maybe better to selectively copy as you need bits
Pingback: Building Robust Dynamic Code with Custom Metadata Field Relationships | Andy in the Cloud