Therapy coding for Dynamics 365 CE - Business Process Flow creation for a custom entity then activation BPF related codes finally adding it to a custom solution as a component

Hello,

 Here is the console application of BPF creation, activation and appending to Test Solution:




#region create business process flow

string nameoftheEntityBPFtobeCreatedfor = "new_test";
CrmServiceClient service = null;
try
{
    service = Your_Connection_Obkject;

    if (service.IsReady)
    {
        
        #region Demonstrate

        #region Define workflow XAML

        // Define the workflow XAML.
        string xamlWF;
        string GuidMain = Guid.NewGuid().ToString();
        String GuidforControlClassId = Guid.NewGuid().ToString();
        String GuidforLabelId = Guid.NewGuid().ToString();
        String GuidforStepLabelId = Guid.NewGuid().ToString();


        xamlWF = @"
<Activity x:Class=""XrmWorkflow" + GuidMain.Replace("{", "").Replace("}", "").Replace("-", "") + @""" xmlns=""http://schemas.microsoft.com/netfx/2009/xaml/activities"" xmlns:mcwb=""clr-namespace:Microsoft.Crm.Workflow.BusinessProcessFlowActivities;assembly=Microsoft.Crm.Workflow, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"" xmlns:mcwo=""clr-namespace:Microsoft.Crm.Workflow.ObjectModel;assembly=Microsoft.Crm, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"" xmlns:mva=""clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"" xmlns:mxs=""clr-namespace:Microsoft.Xrm.Sdk;assembly=Microsoft.Xrm.Sdk, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"" xmlns:mxswa=""clr-namespace:Microsoft.Xrm.Sdk.Workflow.Activities;assembly=Microsoft.Xrm.Sdk.Workflow, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"" xmlns:scg=""clr-namespace:System.Collections.Generic;assembly=mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"" xmlns:sco=""clr-namespace:System.Collections.ObjectModel;assembly=mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"" xmlns:srs=""clr-namespace:System.Runtime.Serialization;assembly=System.Runtime.Serialization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"" xmlns:this=""clr-namespace:"" xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml"">
<x:Members>
<x:Property Name=""InputEntities"" Type=""InArgument(scg:IDictionary(x:String, mxs:Entity))"" />
<x:Property Name=""CreatedEntities"" Type=""InArgument(scg:IDictionary(x:String, mxs:Entity))"" />
</x:Members>
<this:XrmWorkflow" + GuidMain.Replace("{", "").Replace("}", "").Replace("-", "") + @".InputEntities>
<InArgument x:TypeArguments=""scg:IDictionary(x:String, mxs:Entity)"" />
</this:XrmWorkflow" + GuidMain.Replace("{", "").Replace("}", "").Replace("-", "") + @".InputEntities>
<this:XrmWorkflow" + GuidMain.Replace("{", "").Replace("}", "").Replace("-", "") + @".CreatedEntities>
<InArgument x:TypeArguments=""scg:IDictionary(x:String, mxs:Entity)"" />
</this:XrmWorkflow" + GuidMain.Replace("{", "").Replace("}", "").Replace("-", "") + @".CreatedEntities>
<mva:VisualBasic.Settings>Assembly references and imported namespaces for internal implementation</mva:VisualBasic.Settings>
<mxswa:Workflow>
<mxswa:ActivityReference AssemblyQualifiedName=""Microsoft.Crm.Workflow.BusinessProcessFlowActivities.StageRelationshipCollectionComposite, Microsoft.Crm.Workflow, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"" DisplayName=""RelationshipCollectionStep1"">
<mxswa:ActivityReference.Properties>
<sco:Collection x:TypeArguments=""Variable"" x:Key=""Variables"" />
<sco:Collection x:TypeArguments=""Activity"" x:Key=""Activities"" />
</mxswa:ActivityReference.Properties>
</mxswa:ActivityReference>
<mxswa:ActivityReference AssemblyQualifiedName=""Microsoft.Crm.Workflow.Activities.EntityComposite, Microsoft.Crm.Workflow, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"" DisplayName=""EntityStep2: new_test"">
<mxswa:ActivityReference.Properties>
<sco:Collection x:TypeArguments=""Variable"" x:Key=""Variables"" />
<sco:Collection x:TypeArguments=""Activity"" x:Key=""Activities"">
<mxswa:ActivityReference AssemblyQualifiedName=""Microsoft.Crm.Workflow.Activities.StageComposite, Microsoft.Crm.Workflow, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"" DisplayName=""StageStep3: New Stage"">
<mxswa:ActivityReference.Properties>
<sco:Collection x:TypeArguments=""Variable"" x:Key=""Variables"" />
<sco:Collection x:TypeArguments=""Activity"" x:Key=""Activities"">
<mxswa:ActivityReference AssemblyQualifiedName=""Microsoft.Crm.Workflow.Activities.StepComposite, Microsoft.Crm.Workflow, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"" DisplayName=""StepStep4: New Step"">
<mxswa:ActivityReference.Properties>
<sco:Collection x:TypeArguments=""Variable"" x:Key=""Variables"" />
<sco:Collection x:TypeArguments=""Activity"" x:Key=""Activities"">
    <Sequence DisplayName=""ControlStep5"">
    <mcwb:Control ClassId=""" + GuidforControlClassId.Replace("{", "").Replace("}", "") + @""" ControlDisplayName=""Name"" ControlId=""new_name"" DataFieldName=""new_name"" IsSystemControl=""False"" IsUnbound=""False"" SystemStepType=""0"">
        <mcwb:Control.Parameters>
        <InArgument x:TypeArguments=""x:String"">
            <Literal x:TypeArguments=""x:String"" Value="""" />
        </InArgument>
        </mcwb:Control.Parameters>
    </mcwb:Control>
    </Sequence>
</sco:Collection>
<sco:Collection x:TypeArguments=""mcwo:StepLabel"" x:Key=""StepLabels"">
    <mcwo:StepLabel Description=""Name"" LabelId=""" + GuidforLabelId.Replace("{", "").Replace("}", "") + @""" LanguageCode=""1033"" />
</sco:Collection>
<x:String x:Key=""ProcessStepId"">" + GuidforLabelId.Replace("{", "").Replace("}", "") + @"</x:String>
<x:Boolean x:Key=""IsProcessRequired"">False</x:Boolean>
</mxswa:ActivityReference.Properties>
</mxswa:ActivityReference>
</sco:Collection>
<sco:Collection x:TypeArguments=""mcwo:StepLabel"" x:Key=""StepLabels"">
<mcwo:StepLabel Description=""New Stage"" LabelId=""" + GuidforStepLabelId.Replace("{", "").Replace("}", "") + @""" LanguageCode=""1033"" />
</sco:Collection>
<x:String x:Key=""StageId"">" + GuidforStepLabelId.Replace("{", "").Replace("}", "") + @"</x:String>
<x:String x:Key=""StageCategory"">-1</x:String>
<x:Null x:Key=""NextStageId"" />
</mxswa:ActivityReference.Properties>
</mxswa:ActivityReference>
</sco:Collection>
<x:Null x:Key=""RelationshipName"" />
<x:Null x:Key=""AttributeName"" />
<x:Boolean x:Key=""IsClosedLoop"">False</x:Boolean>
</mxswa:ActivityReference.Properties>
</mxswa:ActivityReference>
</mxswa:Workflow>
</Activity>

        ";


        #endregion Define workflow XAML

        #region Create Workflow

        // Create an asynchronous workflow.
        // The workflow should execute after a new opportunity is created.
        Workflow workflow = new Workflow()
        {
            // These properties map to the New Process form settings in the web application.
            Name = "Business Process Flow for PowerStages",
            Type = new OptionSetValue((int)WorkflowTypeEnum.Definition),
            Category = new OptionSetValue((int)WorkflowCategoryEnum.BusinessProcessFlow),
            PrimaryEntity = nameoftheEntityBPFtobeCreatedfor,
            Mode = new OptionSetValue((int)WorkflowModeEnum.Realtime),

            // Additional settings from the second New Process form.
            Description = @"it is dedicated for PowerStages",
            OnDemand = false,
            Subprocess = false,
            Scope = new OptionSetValue((int)WorkflowScopeEnum.Organization),
            //TriggerOnCreate = true,
            //AsyncAutoDelete = true,
            Xaml = xamlWF,

            // Other properties not in the web forms.
            LanguageCode = 1033  // U.S. English                        
        };

        _workflowId = service.Create(workflow);

        Console.WriteLine("Created process '" + workflow.Name + "'");

        #endregion Create Workflow

        #region Activate Workflow

        // Activate the workflow.
        var activateRequest = new SetStateRequest
        {
            EntityMoniker = new EntityReference
                (Workflow.EntityLogicalName, _workflowId),
            State = new OptionSetValue((int)WorkflowStateEnum.Activated),
            Status = new OptionSetValue((int)workflow_statuscodeEnum.Activated)
        };
        OrganizationResponse activateRepose = service.Execute(activateRequest);
        Console.WriteLine("Activated process '" + workflow.Name + "'");

        #endregion Activate Workflow

        #region Add BPF to PowerStages Solution

        // Retrieve a solution

        String solutionUniqueName = "TestSolution";

        QueryExpression queryTestSolution = new QueryExpression { EntityName = Solution.EntityLogicalName, ColumnSet = new ColumnSet(new string[] { "publisherid", "installedon", "version", "versionnumber", "friendlyname", "uniquename" }), Criteria = new FilterExpression() }; 
        queryTestSolution.Criteria.AddCondition("uniquename", ConditionOperator.Equal, solutionUniqueName);

        Solution TestSolution = (Solution)service.RetrieveMultiple(queryTestSolution).Entities[0];

        // Add an existing Solution Component 

        //Add the workflow to the solution 
                           
        AddSolutionComponentRequest addReq = new AddSolutionComponentRequest() { ComponentType = (int)componenttype.Workflow, ComponentId = _workflowId, SolutionUniqueName = TestSolution.UniqueName };

        service.Execute(addReq);

        #endregion

        #endregion Demonstrate

        // CleanUpSample(service);
    }
    else
    {
        const string UNABLE_TO_LOGIN_ERROR = "Unable to Login to Common Data Service";
        if (service.LastCrmError.Equals(UNABLE_TO_LOGIN_ERROR))
        {
            Console.WriteLine("Check the connection string values in cds/App.config.");
            throw new Exception(service.LastCrmError);
        }
        else
        {
            throw service.LastCrmException;
        }
    }
}
catch (Exception ex)
{
    SampleHelpers.HandleException(ex);
}

finally
{
    if (service != null)
        service.Dispose();

    Console.WriteLine("Press <Enter> to exit.");
    Console.ReadLine();
}

#endregion





enjoy !

Edit - 2020/06/29

Important Notice:

String GuidforControlClassId = Guid.NewGuid().ToString();

I have realized that we can not set random GUID as a ClassId. We have to fetch it from CRM.

Since I use createdon field in the sample, I searched ClassId of DateTime in the CRM and find that we are able to reach ClassId from any forms which have DateTime field (Controller) in the UI with following javascript;

formContext.getControl('createdon').$0_4.$J_2.ClassId.$r_0

I am sorry to say that It is unsupported.

Comments

Popular posts from this blog

Complex Query in QueryExpression in Microsoft CRM 2011

Exception caught instantiating TERADATA report server extension SQL Reporting Services

Microsoft Power Apps Portal integration with Dynamics 365 CE On-Premise - Step By Step Guide