Work with solutions


Updated: November 29, 2016

Applies To: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online

This topic presents specific programming tasks included in Sample: Work with solutions and Sample: Detect solution dependencies.

Every solution requires a publisher, represented by the Publisher entity. A solution cannot use the Microsoft Corporation publisher but it can use the Default publisher for the organization or a new publisher

A publisher requires the following:

  • A customization prefix

  • A unique name

  • A friendly name

The following sample first defines a publisher and then checks to see whether the publisher already exists based on the unique name. If it already exists, the customization prefix may have been changed, so this sample seeks to capture the current customization prefix. The PublisherId is also captured so that the publisher record can be deleted. If the publisher is not found, a new publisher is created using the IOrganizationService. Create method

'Define a new publisher
Dim _crmSdkPublisher As Publisher =
 New Publisher With {
  .UniqueName = "sdksamples",
  .FriendlyName = "Microsoft CRM SDK Samples",
  .SupportingWebsiteUrl = "",
  .CustomizationPrefix = "sample",
  .EMailAddress = "",
  .Description = "This publisher was created with samples from the Microsoft Dynamics CRM SDK"

'Does publisher already exist?
Dim querySDKSamplePublisher As QueryExpression =
 New QueryExpression With {
  .EntityName = Publisher.EntityLogicalName,
  .ColumnSet = New ColumnSet("publisherid", "customizationprefix"),
  .Criteria = New FilterExpression()

Dim querySDKSamplePublisherResults As EntityCollection =
Dim SDKSamplePublisherResults As Publisher = Nothing

'If it already exists, use it
If querySDKSamplePublisherResults.Entities.Count > 0 Then
 SDKSamplePublisherResults = CType(querySDKSamplePublisherResults.Entities(0), Publisher)
 _crmSdkPublisherId = CType(SDKSamplePublisherResults.PublisherId, Guid)
 _customizationPrefix = SDKSamplePublisherResults.CustomizationPrefix
End If
'If it doesn't exist, create it
If SDKSamplePublisherResults Is Nothing Then
 _crmSdkPublisherId = _serviceProxy.Create(_crmSdkPublisher)
 Console.WriteLine(String.Format("Created publisher: {0}.", _crmSdkPublisher.FriendlyName))
 _customizationPrefix = _crmSdkPublisher.CustomizationPrefix
End If

This sample shows how toretrieve the default publisher. The default publisher has a constant GUID value: d21aab71-79e7-11dd-8874-00188b01e34f.

' Retrieve the Default Publisher

'The default publisher has a constant GUID value;
Dim DefaultPublisherId As New Guid("{d21aab71-79e7-11dd-8874-00188b01e34f}")

Dim DefaultPublisher As Publisher =
                              New ColumnSet(New String() {"friendlyname"})), 

Dim DefaultPublisherReference As EntityReference = New EntityReference With {
 .Id = DefaultPublisher.Id,
 .LogicalName = Publisher.EntityLogicalName,
 .Name = DefaultPublisher.FriendlyName
Console.WriteLine("Retrieved the {0}.", DefaultPublisherReference.Name)

The following sample shows how to create an unmanaged solution using the Microsoft Dynamics 365 SDK Samples publisher created in Create a publisher.

A solution requires the following:

  • A Publisher

  • A Friendly Name

  • A Unique Name

  • A Version Number

The variable _crmSdkPublisherId is a GUID value representing the publisherid value.

This sample checks whether the Solution already exists in the organization based on the unique name. If the solution does not exist it is created. The SolutionId value is captured so the solution can be deleted.

' Create a Solution
'Define a solution
Dim solution As Solution =
 New Solution With {
  .UniqueName = "samplesolution",
  .FriendlyName = "Sample Solution",
  .PublisherId = New EntityReference(Publisher.EntityLogicalName, _crmSdkPublisherId),
  .Description = "This solution was created by the WorkWithSolutions sample code in the Microsoft Dynamics CRM SDK samples.",
  .Version = "1.0"

'Check whether it already exists
Dim queryCheckForSampleSolution As QueryExpression =
 New QueryExpression With {
  .EntityName = solution.EntityLogicalName,
  .ColumnSet = New ColumnSet(),
  .Criteria = New FilterExpression()

'Create the solution if it does not already exist.
Dim querySampleSolutionResults As EntityCollection =
Dim SampleSolutionResults As Solution = Nothing
If querySampleSolutionResults.Entities.Count > 0 Then
 SampleSolutionResults = CType(querySampleSolutionResults.Entities(0), Solution)
 _solutionsSampleSolutionId = CType(SampleSolutionResults.SolutionId, Guid)
End If
If SampleSolutionResults Is Nothing Then
 _solutionsSampleSolutionId = _serviceProxy.Create(solution)
End If

To retrieve a specific solution you can use the solution’s UniqueName. Each organization will have a default solution with a constant GUID value: FD140AAF-4DF4-11DD-BD17-0019B9312238.

This sample shows how to retrieve data for a solution with the unique name ”samplesolution”. A solution with this name is created in Create a solution.

' Retrieve a solution
Dim solutionUniqueName As String = "samplesolution"
Dim querySampleSolution As QueryExpression =
 New QueryExpression With {
  .EntityName = solution.EntityLogicalName,
  .ColumnSet = New ColumnSet(New String() {"publisherid",
  .Criteria = New FilterExpression()

querySampleSolution.Criteria.AddCondition("uniquename", ConditionOperator.Equal, solutionUniqueName)
Dim SampleSolution As Solution =
 CType(_serviceProxy.RetrieveMultiple(querySampleSolution).Entities(0), Solution)

This sample shows how to create a solution component that is associated with a specific solution. If you don’t associate the solution component to a specific solution when it is created it will only be added to the default solution and you will need to add it to a solution manually or by using the code included in the Add an existing solution component.

This code creates a new global option set and adds it to the solution with a unique name equal to _primarySolutionName.

Dim optionSetMetadata As New OptionSetMetadata() With {
 .Name = _globalOptionSetName,
 .DisplayName = New Label("Example Option Set", _languageCode),
 .IsGlobal = True,
 .OptionSetType = OptionSetType.Picklist
  New OptionMetadata(New Label("Option 1", _languageCode), 1),
  New OptionMetadata(New Label("Option 2", _languageCode), 2)
Dim createOptionSetRequest As CreateOptionSetRequest =
 New CreateOptionSetRequest With {
  .OptionSet = optionSetMetadata

createOptionSetRequest.SolutionUniqueName = _primarySolutionName

This sample shows how to add an existing solution component to a solution.

The following code uses the AddSolutionComponentRequest to add the Account entity as a solution component to an unmanaged solution.

' Add an existing Solution Component
'Add the Account entity to the solution
Dim retrieveForAddAccountRequest As New RetrieveEntityRequest() With {
 .LogicalName = Account.EntityLogicalName
Dim retrieveForAddAccountResponse As RetrieveEntityResponse =
Dim addReq As New AddSolutionComponentRequest() With {
 .ComponentType = componenttype.Entity,
 .ComponentId = CType(retrieveForAddAccountResponse.EntityMetadata.MetadataId, Guid),
 .SolutionUniqueName = solution.UniqueName

This sample shows how to remove a solution component from an unmanaged solution. The following code uses the RemoveSolutionComponentRequest to remove an entity solution component from an unmanaged solution. The solution.UniqueName references the Solution created in the Create a solution.

' Remove a Solution Component
'Remove the Account entity from the solution
Dim retrieveForRemoveAccountRequest As New RetrieveEntityRequest() With {
 .LogicalName = Account.EntityLogicalName
Dim retrieveForRemoveAccountResponse As RetrieveEntityResponse =
 CType(_serviceProxy.Execute(retrieveForRemoveAccountRequest), RetrieveEntityResponse)

Dim removeReq As New RemoveSolutionComponentRequest() With {
 .ComponentId = CType(retrieveForRemoveAccountResponse.EntityMetadata.MetadataId, Guid),
 .ComponentType = componenttype.Entity,
 .SolutionUniqueName = solution.UniqueName

This sample shows how to export an unmanaged solution or package a managed solution. The code uses ExportSolutionRequest to export a compressed file representing an unmanaged solution. The option to create a managed solution is set using the Managed property. This sample saves a file named to the c:\temp\ folder.

' Export or package a solution
'Export an a solution
Dim outputDir As String = "C:\temp\"

Dim exportSolutionRequest As New ExportSolutionRequest()
exportSolutionRequest.Managed = False
exportSolutionRequest.SolutionName = solution.UniqueName

Dim exportSolutionResponse As ExportSolutionResponse =
 CType(_serviceProxy.Execute(exportSolutionRequest), ExportSolutionResponse)

Dim exportXml() As Byte = exportSolutionResponse.ExportSolutionFile
Dim filename As String = solution.UniqueName & ".zip"
File.WriteAllBytes(outputDir & filename, exportXml)

Console.WriteLine("Solution exported to {0}.", outputDir & filename)

This sample shows how to install or upgrade a solution using the ImportSolutionRequest message.

You can use the ImportJob entity to capture data about the success of the import.

The following sample shows how to import a solution without tracking the success.

' Install or Upgrade a Solution                  

Dim fileBytes() As Byte = File.ReadAllBytes(ManagedSolutionLocation)

Dim impSolReq As New ImportSolutionRequest() With {
 .CustomizationFile = fileBytes


Console.WriteLine("Imported Solution from {0}", ManagedSolutionLocation)

When you specify an ImportJobId for the ImportSolutionRequest, you can use that value to query the ImportJob entity about the status of the import.

The ImportJobId can also be used to download an import log file using the RetrieveFormattedImportJobResultsRequest message.

The following sample shows how to retrieve the import job record and the content of the ImportJob.Data attribute.

' Monitor import success
Dim fileBytesWithMonitoring() As Byte = File.ReadAllBytes(ManagedSolutionLocation)

Dim impSolReqWithMonitoring As New ImportSolutionRequest() With {
 .CustomizationFile = fileBytes,
 .ImportJobId = Guid.NewGuid()

Console.WriteLine("Imported Solution with Monitoring from {0}", ManagedSolutionLocation)

Dim job As ImportJob =
                              New ColumnSet(New String() {"data", "solutionname"})), 

Dim doc As New System.Xml.XmlDocument()

Dim ImportedSolutionName As String = doc.SelectSingleNode("//solutionManifest/UniqueName").InnerText
Dim SolutionImportResult As String = doc.SelectSingleNode("//solutionManifest/result/@result").Value

Console.WriteLine("Report from the ImportJob data")
Console.WriteLine("Solution Unique name: {0}", ImportedSolutionName)
Console.WriteLine("Solution Import Result: {0}", SolutionImportResult)

'This code displays the results for Global Option sets installed as part of a solution.
Dim optionSets As System.Xml.XmlNodeList = doc.SelectNodes("//optionSets/optionSet")
For Each node As System.Xml.XmlNode In optionSets
 Dim OptionSetName As String = node.Attributes("LocalizedName").Value
 Dim result As String = node.FirstChild.Attributes("result").Value

 If result = "success" Then
  Console.WriteLine("{0} result: {1}", OptionSetName, result)
  Dim errorCode As String = node.FirstChild.Attributes("errorcode").Value
  Dim errorText As String = node.FirstChild.Attributes("errortext").Value

  Console.WriteLine("{0} result: {1} Code: {2} Description: {3}", OptionSetName, result, errorCode, errorText)
 End If
Next node

The contents of the Data property is a string representing an XML file. The following is a sample captured using the code in this sample. This managed solution contained a single global option set called sample_tempsampleglobaloptionsetname.

<importexportxml start="634224017519682730"
  <solutionManifest languagecode="1033"
                    LocalizedName="Sample Solution for Import"
    <LocalizedName description="Sample Solution for Import"
                   languagecode="1033" />
    <Description description="This solution was created by the WorkWithSolutions sample code in the Microsoft CRM SDK samples."
                 languagecode="1033" />
     <LocalizedName description="Microsoft CRM SDK Samples"
                    languagecode="1033" />
     <Description description="This publisher was created with samples from the Microsoft CRM SDK"
                  languagecode="1033" />
      <City />
      <Country />
      <Line1 />
      <Line2 />
      <PostalCode />
      <StateOrProvince />
      <Telephone1 />
   <results />
   <result result="success"
           datetimeticks="634224269520845122" />
 <entities />
 <nodes />
 <settings />
 <dashboards />
 <securityroles />
 <workflows />
 <templates />
  <optionSet id="sample_tempsampleglobaloptionsetname"
             LocalizedName="Example Option Set"
   <result result="success"
           datetimeticks="634224269561025400" />
 <ConnectionRoles />
 <SolutionPluginAssemblies />
 <SdkMessageProcessingSteps />
 <ServiceEndpoints />
 <webResources />
 <reports />
 <FieldSecurityProfiles />
   <result result="success"
           datetimeticks="634224269520092986" />
 <entitySubhandlers />
  <publish processed="false" />
  <rootComponent processed="true">
   <result result="success"
           datetimeticks="634224269608387238" />
  <dependency processed="true">
   <result result="success"
           datetimeticks="634224269609715208" />

This sample shows how to delete a solution.The following sample shows how to retrieve a solution using the solution uniquename and then extract the solutionid from the results. Use the solutionid with the IOrganizationService. Delete method.

' Delete a solution

Dim queryImportedSolution As QueryExpression =
 New QueryExpression With {
  .EntityName = solution.EntityLogicalName,
  .ColumnSet = New ColumnSet(New String() {"solutionid", "friendlyname"}),
  .Criteria = New FilterExpression()

queryImportedSolution.Criteria.AddCondition("uniquename", ConditionOperator.Equal, ImportedSolutionName)

Dim ImportedSolution As Solution =
 CType(_serviceProxy.RetrieveMultiple(queryImportedSolution).Entities(0), Solution)

_serviceProxy.Delete(solution.EntityLogicalName, CType(ImportedSolution.SolutionId, Guid))

Console.WriteLine("Deleted the {0} solution.", ImportedSolution.FriendlyName)

This sample shows how to create a report showing the dependencies between solution components.

This code will:

  • Retrieve all the components for a solution.

  • Retrieve all the dependencies for each component.

  • For each dependency found display a report describing the dependency.

' Grab all Solution Components for a solution.
Dim componentQuery As QueryByAttribute =
 New QueryByAttribute With {
  .EntityName = SolutionComponent.EntityLogicalName,
  .ColumnSet = New ColumnSet("componenttype", "objectid",
                             "solutioncomponentid", "solutionid")
' In your code, this value would probably come from another query.

Dim allComponents As IEnumerable(Of SolutionComponent) =
 _serviceProxy.RetrieveMultiple(componentQuery).Entities.Cast(Of SolutionComponent)()

For Each component As SolutionComponent In allComponents
    ' For each solution component, retrieve all dependencies for the component.
    Dim dependentComponentsRequest As RetrieveDependentComponentsRequest =
     New RetrieveDependentComponentsRequest With {
      .ComponentType = component.ComponentType.Value,
      .ObjectId = component.ObjectId.Value
    Dim dependentComponentsResponse As RetrieveDependentComponentsResponse =

    ' If there are no dependent components, we can ignore this component.
    If dependentComponentsResponse.EntityCollection.Entities.Any() = False Then
        Continue For
    End If

    ' If there are dependencies upon this solution component, and the solution
    ' itself is managed, then you will be unable to delete the solution.
    Console.WriteLine("Found {0} dependencies for Component {1} of type {2}",
    'A more complete report requires more code
    For Each d As Dependency In dependentComponentsResponse.EntityCollection.Entities
    Next d
Next component

The DependencyReport method is in the following code sample.

The DependencyReport method provides a friendlier message based on information found within the dependency.


In this sample the method is only partially implemented. It can display messages only for attribute and option set solution components.

''' <summary>
''' Shows how to get a more friendly message based on information within the dependency
''' <param name="dependency">A Dependency returned from the RetrieveDependentComponents message</param>
''' </summary> 
Public Sub DependencyReport(ByVal dependency As Dependency)
    'These strings represent parameters for the message.
    Dim dependentComponentName As String = ""
    Dim dependentComponentTypeName As String = ""
    Dim dependentComponentSolutionName As String = ""
    Dim requiredComponentName As String = ""
    Dim requiredComponentTypeName As String = ""
    Dim requiredComponentSolutionName As String = ""

    'The ComponentType global Option Set contains options for each possible component.
    Dim componentTypeRequest As RetrieveOptionSetRequest =
     New RetrieveOptionSetRequest With {
      .Name = "componenttype"

    Dim componentTypeResponse As RetrieveOptionSetResponse =
     CType(_serviceProxy.Execute(componentTypeRequest), RetrieveOptionSetResponse)
    Dim componentTypeOptionSet As OptionSetMetadata =
     CType(componentTypeResponse.OptionSetMetadata, OptionSetMetadata)
    ' Match the Component type with the option value and get the label value of the option.
    For Each opt As OptionMetadata In componentTypeOptionSet.Options
        If dependency.DependentComponentType.Value = opt.Value Then
            dependentComponentTypeName = opt.Label.UserLocalizedLabel.Label
        End If
        If dependency.RequiredComponentType.Value = opt.Value Then
            requiredComponentTypeName = opt.Label.UserLocalizedLabel.Label
        End If
    Next opt
    'The name or display name of the compoent is retrieved in different ways depending on the component type
    dependentComponentName = getComponentName(dependency.DependentComponentType.Value,
    requiredComponentName = getComponentName(dependency.RequiredComponentType.Value,

    ' Retrieve the friendly name for the dependent solution.
    Dim dependentSolution As Solution =
                                  CType(dependency.DependentComponentBaseSolutionId, Guid),
                                  New ColumnSet("friendlyname")), 
    dependentComponentSolutionName = dependentSolution.FriendlyName

    ' Retrieve the friendly name for the required solution.
    Dim requiredSolution As Solution =
                                  New ColumnSet("friendlyname")), 
    requiredComponentSolutionName = requiredSolution.FriendlyName

    'Display the message
    Console.WriteLine("The {0} {1} in the {2} depends on the {3} {4} in the {5} solution.",
End Sub

Use the RetrieveDependenciesForDeleteRequest message to identify any other solution components which would prevent a given solution component from being deleted. The following code sample looks for any attributes using a known global optionset. Any attribute using the global optionset would prevent the global optionset from being deleted.

' Use the RetrieveOptionSetRequest message to retrieve  
' a global option set by it's name.
Dim retrieveOptionSetRequest_Renamed As RetrieveOptionSetRequest =
    New RetrieveOptionSetRequest With {.Name = _globalOptionSetName}

' Execute the request.
Dim retrieveOptionSetResponse_Renamed As RetrieveOptionSetResponse =
    CType(_serviceProxy.Execute(retrieveOptionSetRequest_Renamed), RetrieveOptionSetResponse)
_globalOptionSetId = retrieveOptionSetResponse_Renamed.OptionSetMetadata.MetadataId
If _globalOptionSetId IsNot Nothing Then
    'Use the global OptionSet MetadataId with the appropriate componenttype
    ' to call RetrieveDependenciesForDeleteRequest
    Dim retrieveDependenciesForDeleteRequest_Renamed As RetrieveDependenciesForDeleteRequest =
        New RetrieveDependenciesForDeleteRequest With
            .ComponentType = CInt(Fix(componenttype.OptionSet)),
            .ObjectId = CType(_globalOptionSetId, Guid)

    Dim retrieveDependenciesForDeleteResponse_Renamed As RetrieveDependenciesForDeleteResponse =
    For Each d As Dependency In retrieveDependenciesForDeleteResponse_Renamed _

        If d.DependentComponentType.Value = 2 Then 'Just testing for Attributes
            Dim attributeLabel As String = ""
            Dim retrieveAttributeRequest_Renamed As RetrieveAttributeRequest =
                New RetrieveAttributeRequest With
                    .MetadataId = CType(d.DependentComponentObjectId, Guid)
            Dim retrieveAttributeResponse_Renamed As RetrieveAttributeResponse =

            Dim attmet As AttributeMetadata = retrieveAttributeResponse_Renamed.AttributeMetadata

            attributeLabel = attmet.DisplayName.UserLocalizedLabel.Label

            Console.WriteLine("An {0} named {1} will prevent deleting the {2} global option set.",
                              CType(d.DependentComponentType.Value, componenttype),
                              attributeLabel, _globalOptionSetName)
        End If
    Next d
End If

Microsoft Dynamics 365

© 2016 Microsoft. All rights reserved. Copyright