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

Assembly Microsoft.Dynamics.Service.Plugins.dll can not be loaded. Dynamics CRM 365 Engine version 9 - CRM User creation error

Exception caught instantiating TERADATA report server extension SQL Reporting Services

Could not load file or assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. The system cannot find the file specified at Configuration class initiation in CrmServiceHelper.cv