Sample: Detect solution dependencies
Applies To: Dynamics CRM 2013
This sample code is for Microsoft Dynamics CRM 2013 and Microsoft Dynamics CRM Online. Download the Microsoft Dynamics CRM SDK package. It can be found in the following location in the download package:
SampleCode\CS\Solutions\GetSolutionDependencies.cs
SampleCode\VB\Solutions\GetSolutionDependencies.vb
Requirements
For more information about the requirements for running the sample code provided in this SDK, see Use the sample and helper code.
Demonstrates
This sample shows how to how to detect dependencies before you delete a solution component.
Example
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Description;
// These namespaces are found in the Microsoft.Xrm.Sdk.dll assembly
// found in the SDK\bin folder.
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Xrm.Sdk.Messages;
// This namespace is found in Microsoft.Crm.Sdk.Proxy.dll assembly
// found in the SDK\bin folder.
using Microsoft.Crm.Sdk.Messages;
namespace Microsoft.Crm.Sdk.Samples
{
/// <summary>
/// Demonstrates how to detect any dependencies prior to deleting a
/// solution component.
/// </summary>
public class GetSolutionDependencies
{
#region Class Level Members
/// <summary>
/// Stores the organization service proxy.
/// </summary>
OrganizationServiceProxy _serviceProxy;
private Guid _publisherId;
private const string _primarySolutionName = "PrimarySolution";
private Guid _primarySolutionId;
private Guid _secondarySolutionId;
private const String _prefix = "sample";
private const String _globalOptionSetName = _prefix + "_exampleoptionset";
private Guid? _globalOptionSetId;
private const String _picklistName = _prefix + "_examplepicklist";
// Specify which language code to use in the sample. If you are using a language
// other than US English, you will need to modify this value accordingly.
// See https://msdn.microsoft.com/en-us/library/0h88fahh.aspx
private const int _languageCode = 1033;
#endregion Class Level Members
#region How To Sample Code
/// <summary>
/// Shows how to detect dependencies that may cause a managed solution to become
/// un-deletable.
///
/// Get all solution components of a solution
/// For each solution component, list the dependencies upon that component.
/// </summary>
/// <param name="serverConfig">Contains server connection information.</param>
/// <param name="promptForDelete">When True, the user will be prompted to delete all
/// created entities.</param>
public void Run(ServerConnection.Configuration serverConfig, bool promptForDelete)
{
try
{
// Connect to the Organization service.
// The using statement assures that the service proxy will be properly disposed.
using (_serviceProxy = ServerConnection.GetOrganizationProxy(serverConfig))
{
// This statement is required to enable early-bound type support.
_serviceProxy.EnableProxyTypes();
// Call the method to create any data that this sample requires.
CreateRequiredRecords();
// Grab all Solution Components for a solution.
QueryByAttribute componentQuery = new QueryByAttribute
{
EntityName = SolutionComponent.EntityLogicalName,
ColumnSet = new ColumnSet("componenttype", "objectid", "solutioncomponentid", "solutionid"),
Attributes = { "solutionid" },
// In your code, this value would probably come from another query.
Values = { _primarySolutionId }
};
IEnumerable<SolutionComponent> allComponents =
_serviceProxy.RetrieveMultiple(componentQuery).Entities.Cast<SolutionComponent>();
foreach (SolutionComponent component in allComponents)
{
// For each solution component, retrieve all dependencies for the component.
RetrieveDependentComponentsRequest dependentComponentsRequest =
new RetrieveDependentComponentsRequest
{
ComponentType = component.ComponentType.Value,
ObjectId = component.ObjectId.Value
};
RetrieveDependentComponentsResponse dependentComponentsResponse =
(RetrieveDependentComponentsResponse)_serviceProxy.Execute(dependentComponentsRequest);
// If there are no dependent components, we can ignore this component.
if (dependentComponentsResponse.EntityCollection.Entities.Any() == false)
continue;
// 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}",
dependentComponentsResponse.EntityCollection.Entities.Count,
component.ObjectId.Value,
component.ComponentType.Value
);
//A more complete report requires more code
foreach (Dependency d in dependentComponentsResponse.EntityCollection.Entities)
{
DependencyReport(d);
}
}
//Find out if any dependencies on a specific global option set would prevent it from being deleted
// Use the RetrieveOptionSetRequest message to retrieve
// a global option set by it's name.
RetrieveOptionSetRequest retrieveOptionSetRequest =
new RetrieveOptionSetRequest
{
Name = _globalOptionSetName
};
// Execute the request.
RetrieveOptionSetResponse retrieveOptionSetResponse =
(RetrieveOptionSetResponse)_serviceProxy.Execute(
retrieveOptionSetRequest);
_globalOptionSetId = retrieveOptionSetResponse.OptionSetMetadata.MetadataId;
if (_globalOptionSetId != null)
{
//Use the global OptionSet MetadataId with the appropriate componenttype
// to call RetrieveDependenciesForDeleteRequest
RetrieveDependenciesForDeleteRequest retrieveDependenciesForDeleteRequest = new RetrieveDependenciesForDeleteRequest
{
ComponentType = (int)componenttype.OptionSet,
ObjectId = (Guid)_globalOptionSetId
};
RetrieveDependenciesForDeleteResponse retrieveDependenciesForDeleteResponse =
(RetrieveDependenciesForDeleteResponse)_serviceProxy.Execute(retrieveDependenciesForDeleteRequest);
Console.WriteLine("");
foreach (Dependency d in retrieveDependenciesForDeleteResponse.EntityCollection.Entities)
{
if (d.DependentComponentType.Value == 2)//Just testing for Attributes
{
String attributeLabel = "";
RetrieveAttributeRequest retrieveAttributeRequest = new RetrieveAttributeRequest
{
MetadataId = (Guid)d.DependentComponentObjectId
};
RetrieveAttributeResponse retrieveAttributeResponse = (RetrieveAttributeResponse)_serviceProxy.Execute(retrieveAttributeRequest);
AttributeMetadata attmet = retrieveAttributeResponse.AttributeMetadata;
attributeLabel = attmet.DisplayName.UserLocalizedLabel.Label;
Console.WriteLine("An {0} named {1} will prevent deleting the {2} global option set.",
(componenttype)d.DependentComponentType.Value,
attributeLabel,
_globalOptionSetName);
}
}
}
DeleteRequiredRecords(promptForDelete);
}
}
// Catch any service fault exceptions that Microsoft Dynamics CRM throws.
catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
{
// You can handle an exception here or pass it back to the calling method.
throw;
}
}
/// <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 void DependencyReport(Dependency dependency)
{
//These strings represent parameters for the message.
String dependentComponentName = "";
String dependentComponentTypeName = "";
String dependentComponentSolutionName = "";
String requiredComponentName = "";
String requiredComponentTypeName = "";
String requiredComponentSolutionName = "";
//The ComponentType global Option Set contains options for each possible component.
RetrieveOptionSetRequest componentTypeRequest = new RetrieveOptionSetRequest
{
Name = "componenttype"
};
RetrieveOptionSetResponse componentTypeResponse = (RetrieveOptionSetResponse)_serviceProxy.Execute(componentTypeRequest);
OptionSetMetadata componentTypeOptionSet = (OptionSetMetadata)componentTypeResponse.OptionSetMetadata;
// Match the Component type with the option value and get the label value of the option.
foreach (OptionMetadata opt in componentTypeOptionSet.Options)
{
if (dependency.DependentComponentType.Value == opt.Value)
{
dependentComponentTypeName = opt.Label.UserLocalizedLabel.Label;
}
if (dependency.RequiredComponentType.Value == opt.Value)
{
requiredComponentTypeName = opt.Label.UserLocalizedLabel.Label;
}
}
//The name or display name of the compoent is retrieved in different ways depending on the component type
dependentComponentName = getComponentName(dependency.DependentComponentType.Value, (Guid)dependency.DependentComponentObjectId);
requiredComponentName = getComponentName(dependency.RequiredComponentType.Value, (Guid)dependency.RequiredComponentObjectId);
// Retrieve the friendly name for the dependent solution.
Solution dependentSolution = (Solution)_serviceProxy.Retrieve
(
Solution.EntityLogicalName,
(Guid)dependency.DependentComponentBaseSolutionId,
new ColumnSet("friendlyname")
);
dependentComponentSolutionName = dependentSolution.FriendlyName;
// Retrieve the friendly name for the required solution.
Solution requiredSolution = (Solution)_serviceProxy.Retrieve
(
Solution.EntityLogicalName,
(Guid)dependency.RequiredComponentBaseSolutionId,
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.",
dependentComponentName,
dependentComponentTypeName,
dependentComponentSolutionName,
requiredComponentName,
requiredComponentTypeName,
requiredComponentSolutionName);
}
// The name or display name of the component depends on the type of component.
public String getComponentName(int ComponentType, Guid ComponentId)
{
String name = "";
switch (ComponentType)
{
// A separate method is required for each type of component
case (int)componenttype.Attribute:
name = getAttributeInformation(ComponentId);
break;
case (int)componenttype.OptionSet:
name = getGlobalOptionSetName(ComponentId);
break;
default:
name = "not implemented";
break;
}
return name;
}
// Retrieve the display name and parent entity information about an attribute solution component.
public string getAttributeInformation(Guid id)
{
String attributeInformation = "";
RetrieveAttributeRequest req = new RetrieveAttributeRequest
{
MetadataId = id
};
RetrieveAttributeResponse resp = (RetrieveAttributeResponse)_serviceProxy.Execute(req);
AttributeMetadata attmet = resp.AttributeMetadata;
attributeInformation = attmet.EntityLogicalName + " : " + attmet.DisplayName.UserLocalizedLabel.Label;
return attributeInformation;
}
//Retrieve the name of a global Option set
public String getGlobalOptionSetName(Guid id)
{
String name = "";
RetrieveOptionSetRequest req = new RetrieveOptionSetRequest
{
MetadataId = id
};
RetrieveOptionSetResponse resp = (RetrieveOptionSetResponse)_serviceProxy.Execute(req);
OptionSetMetadataBase os = (OptionSetMetadataBase)resp.OptionSetMetadata;
name = os.DisplayName.UserLocalizedLabel.Label;
return name;
}
/// <summary>
/// This method creates any entity records that this sample requires.
/// Create a publisher
/// Create a new solution, "Primary"
/// Create a Global Option Set in solution "Primary"
/// Export the "Primary" solution, setting it to Protected
/// Delete the option set and solution
/// Import the "Primary" solution, creating a managed solution in CRM.
/// Create a new solution, "Secondary"
/// Create an attribute in "Secondary" that references the Global Option Set
/// </summary>
public void CreateRequiredRecords()
{
//Create the publisher that will "own" the two solutions
Publisher publisher = new Publisher
{
UniqueName = "examplepublisher",
FriendlyName = "An Example Publisher",
Description = "This is an example publisher",
CustomizationPrefix = _prefix
};
_publisherId = _serviceProxy.Create(publisher);
//Create the primary solution - note that we are not creating it
//as a managed solution as that can only be done when exporting the solution.
Solution primarySolution = new Solution
{
Version = "1.0",
FriendlyName = "Primary Solution",
PublisherId = new EntityReference(Publisher.EntityLogicalName, _publisherId),
UniqueName = _primarySolutionName
};
_primarySolutionId = _serviceProxy.Create(primarySolution);
//Now, create the Global Option Set and associate it to the solution.
OptionSetMetadata optionSetMetadata = new OptionSetMetadata()
{
Name = _globalOptionSetName,
DisplayName = new Label("Example Option Set", _languageCode),
IsGlobal = true,
OptionSetType = OptionSetType.Picklist,
Options =
{
new OptionMetadata(new Label("Option 1", _languageCode), 1),
new OptionMetadata(new Label("Option 2", _languageCode), 2)
}
};
CreateOptionSetRequest createOptionSetRequest = new CreateOptionSetRequest
{
OptionSet = optionSetMetadata
};
createOptionSetRequest.SolutionUniqueName = _primarySolutionName;
_serviceProxy.Execute(createOptionSetRequest);
//Export the solution as managed so that we can later import it.
ExportSolutionRequest exportRequest = new ExportSolutionRequest
{
Managed = true,
SolutionName = _primarySolutionName
};
ExportSolutionResponse exportResponse =
(ExportSolutionResponse)_serviceProxy.Execute(exportRequest);
// Delete the option set previous created, so it can be imported under the
// managed solution.
DeleteOptionSetRequest deleteOptionSetRequest = new DeleteOptionSetRequest
{
Name = _globalOptionSetName
};
_serviceProxy.Execute(deleteOptionSetRequest);
// Delete the previous primary solution, so it can be imported as managed.
_serviceProxy.Delete(Solution.EntityLogicalName, _primarySolutionId);
_primarySolutionId = Guid.Empty;
// Re-import the solution as managed.
ImportSolutionRequest importRequest = new ImportSolutionRequest
{
CustomizationFile = exportResponse.ExportSolutionFile
};
_serviceProxy.Execute(importRequest);
// Retrieve the solution from CRM in order to get the new id.
QueryByAttribute primarySolutionQuery = new QueryByAttribute
{
EntityName = Solution.EntityLogicalName,
ColumnSet = new ColumnSet("solutionid"),
Attributes = { "uniquename" },
Values = { _primarySolutionName }
};
_primarySolutionId = _serviceProxy.RetrieveMultiple(primarySolutionQuery).Entities
.Cast<Solution>().FirstOrDefault().SolutionId.GetValueOrDefault();
// Create a secondary solution.
Solution secondarySolution = new Solution
{
Version = "1.0",
FriendlyName = "Secondary Solution",
PublisherId = new EntityReference(Publisher.EntityLogicalName, _publisherId),
UniqueName = "SecondarySolution"
};
_secondarySolutionId = _serviceProxy.Create(secondarySolution);
// Create a Picklist attribute in the secondary solution linked to the option set in the
// primary - see WorkWithOptionSets.cs for more on option sets.
PicklistAttributeMetadata picklistMetadata = new PicklistAttributeMetadata
{
SchemaName = _picklistName,
LogicalName = _picklistName,
DisplayName = new Label("Example Picklist", _languageCode),
RequiredLevel = new AttributeRequiredLevelManagedProperty(AttributeRequiredLevel.None),
OptionSet = new OptionSetMetadata
{
IsGlobal = true,
Name = _globalOptionSetName
}
};
CreateAttributeRequest createAttributeRequest = new CreateAttributeRequest
{
EntityName = Contact.EntityLogicalName,
Attribute = picklistMetadata
};
createAttributeRequest["SolutionUniqueName"] = secondarySolution.UniqueName;
_serviceProxy.Execute(createAttributeRequest);
}
/// <summary>
/// Deletes any entity records that were created for this sample.
/// <param name="prompt">Indicates whether to prompt the user to delete the records created in this sample.</param>
/// </summary>
public void DeleteRequiredRecords(bool prompt)
{
bool deleteRecords = true;
if (prompt)
{
Console.WriteLine("\nDo you want these entity records deleted? (y/n)");
String answer = Console.ReadLine();
deleteRecords = (answer.StartsWith("y") || answer.StartsWith("Y"));
}
if (deleteRecords)
{
DeleteAttributeRequest deleteAttributeRequest = new DeleteAttributeRequest
{
EntityLogicalName = Contact.EntityLogicalName,
LogicalName = _picklistName
};
_serviceProxy.Execute(deleteAttributeRequest);
_serviceProxy.Delete(Solution.EntityLogicalName, _primarySolutionId);
_serviceProxy.Delete(Solution.EntityLogicalName, _secondarySolutionId);
_serviceProxy.Delete(Publisher.EntityLogicalName, _publisherId);
Console.WriteLine("Entity records have been deleted.");
}
}
#endregion How To Sample Code
#region Main
/// <summary>
/// Standard Main() method used by most SDK samples.
/// </summary>
/// <param name="args"></param>
static public void Main(string[] args)
{
try
{
// Obtain the target organization's Web address and client logon
// credentials from the user.
ServerConnection serverConnect = new ServerConnection();
ServerConnection.Configuration config = serverConnect.GetServerConfiguration();
GetSolutionDependencies app = new GetSolutionDependencies();
app.Run(config, true);
}
catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> ex)
{
Console.WriteLine("The application terminated with an error.");
Console.WriteLine("Timestamp: {0}", ex.Detail.Timestamp);
Console.WriteLine("Code: {0}", ex.Detail.ErrorCode);
Console.WriteLine("Message: {0}", ex.Detail.Message);
Console.WriteLine("Plugin Trace: {0}", ex.Detail.TraceText);
Console.WriteLine("Inner Fault: {0}",
null == ex.Detail.InnerFault ? "No Inner Fault" : "Has Inner Fault");
}
catch (System.TimeoutException ex)
{
Console.WriteLine("The application terminated with an error.");
Console.WriteLine("Message: {0}", ex.Message);
Console.WriteLine("Stack Trace: {0}", ex.StackTrace);
Console.WriteLine("Inner Fault: {0}",
null == ex.InnerException.Message ? "No Inner Fault" : ex.InnerException.Message);
}
catch (System.Exception ex)
{
Console.WriteLine("The application terminated with an error.");
Console.WriteLine(ex.Message);
// Display the details of the inner exception.
if (ex.InnerException != null)
{
Console.WriteLine(ex.InnerException.Message);
FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault> fe = ex.InnerException
as FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>;
if (fe != null)
{
Console.WriteLine("Timestamp: {0}", fe.Detail.Timestamp);
Console.WriteLine("Code: {0}", fe.Detail.ErrorCode);
Console.WriteLine("Message: {0}", fe.Detail.Message);
Console.WriteLine("Plugin Trace: {0}", fe.Detail.TraceText);
Console.WriteLine("Inner Fault: {0}",
null == fe.Detail.InnerFault ? "No Inner Fault" : "Has Inner Fault");
}
}
}
// Additional exceptions to catch: SecurityTokenValidationException, ExpiredSecurityTokenException,
// SecurityAccessDeniedException, MessageSecurityException, and SecurityNegotiationException.
finally
{
Console.WriteLine("Press <Enter> to exit.");
Console.ReadLine();
}
}
#endregion Main
}
}
Imports System.ServiceModel
Imports System.ServiceModel.Description
' These namespaces are found in the Microsoft.Xrm.Sdk.dll assembly
' found in the SDK\bin folder.
Imports Microsoft.Xrm.Sdk
Imports Microsoft.Xrm.Sdk.Client
Imports Microsoft.Xrm.Sdk.Metadata
Imports Microsoft.Xrm.Sdk.Query
Imports Microsoft.Xrm.Sdk.Discovery
Imports Microsoft.Xrm.Sdk.Messages
' This namespace is found in Microsoft.Crm.Sdk.Proxy.dll assembly
' found in the SDK\bin folder.
Imports Microsoft.Crm.Sdk.Messages
Namespace Microsoft.Crm.Sdk.Samples
''' <summary>
''' Demonstrates how to detect any dependencies prior to deleting a
''' solution component.
''' </summary>
Public Class GetSolutionDependencies
#Region "Class Level Members"
''' <summary>
''' Stores the organization service proxy.
''' </summary>
Private _serviceProxy As OrganizationServiceProxy
Private _publisherId As Guid
Private Const _primarySolutionName As String = "PrimarySolution"
Private _primarySolutionId As Guid
Private _secondarySolutionId As Guid
Private Const _prefix As String = "sample"
Private Const _globalOptionSetName As String = _prefix & "_exampleoptionset"
Private _globalOptionSetId? As Guid
Private Const _picklistName As String = _prefix & "_examplepicklist"
' Specify which language code to use in the sample. If you are using a language
' other than US English, you will need to modify this value accordingly.
' See https://msdn.microsoft.com/en-us/library/0h88fahh.aspx
Private Const _languageCode As Integer = 1033
#End Region ' Class Level Members
#Region "How To Sample Code"
''' <summary>
''' Shows how to detect dependencies that may cause a managed solution to become
''' un-deletable.
'''
''' Get all solution components of a solution
''' For each solution component, list the dependencies upon that component.
''' </summary>
''' <param name="serverConfig">Contains server connection information.</param>
''' <param name="promptForDelete">When True, the user will be prompted to delete all
''' created entities.</param>
Public Sub Run(ByVal serverConfig As ServerConnection.Configuration,
ByVal promptForDelete As Boolean)
Try
' Connect to the Organization service.
' The using statement assures that the service proxy will be properly disposed.
_serviceProxy = ServerConnection.GetOrganizationProxy(serverConfig)
Using _serviceProxy
' This statement is required to enable early-bound type support.
_serviceProxy.EnableProxyTypes()
' Call the method to create any data that this sample requires.
CreateRequiredRecords()
' Grab all Solution Components for a solution.
Dim componentQuery As QueryByAttribute =
New QueryByAttribute With {
.EntityName = SolutionComponent.EntityLogicalName,
.ColumnSet = New ColumnSet("componenttype", "objectid",
"solutioncomponentid", "solutionid")
}
componentQuery.Attributes.Add("solutionid")
componentQuery.Values.Add(_primarySolutionId)
' 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 =
CType(_serviceProxy.Execute(dependentComponentsRequest),
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}",
dependentComponentsResponse.EntityCollection.Entities.Count,
component.ObjectId.Value,
component.ComponentType.Value)
'A more complete report requires more code
For Each d As Dependency In dependentComponentsResponse.EntityCollection.Entities
DependencyReport(d)
Next d
Next component
'Find out if any dependencies on a specific global option set would prevent it 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 =
CType(_serviceProxy.Execute(retrieveDependenciesForDeleteRequest_Renamed),
RetrieveDependenciesForDeleteResponse)
Console.WriteLine("")
For Each d As Dependency In retrieveDependenciesForDeleteResponse_Renamed _
.EntityCollection.Entities
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 =
CType(_serviceProxy.Execute(retrieveAttributeRequest_Renamed),
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
DeleteRequiredRecords(promptForDelete)
End Using
' Catch any service fault exceptions that Microsoft Dynamics CRM throws.
Catch fe As FaultException(Of Microsoft.Xrm.Sdk.OrganizationServiceFault)
' You can handle an exception here or pass it back to the calling method.
Throw
End Try
End Sub
''' <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,
CType(dependency.DependentComponentObjectId,
Guid))
requiredComponentName = getComponentName(dependency.RequiredComponentType.Value,
CType(dependency.RequiredComponentObjectId,
Guid))
' Retrieve the friendly name for the dependent solution.
Dim dependentSolution As Solution =
CType(_serviceProxy.Retrieve(Solution.EntityLogicalName,
CType(dependency.DependentComponentBaseSolutionId, Guid),
New ColumnSet("friendlyname")),
Solution)
dependentComponentSolutionName = dependentSolution.FriendlyName
' Retrieve the friendly name for the required solution.
Dim requiredSolution As Solution =
CType(_serviceProxy.Retrieve(Solution.EntityLogicalName,
CType(dependency.RequiredComponentBaseSolutionId,
Guid),
New ColumnSet("friendlyname")),
Solution)
requiredComponentSolutionName = requiredSolution.FriendlyName
'Display the message
Console.WriteLine("The {0} {1} in the {2} depends on the {3} {4} in the {5} solution.",
dependentComponentName,
dependentComponentTypeName,
dependentComponentSolutionName,
requiredComponentName,
requiredComponentTypeName,
requiredComponentSolutionName)
End Sub
' The name or display name of the component depends on the type of component.
Public Function getComponentName(ByVal Type As Integer, ByVal ComponentId As Guid) As String
Dim name As String = ""
Select Case Type
' A separate method is required for each type of component
Case componenttype.Attribute
name = getAttributeInformation(ComponentId)
Case componenttype.OptionSet
name = getGlobalOptionSetName(ComponentId)
Case Else
name = "not implemented"
End Select
Return name
End Function
' Retrieve the display name and parent entity information about an attribute solution component.
Public Function getAttributeInformation(ByVal id As Guid) As String
Dim attributeInformation As String = ""
Dim req As RetrieveAttributeRequest = New RetrieveAttributeRequest With {.MetadataId = id}
Dim resp As RetrieveAttributeResponse = CType(_serviceProxy.Execute(req), RetrieveAttributeResponse)
Dim attmet As AttributeMetadata = resp.AttributeMetadata
attributeInformation = attmet.EntityLogicalName & " : " & attmet.DisplayName.UserLocalizedLabel.Label
Return attributeInformation
End Function
'Retrieve the name of a global Option set
Public Function getGlobalOptionSetName(ByVal id As Guid) As String
Dim name As String = ""
Dim req As RetrieveOptionSetRequest = New RetrieveOptionSetRequest With {.MetadataId = id}
Dim resp As RetrieveOptionSetResponse = CType(_serviceProxy.Execute(req), RetrieveOptionSetResponse)
Dim os As OptionSetMetadataBase = CType(resp.OptionSetMetadata, OptionSetMetadataBase)
name = os.DisplayName.UserLocalizedLabel.Label
Return name
End Function
''' <summary>
''' This method creates any entity records that this sample requires.
''' Create a publisher
''' Create a new solution, "Primary"
''' Create a Global Option Set in solution "Primary"
''' Export the "Primary" solution, setting it to Protected
''' Delete the option set and solution
''' Import the "Primary" solution, creating a managed solution in CRM.
''' Create a new solution, "Secondary"
''' Create an attribute in "Secondary" that references the Global Option Set
''' </summary>
Public Sub CreateRequiredRecords()
'Create the publisher that will "own" the two solutions
Dim publisher As Publisher =
New Publisher With {
.UniqueName = "examplepublisher",
.FriendlyName = "An Example Publisher",
.Description = "This is an example publisher",
.CustomizationPrefix = _prefix
}
_publisherId = _serviceProxy.Create(publisher)
'Create the primary solution - note that we are not creating it
'as a managed solution as that can only be done when exporting the solution.
Dim primarySolution As Solution =
New Solution With {
.Version = "1.0",
.FriendlyName = "Primary Solution",
.PublisherId =
New EntityReference(publisher.EntityLogicalName, _publisherId),
.UniqueName = _primarySolutionName
}
_primarySolutionId = _serviceProxy.Create(primarySolution)
'Now, create the Global Option Set and associate it to the solution.
Dim optionSetMetadata As New OptionSetMetadata() With {
.Name = _globalOptionSetName,
.DisplayName = New Label("Example Option Set", _languageCode),
.IsGlobal = True,
.OptionSetType = OptionSetType.Picklist
}
optionSetMetadata.Options.AddRange(
{
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
_serviceProxy.Execute(createOptionSetRequest)
'Export the solution as managed so that we can later import it.
Dim exportRequest As ExportSolutionRequest =
New ExportSolutionRequest With {
.Managed = True,
.SolutionName = _primarySolutionName
}
Dim exportResponse As ExportSolutionResponse =
CType(_serviceProxy.Execute(exportRequest), ExportSolutionResponse)
' Delete the option set previous created, so it can be imported under the
' managed solution.
Dim deleteOptionSetRequest As DeleteOptionSetRequest =
New DeleteOptionSetRequest With {
.Name = _globalOptionSetName
}
_serviceProxy.Execute(deleteOptionSetRequest)
' Delete the previous primary solution, so it can be imported as managed.
_serviceProxy.Delete(Solution.EntityLogicalName, _primarySolutionId)
_primarySolutionId = Guid.Empty
' Re-import the solution as managed.
Dim importRequest As ImportSolutionRequest =
New ImportSolutionRequest With {
.CustomizationFile = exportResponse.ExportSolutionFile
}
_serviceProxy.Execute(importRequest)
' Retrieve the solution from CRM in order to get the new id.
Dim primarySolutionQuery As QueryByAttribute =
New QueryByAttribute With {
.EntityName = Solution.EntityLogicalName,
.ColumnSet = New ColumnSet("solutionid")
}
primarySolutionQuery.Attributes.Add("uniquename")
primarySolutionQuery.Values.Add(_primarySolutionName)
_primarySolutionId =
_serviceProxy.RetrieveMultiple(primarySolutionQuery) _
.Entities.Cast(Of Solution)().FirstOrDefault().SolutionId.GetValueOrDefault()
' Create a secondary solution.
Dim secondarySolution As Solution =
New Solution With {
.Version = "1.0",
.FriendlyName = "Secondary Solution",
.PublisherId = New EntityReference(publisher.EntityLogicalName, _publisherId),
.UniqueName = "SecondarySolution"
}
_secondarySolutionId = _serviceProxy.Create(secondarySolution)
' Create a Picklist attribute in the secondary solution linked to the option set in the
' primary - see WorkWithOptionSets.cs for more on option sets.
Dim picklistMetadata As PicklistAttributeMetadata =
New PicklistAttributeMetadata With {
.SchemaName = _picklistName,
.LogicalName = _picklistName,
.DisplayName = New Label("Example Picklist", _languageCode),
.RequiredLevel = New AttributeRequiredLevelManagedProperty(AttributeRequiredLevel.None),
.OptionSet = New OptionSetMetadata With {
.IsGlobal = True,
.Name = _globalOptionSetName
}
}
Dim createAttributeRequest As CreateAttributeRequest =
New CreateAttributeRequest With {
.EntityName = Contact.EntityLogicalName,
.Attribute = picklistMetadata
}
createAttributeRequest("SolutionUniqueName") = secondarySolution.UniqueName
_serviceProxy.Execute(createAttributeRequest)
End Sub
''' <summary>
''' Deletes any entity records that were created for this sample.
''' <param name="prompt">Indicates whether to prompt the user to delete the records created in this sample.</param>
''' </summary>
Public Sub DeleteRequiredRecords(ByVal prompt As Boolean)
Dim deleteRecords As Boolean = True
If prompt Then
Console.WriteLine(vbLf & "Do you want these entity records deleted? (y/n)")
Dim answer As String = Console.ReadLine()
deleteRecords = (answer.StartsWith("y") OrElse answer.StartsWith("Y"))
End If
If deleteRecords Then
Dim deleteAttributeRequest As DeleteAttributeRequest =
New DeleteAttributeRequest With {
.EntityLogicalName = Contact.EntityLogicalName,
.LogicalName = _picklistName
}
_serviceProxy.Execute(deleteAttributeRequest)
_serviceProxy.Delete(Solution.EntityLogicalName, _primarySolutionId)
_serviceProxy.Delete(Solution.EntityLogicalName, _secondarySolutionId)
_serviceProxy.Delete(Publisher.EntityLogicalName, _publisherId)
Console.WriteLine("Entity records have been deleted.")
End If
End Sub
#End Region ' How To Sample Code
#Region "Main"
''' <summary>
''' Standard Main() method used by most SDK samples.
''' </summary>
''' <param name="args"></param>
Public Shared Sub Main(ByVal args() As String)
Try
' Obtain the target organization's Web address and client logon
' credentials from the user.
Dim serverConnect As New ServerConnection()
Dim config As ServerConnection.Configuration =
serverConnect.GetServerConfiguration()
Dim app As New GetSolutionDependencies()
app.Run(config, True)
Catch ex As FaultException(Of Microsoft.Xrm.Sdk.OrganizationServiceFault)
Console.WriteLine("The application terminated with an error.")
Console.WriteLine("Timestamp: {0}", ex.Detail.Timestamp)
Console.WriteLine("Code: {0}", ex.Detail.ErrorCode)
Console.WriteLine("Message: {0}", ex.Detail.Message)
Console.WriteLine("Plugin Trace: {0}", ex.Detail.TraceText)
Console.WriteLine("Inner Fault: {0}",
If(Nothing Is ex.Detail.InnerFault, "No Inner Fault", "Has Inner Fault"))
Catch ex As TimeoutException
Console.WriteLine("The application terminated with an error.")
Console.WriteLine("Message: {0}", ex.Message)
Console.WriteLine("Stack Trace: {0}", ex.StackTrace)
Console.WriteLine("Inner Fault: {0}",
If(Nothing Is ex.InnerException.Message, "No Inner Fault", ex.InnerException.Message))
Catch ex As Exception
Console.WriteLine("The application terminated with an error.")
Console.WriteLine(ex.Message)
' Display the details of the inner exception.
If ex.InnerException IsNot Nothing Then
Console.WriteLine(ex.InnerException.Message)
Dim fe As FaultException(Of Microsoft.Xrm.Sdk.OrganizationServiceFault) =
TryCast(ex.InnerException,
FaultException(Of Microsoft.Xrm.Sdk.OrganizationServiceFault))
If fe IsNot Nothing Then
Console.WriteLine("Timestamp: {0}", fe.Detail.Timestamp)
Console.WriteLine("Code: {0}", fe.Detail.ErrorCode)
Console.WriteLine("Message: {0}", fe.Detail.Message)
Console.WriteLine("Plugin Trace: {0}", fe.Detail.TraceText)
Console.WriteLine("Inner Fault: {0}",
If(Nothing Is fe.Detail.InnerFault, "No Inner Fault", "Has Inner Fault"))
End If
End If
' Additional exceptions to catch: SecurityTokenValidationException, ExpiredSecurityTokenException,
' SecurityAccessDeniedException, MessageSecurityException, and SecurityNegotiationException.
Finally
Console.WriteLine("Press <Enter> to exit.")
Console.ReadLine()
End Try
End Sub
#End Region ' Main
End Class
End Namespace
See Also
Package and distribute extensions using solutions
Introduction to solutions
Plan for solution development
Dependency tracking for solution components
Create, export, or import an unmanaged solution
Create, install, and update a managed solution
Uninstall or delete a solution
Solution entities
Work with solutions
Sample: Work with solutions