Simon Fell > Its just code > Metadata API from .NET

Thursday, January 31, 2008

A few people have been asking for a complete example of using the metadata API from .NET, If you've using the regular enterprise/partner API, then the patterns should look familiar. One wrinkle for .NET is the whole fooSpecified mess, this is more apparent in the metadata API because most of the fields aren't strings.

I created a new project in VS.NET 2005, imported the enterprise WSDL (as sforce) and imported the metadata WSDL (as metaforce), the code makes a login call with the enterprise API, creates a stub for the metadata API, configuring the sessionHeader and Url from the login result (just like for the regular enterprise stub). then goes on to setup a CustomObject structure and passes it to create. One difference to the enterprise API is that the process is async, so you get back an AsyncResult structure that indicates its current state, a guess at how long to wait before checking its state again and so on, so we sit in a loop (with the specified waits) until its done. If the object was created, then we call describeSObject on the enterprise API to check that it really did do something, here's the code.

using System;
using System.Collections.Generic;
using System.Text;

namespace metadataDemo
    class Program
        static void Main(string[] args)
            if (args.Length != 2)
                Console.WriteLine("useage: metadataDemo username password");
            MetadataCreator mc = new MetadataCreator(args[0], args[1]);

    class MetadataCreator
        private metaforce.MetadataService ms;
        private sforce.SforceService ss;

        public MetadataCreator(String username, String password)
            ss = new sforce.SforceService();
            sforce.LoginResult lr = ss.login(username, password);
            ss.Url = lr.serverUrl;
            ss.SessionHeaderValue = new sforce.SessionHeader();
            ss.SessionHeaderValue.sessionId = lr.sessionId;

            ms = new metaforce.MetadataService();
            ms.Url = lr.metadataServerUrl;
            ms.SessionHeaderValue = new metaforce.SessionHeader();
            ms.SessionHeaderValue.sessionId = lr.sessionId;

        public void Create()
            metaforce.CustomObject co = new metaforce.CustomObject();
            co.deploymentStatus = metaforce.DeploymentStatus.Deployed;
            co.deploymentStatusSpecified = true;
            co.description = "My Custom Object created from .NET";
            co.fullName = "DotNetCustomObject__c";
            co.label = "DotNet Custom Object";
            co.pluralLabel = "DotNet Custom Objects";
            co.sharingModel = metaforce.SharingModel.ReadWrite;
            co.sharingModelSpecified = true;
            co.nameField = new metaforce.CustomField();
            co.nameField.type = metaforce.FieldType.Text;
            co.nameField.label = "The Name";
            co.nameField.length = 100;
            co.nameField.lengthSpecified = true;

            metaforce.AsyncResult r = ms.create(new metaforce.Metadata[] { co })[0];
            while(!r.done) {
                System.Threading.Thread.Sleep(r.secondsToWait * 1000);
                r = ms.checkStatus(new string[] { })[0];
            if (r.state == metaforce.AsyncRequestState.Error) 
                Console.WriteLine("Error : {0} {1}", r.statusCode, r.message);
            else {
                Console.WriteLine("Created customObject {0}, calling describeSObject now", co.fullName);
                sforce.DescribeSObjectResult d = ss.describeSObject(co.fullName);
                Console.WriteLine("DescribeSobject for {0} ({1})",, d.label);
                foreach(sforce.Field f in d.fields)
                    Console.WriteLine("{0}\t{1}",, f.type);
And here's the output from a running it
Created customObject DotNetCustomObject__c, calling describeSObject now
DescribeSobject for DotNetCustomObject__c (DotNet Custom Object)
Id      id
OwnerId reference
IsDeleted       boolean
Name    string
CreatedDate     datetime
CreatedById     reference
LastModifiedDate        datetime
LastModifiedById        reference
SystemModstamp  datetime

Creating customFields is just as easy, just remember to specify the fully qualified fieldname as its fullName, e.g. if you wanted a new field on your custom object co__c, you set the name to be co__c.newField__c, or if you wanted a new custom field on Account, it'd be Account.newField__c. Share and Enjoy.