The Metatdata API is used by many products to push and pull code to and from Salesforce. It is found by going to App Setup, Develop, API and choosing the Metadata API. The Metadata can be added to a VB.Net project by following these steps.
1) Open up VB.NET 2010
2) Click on File, New Project…
3) Choose a Windows Forms Application; Give it a name; and Click on OK
4) Rename Form1 to something more meaningful.
5) Download the Metatdata API from Salesforce
a. Log into Salesforce
b. Click on Your Name, Setup, App Setup, Develop, API
c. Click on Generate Metadata WSDL
d. Save the generated page as an XML file.
6) In VB.NET click on the ‘Show All Files’ button in the Solution Explorer
7) Right-click on the References node and select ‘Add Service Reference’
8) Click on the ‘Advanced…’ button
9) Click on the ‘Add Web Reference…’ button
10) In the URL combo box type in the path to your saved wsdl. For me the path looked like this: C:\Users\terry.luschen\Documents\Salesforce Info\Whitepapers\November 2012 Whitepaper – Title\metadata.wsdl.xml
11) Change the ‘Web reference name’ to something more reasonable like ‘SFMetaDataAPI’
12) Now repeat the same steps for the Partner API. It is the ‘Generate Partner WSDL’ in Salesforce.
13) Drop a button on your form, rename it to something appropriate and double-click on it to get to the code.
We are now at the point where we can login using the Partner API so our Metatdata API can be used.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
Private sfPartner As New SFPartnerAPI.SforceService()Private sfMetaData As New SFMetadataAPI.MetadataService() Private Sub btnSourceControl_Click(sender As System.Object, e As System.EventArgs) Handles btnSourceControl.Click LogInToSalesforce() DoDeploy() DoRetrieve() ' I will work through this in my next blog post MsgBox("Action Complete") End Sub Private Sub LogInToSalesforce() Dim sfSLoginResult As SFPartnerAPI.LoginResult sfSLoginResult = sfPartner.login("email", "passwordSecurityToken") sfPartner.SessionHeaderValue = New SFPartnerAPI.SessionHeader() sfPartner.SessionHeaderValue.sessionId = sfSLoginResult.sessionId sfMetaData.SessionHeaderValue = New SFMetadataAPI.SessionHeader() sfMetaData.SessionHeaderValue.sessionId = sfSLoginResult.sessionId sfMetaData.Url = sfSLoginResult.metadataServerUrl sfPartner.Url = sfSLoginResult.serverUrl End Sub |
Now that we are logged in we are ready to deploy our code. The example below has some hard-coded file names, but hopefully the structure of how to call the Metadata API is clear.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
Private Sub DoDeploy() Dim deployOpts As New SFMetadataAPI.DeployOptions Dim asyncResultItem As New SFMetadataAPI.AsyncResult Dim newDeployResult As SFMetadataAPI.DeployResult Dim newRetrieveResult As SFMetadataAPI.RetrieveResult Dim myFileProperties() As SFMetadataAPI.FileProperties Dim deployMessageItem() As SFMetadataAPI.DeployMessage Dim byteFile As Byte() Dim i As Integer Dim newZipFile As String deployOpts.runAllTests = False deployOpts.runTests = Nothing deployOpts.autoUpdatePackage = False deployOpts.allowMissingFiles = False deployOpts.checkOnly = False deployOpts.ignoreWarnings = False deployOpts.performRetrieve = True deployOpts.purgeOnDelete = False deployOpts.rollbackOnError = False deployOpts.singlePackage = True newZipFile = "C:\Deploy Test2\packageToSend.zip" If System.IO.File.Exists(newZipFile) Then System.IO.File.Delete(newZipFile) End If 'Build a Zip File to Send to Salesforce - <a href="http://dotnetzip.codeplex.com/">http://dotnetzip.codeplex.com/</a> project Using zip As Ionic.Zip.ZipFile = New Ionic.Zip.ZipFile zip.AddFile("C:\Deploy Test2\package.xml", "") zip.AddFile("C:\Deploy Test2\classes\startHereController.cls", "classes") zip.AddFile("C:\Deploy Test2\classes\startHereController.cls-meta.xml", "classes") zip.Save(newZipFile) End Using 'This needs to be a zip file. The zip file can have any file name ' In the root directory there must be a package.xml file ' If you have classes there must be a classes folder and if you have triggers there must be a triggers folder byteFile = LoadFile(newZipFile) asyncResultItem = sfMetaData.deploy(byteFile, deployOpts) If asyncResultItem.done = False Then waitUntilDone(asyncResultItem.id) End If newDeployResult = sfMetaData.checkDeployStatus(asyncResultItem.id) If newDeployResult.success Then MsgBox("Success sending files") If deployOpts.performRetrieve Then 'If performRetrieve is set then we get a zip file back of all of the contents newRetrieveResult = newDeployResult.retrieveResult myFileProperties = newRetrieveResult.fileProperties For i = myFileProperties.GetLowerBound(0) To myFileProperties.GetUpperBound(0) MsgBox("File " & i & " - Name: " & myFileProperties(i).fullName & " - Last Modified By: " & myFileProperties(i).lastModifiedByName & _ " - Last Modified Date: " & myFileProperties(i).lastModifiedDate) Next System.IO.File.WriteAllBytes("C:\Deploy Test2\packageRetrieved.zip", newDeployResult.retrieveResult.zipFile()) End If Else MsgBox("Failed sending files") deployMessageItem = newDeployResult.messages For i = deployMessageItem.GetLowerBound(0) To deployMessageItem.GetUpperBound(0) MsgBox("Message " & i & ": " & deployMessageItem(i).problem + " " + deployMessageItem(i).fileName) Next End IfEnd SubPrivate Function LoadFile(ByVal fileLocation As String) As Byte() Dim fInfo As New System.IO.FileInfo(fileLocation) Dim fs As System.IO.FileStream = System.IO.File.OpenRead(fileLocation) Dim buffer(Convert.ToInt32(fInfo.Length)) As Byte fs.Read(buffer, 0, buffer.Length) Return bufferEnd Function |
The hardest parts to get the code going was to 1) Understand the structure of the zip file and 2) To find a .NET utility that could zip a file for me.
I found a .NET zip utility at http://dotnetzip.codeplex.com and through various readings I figured out the file structure for the zip file. There needs to be a package.xml file in the root directory of the zip file. This package.xml file is structured just like the same files in the directory structure of your Eclipse project. Then there needs to be the same folders as there are in your Eclipse project like ‘classes’ and ‘triggers’. Once that zip file is set up it is as easy as calling the LoadFile function to generate the corresponding byte array for the zip file. From there the ‘deploy’ and ‘checkdeploystatus’ calls have to be made and your Apex code should be uploaded. The code would need to be more generic and have more error handling to make it production ready, but the point here is to help you get started on your own project with a working example.
I hope to write about retrieving metadata zip files next week.



Hi there would you mind stating which blog platform you’re using? I’m going to start
my own blog in the near future but I’m having a difficult time making a decision between BlogEngine/Wordpress/B2evolution and Drupal. The reason I ask is because your layout seems different then most blogs and I’m looking for something completely
unique. P.S Apologies for getting off-topic but
I had to ask!
We’re using ExpressionEngine 2.0. http://ellislab.com/expressionengine
We’ve been using ExpressionEngine on our company site for the last 7 years now.