[Code Snippet] Custom Workflow Activity with Input and Output Params

Of late , I was getting questions around the usage of custom workflow activity and troubleshooting.

In this article, I am going to cover creation and the usage of custom workflow activity with both Input and Output arguemnts.

To simplify the understanding, lets take a simple scenario:

  • ‘Contact’ and ‘Account’ entities have 2 custom attributes (Company ID, Company Name)
  • Whenever a new ‘Contact’ gets created, check if there is an existing ‘Account’ with the ‘Contact.Company ID’
    • If no existing Account, create a new Account with Contact’s data.
    • If there is an existing Account, update Account with Contact’s data.

To achieve this,I am going create a new Custom workflow activity with below design:

  • Create a new ‘Custom Workflow Activity’ with the filter logic to check whether there is an existing ‘Account’ with ‘Company ID’ and returns the existing Account.
  • In Dynamics, Create a new Workflow on ‘Contact’ creation and trigger the newly created custom workflow activity by passing ‘Company Code’.

Lets go step by step.

Step 1: Create a new Custom Workflow Activity:

  • Open a new Class Library project in Visual Studio and download dynamics core library from Nuget .
  • The workflow will have 3 Arguments
    • Input Arguments
      • ContactCompanyID – ‘Company ID’ of Created Contact. Value will be passed from Dynamics Workflow.
    • Output Arguments
      • ExistingAccount – Entity reference of matching Account by ‘Company ID’. Value.
      • IsAccountExists – Boolean.
  • Complete Code:

using System;
using System.Activities;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Workflow;

public class CreateOrUpdateAccountActivity : CodeActivity{

// Input Parameter – Contact’s Company will be passed to this Parameter FROM Workflow
[Input(“Company ID”)]
public InArgument<string> ContactCompanyID { get; set; }

// Output Parameter – Matched Account will be passed back TO Workflow
[Output(“Existing Account”)]
public OutArgument<EntityReference> ExistingAccount { get; set; }

// Output Parameter – Boolean flag will be passed TO Workflow
[Output(“Account Exists?”)]
public OutArgument<bool> IsAccountExists { get; set; }

protected override void Execute(CodeActivityContext executionContext){
var tracingService = executionContext.GetExtension<ITracingService>();
var context = executionContext.GetExtension<IWorkflowContext>();
var serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
var service = serviceFactory.CreateOrganizationService(context.UserId);
// Read the ‘Company ID’ from Workflow Input variable
var contactCompanyName = this.ContactCompanyID.Get<string>(executionContext);

// Check if Account exists already with the Contact’s Company ID
var matchedAccount = GetAccountByCompanyID(contactCompanyName, service);

if (matchedAccount == null){
// Set the boolean output param to false, as No matched Account
this.IsAccountExists.Set(executionContext, false);
// Set the matched Account to Workflow Output param.
this.ExistingAccount.Set(executionContext, new EntityReference(matchedAccount.LogicalName, matchedAccount.Id));
// Set the boolean output param to true, as matched Account found
this.IsAccountExists.Set(executionContext, true);
catch (Exception ex){
tracingService.Trace(“Error in CreateOrUpdateAccountActivity – ” + ex.Message);

private static Entity GetAccountByCompanyID(string companyID, IOrganizationService service){
var queryAccounts = new QueryExpression(“account”){
ColumnSet = new ColumnSet(new string[] { “accountid” })
var filterAccount = new FilterExpression(LogicalOperator.And);
filterAccount.AddCondition(“new_companyid”, ConditionOperator.Equal, companyID);

queryAccounts.Criteria = filterAccount;
var contacts = service.RetrieveMultiple(queryAccounts);
if (contacts != null && contacts.Entities != null && contacts.Entities.Count > 0){
return contacts[0];

return null;

  • Sign the Assembly and build the code
  • Register the ‘Custom Workflow Activity’ using Plug-in Registration Tool.


Step 2: Create Dynamics Workflow Process:

  • Create a new Workflow Process on ‘Contact’ creation.


  • Under the Workflow steps, as a first step trigger the Custom Workflow Activity


  • Click on ‘Set Properties’ and set the ‘Company ID’ argument with ‘Contact.Company ID’


  • Next, add a ‘Check Condition’ step to check the ‘IsAccountExists’ value returned by custom workflow activity.


  • Set the condition, If ‘Account Exists?’ (This is the display name of Input argument we set in custom workflow) is ‘False’


  • As a child step of ‘Check Condition’, add a ‘Create Record’ step to create a new ‘Account’


  • Next, add another ‘Check Condition’ step, to handle the ‘Else’ condition and update the ‘Existing Account’
  • Under the Else ‘Check Condition’, add an ‘Update Record’ step to update the ‘Account’ returned by ‘Custom Workflow Activity’


  • Activate the workflow


Test the flow:

  • Create a new ‘Contact’


  • Check the workflow execution by clicking on ‘Process Sessions’ tab from the workflow. This helps us to troubleshoot, in case of any issues in our workflow steps.


  • Since all the steps in above screen are Success, now Go to ‘Accounts’ and you should see a new ‘Account’ as there was no existing Account already.WF_11


  • Compare to writing the whole logic server side, this approach gives us the flexibility to map the desired fields from ‘Contact’ to ‘Account’ as its a Workflow.



[Step by Step] Using ADX Auto Numbering Solution In Plug-ins

As we know with Dynamics 365 for CE 9.0 release, we can add an auto-number attribute for any entity programmatically.

Prior to Dynamics 9.0, ‘AdxstudioAutoNumberingWorkflowHelper’ solution is one of the options to implement auto numbering.

Lets see how to use this solution and generate auto number on Case entity.

What does AdxstudioAutoNumberingWorkflowHelper solution contain?

  • AdxstudioAutoNumberingWorkflowHelper solution contain 2 key components
  • AN_8
    • ‘Auto Numbering Definition’ entity – Where you define auto numbering pattern.
    • ‘Adxstudio.Xrm.Workflow.AutoNumberHelper’ custom workflow – Which generates the Auto number as per the configurations made in ‘Auto Numbering Definition’ entity

Below are the high level steps:

  • Download and Install ADX solution and configure ‘Auto Number Definition’.
  • Create a new ‘Action’ which calls Adx Auto Number Workflow.
  • Trigger ‘Action’ from ‘Plugin’

Install ADX solution and configure Definition:

  • Download and install the solution.
  • Once the solution is installed you would get Sitemap node as below under ‘Settings’.


  • Create a record in ‘Auto Numbering Definition’ entity and set below fields
  • AN_3
    • Name – Can be anything. ‘Name’ has to be passed as parameter to the ‘Adxstudio.Xrm.Workflow.AutoNumberHelper’ workflow
    • Format – Pattern of Auto number
    • Digits – Length of your auto number digits.

Create a new ‘Action’ which calls Adx Auto Number Workflow:

Best way to trigger workflow from Plug-in is by creating an ‘Action’

  • Create a new ‘Action’
  • Add 2 parameters
    • ‘NumberDefintion’ – Input : We use this to pass ‘Auto Numbering Definition’ Name
    • AutoNumber – Output : We use this to capture the generated  ‘Auto Number’
  • Add 2 steps to the ‘Action’
    • Step 1 – Trigger ‘Adxstudio Workflow Helpers:Get Auto-Number’  with below PropertiesAN_2
    • Step 2 – Capture the generated Auto Number using ‘Assign Value:’ step with below Properties


  • Overall ‘Action’ looks as below


  • Activate the ‘Action’

Trigger ‘Action’ from ‘Plugin’:

  • Below is the code to execute the ‘Action’ by passing Parameter and read the ‘Auto Number’

var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
var serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var svc = serviceFactory.CreateOrganizationService(context.UserId);

if (context.InputParameters.Contains(“Target”) && context.InputParameters[“Target”] is Entity) {
var entCase = context.InputParameters[“Target”] as Entity;

// Logic to trigger Custom Action which trigger ADX Auto Number WF
// Pass your Custom Action Name (i.e., raj_TriggerADXAutoNumber in my case)
var orgRequest = new OrganizationRequest(“raj_TriggerADXAutoNumber”);
// Pass ‘Auto Number Definition Name’ you configured in system
orgRequest[“NumberDefinition”] = “Case Auto Number”;

// Execute the request
OrganizationResponse response = svc.Execute(orgRequest);
string autoNumber = response.Results[“AutoNumber”].ToString();

// Set generated auto number to Case’s Title field
entCase[“title”] = autoNumber;

  • Register the Plug-in on ‘PreCaseCreate’
  • Create a Case and you would get the number set on ‘Title’ field







Dynamics Portals – Display Plug-in error messages

You would have got “An unknown failure has occurred…” many times in your portal when there was exception thrown by your Dynamics CE plug-in code.

Now in Dynamics Portals, we can show the plug-in error in following portal screens by adding “Site/EnableCustomPluginError” entry in Dynamics CE Portals -> Site Settings

  • Entity list
    • Retrieval of records
  • Entity form
    • Retrieve
    • Create/Update and so on
  • Web forms
    • Retrieve
    • Create/Update and so on

Lets see the approach and steps.

  • I have created and registered a plug-in with logic to intentionally throw an exception on ‘Case’ Create.


  • Go to your Portal and try to create a Case and you would get below generic exception “An unknown failure…”


  • Now open your Dynamics CE application, navigate to Portals -> Site Settings and create a new Site Setting with Name “Site/EnableCustomPluginError” and Value true.


  • Go back to your Portal and try to create a Case and you would get actual Plug-in error.



Azure DevOps – Getting started by committing a C# console project to Repo and set Policies

February 23, 2019 Leave a comment

Those who are hearing ‘Azure DevOps Services’ for the first time, its formerly known as ‘Visual Studio Team Services (VSTS)’.

And Team Foundation Server (TFS) is now called ‘Azure DevOps Server’.

Below table gives the glimpse of how VSTS features represents in Azure DevOps.


To know more about Azure DevOps refer this link

In this article, I am going provide the steps to sign-up for ‘Azure DevOps’ and explain how to on board a C# console project.


  • Microsoft account.
    • You can also use your 30 days trail Dynamics Account to login.
    • If you’re a Visual Studio subscriber and you get Azure DevOps as a benefit, use the Microsoft account associated with your subscription

Sign up for Azure DevOps:

  • Navigate to the VS Portal and click on ‘Get started for free’ as highlighted below


  • Log in with your ‘Microsoft Account’


  • Once you complete the registration, you will be redirected to Azure DevOps portal.


  • A new Organization would have got created by now and to sign in to your organization at any time, go to{yourorganization}.

Create a New Project:

Once you got your Organization ready, next you need to create a new ‘Project’.

  • On the ‘Create a Project to get started’ form, provide your project name and set the Visibility.
    • Set the Visibility to ‘Private’ if you are working on a customer project which you would not want to expose to Public.
    • ‘Public’ visibility is meant for Open Source projects.


  • Under ‘Advanced’ tab, I am going with ‘Git’ as my Version Control and ‘Agile’ as Work item process.
  • Your ready with Project and we are close to on board our C# console project.


After you create a new Organization and Project, you can begin coding with Git using Repos.

  • Click on ‘Repos’.
  • As a first step in Repos, add ‘README’ or ‘gitignore’, as shown in below.Note that this step is optional but very effective house keeping step.
    • README – You can provide description and objective of your project.
    • gitignore – Will ignore unwanted files to be added to repos.
      • As an example you don’t need components like (Actual nuGet packages, .suo files which comes with VS).
      • I’ve added ‘VisualStudio’ option to the ‘gitignore’ which ignores all unwanted VS files.


  • Click ‘Initialize’
  • Now you would see 2 files added to your Repo.
  • As mentioned, ‘.gitignore’ contain unwanted file extensions, auto populated. You can add/remove if you want.


  • ‘’, is a Mark Down file which is a combination of Text and HTML tags. Add your project description.
    • Denotes <H1> tag; ## denotes <H2> tag.


Clone the Repo to your computer:

To work with a Git repo, you clone it to your computer. Cloning a repo creates a complete local copy of the repo for you to work with.

In this article, I am going to use ‘Visual Studio’ to Clone the Repo. You can also use ‘Command line’ commands using ‘Command Prompt’

  • Click ‘Clone’ and select ‘Clone in Visual Studio’ option under ‘IDE’.


  • Now Visual Studio opens up and you might need to provide your Microsoft Account credentials.
  • In case if you get ‘Unauthorization’ error, you can connect as below from your VS ‘Team Explorer’.


  • Once you complete the clone, the 2 files (i.e., .gitignore, would synced to your computer folder.

Commit C# Console Project to Repo:

Once you Cloned of your DevOps ‘Project’ to your local computer folder, below are the steps to add a new C# console project to Repo.

  • Create a new C# console application project and save at the same folder location where you cloned your Azure Project.
  • Now you would see the Console project in your Visual Studio.
  • You would notice #5 in the Visual Studio footer, which denotes 5 new files are new and not synced to your Repo.


  • You need to commit the files to sync with your Repo.
  • Click on #5 icon and provide mandatory ‘Commit comments’ and choose ‘Commit All and Push’ option to commit and upload the C# console project to Repo.


  • Now, go to your Azure DevOps portal and you would see your C# console project.


  • You can also Edit the class files directly from DevOps portal.

Branch Policies:

As you would have noticed, Visual Studio did not allow me to commit the files until I provide the ‘Comments’. Its a policy implied by default by DevOps.

Lets see how to configure the policies.

  • Once you create a new Project, DevOps will create a default ‘master’ branch.
    • Refer article for more details on ‘Branch’.
  • Under Repos->Branches, select ‘Branch Policies’ from the Branch menu.


  • You can configure appropriate policies mainly to improve the code quality.
    • Refer link to know more details on Policies.


Grant access on Project to Users:

For every new project you add to DevOps Organization, a new Team gets created with naming convention {Project Name} Team.

  • Click on ‘Teams’ tab as shown in screen below and pick your Project team.
  • ‘+Add’ to add users


  • Refer this link to know more details on managing Teams.

Service Hooks:

  • Service hooks let you run tasks on other services when events happen in your Azure DevOps Services projects.
  • For example, you can create an entry in Azure Service Bus when there Code commit happens.


  • Refer this article for more details on ‘Service Hooks’

In my next article I will share the details on how to implement Build activities using Pipelines


Dynamics Portals – Entity List – Integration using OData feed

February 17, 2019 Leave a comment

With Dynamics Portal’s Entity list ‘OData Feed’ option, we can expose the data in the form OData API, which can be further consumed by external applications.

Lets take a scenario,

  • You have a public facing Dynamics Portal where people gets registered, which get stored as ‘Contacts’ in Dynamics CE instance.
  • You need to share all the ‘Active Contacts’ to your back end team which uses an Excel sheet to manage data.

It can be achieved by enabling OData feed on Entity List by following below steps:

  • Open your Entity List and go tot ‘OData Feed’ section. (Refer my previous articles on how to create an Entity List)
  • Enable the ‘OData Feed’ option along with below details
    • Entity Type Name : Your Dynamics entity schema name
    • Entity Set Name : Can be anything but as a best practice provide plural name of your Dynamics entity.
    • View : Select the view.  The structure of your OData feed would be determined based on the view you pick.


  • Once the ‘OData Feed’ enabled, you can get all the oData feeds enabled on Portals by forming URL -> {Your Portal URL}+”/_odata


  • Now access the ‘Contacts’ oData feed by forming URL -> {Your Portal URL}+”/_odata“+Entity Set Name (i.e., contacts)


How to consume the oData API in Excel:

  • Copy the OData set URL
  • In the new Excel sheet, go to Data -> Get Data -> From Other Sources -> From OData Feed


  • Paste the OData URL and click ‘OK’


  • Click ‘Load’ to load the data to excel sheet.


  • If you want to Transform data like replace ‘Null’, click on ‘Transform Data’


  • Your excel sheet shall looks as below once the data loads.



  • The OData API URL is accessible anonymously with no authentication prompted.




Dynamics Portals – Entity List ‘Map View’

February 16, 2019 Leave a comment

Dynamics Portal’s Entity List has an option to pin the records on a Map based on the Address of those records.

How does ‘Map View’ looks on Portal:

  •  With ‘Map View’ enabled, you will get a Map (bing/google) and matching records will be pinned on the map (i.e., Refer 1 pinned on the Map below).


How to set up ‘Map View’:

  • On the ‘Entity List’ form, check the ‘Map Enabled’ checkbox.
  • In ‘Entity Field Mappings’, map the address fields from ‘Contact’ entity (As my entity list mapped to Contact).


  • Under ‘Settings’ section, provide below details
    • Map Type : Bing/Google (I chose ‘Bing’ for this article)
    • Credentials : Bing API credentials (You need to login here with your Live/Outlook account and generate the Key)
    • Portal_Map_5
    • Default Center Latitude : This is important. Geo coordinates where you want your map start from (I took ‘New Jersey’ coordinates as Default)
    • Default Center Longitude : This is important. Geo coordinates where you want your map start from (I took ‘New Jersey’ coordinates as Default)
    • Save
  • On Portal, you will notice a Map loaded with ‘Default Center Latitude’ and ‘Default Center Longitude’ (i.e., ‘New Jersey’ in this example).


  • In the ‘Address’ search box, provide ‘New York’ and click ‘Search’.


  • As I configured Contact ‘Rajeev Pentyala’ with ‘New York’ coordinates (Refer screen below), Contact showed up in above screen.


  • You can click on ‘Get Directions’ to get directions between Searched city (i.e., ‘New York’) and Default coordinates (i.e., New Jersey).