Archive
[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.
- Input Arguments
- 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
[RequiredArgument]
[Input(“Company ID”)]
public InArgument<string> ContactCompanyID { get; set; }// Output Parameter – Matched Account will be passed back TO Workflow
[ReferenceTarget(“account”)]
[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);
try{
// 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);
}
else{
// 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);
throw;
}
}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.
Notes:
- 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.
🙂
Dynamics Portals – Portal Checker
‘Portal Checker’ has been added as part of Portal capabilities version 9.1.1 for Dynamics 365 CE apps.
Portal checker for Dynamics 365 for CE Portal is a self-service diagnostic tool that can be used by Portal administrators to identify common issues in their portal.
- Connect to Dynamics 365 Administration Center
- In the Portal administration screen, click on ‘Diagnose and resolve problems’
Portal checker helps to identify issues with your portal by looking at various configuration parameters and provides suggestions on how to fix them.
Refer this article for more details
🙂
Dynamics – Troubleshooting Server Side Sync exceptions
Server-side synchronization is used to synchronize your email system with Dynamics 365 for Customer Engagement apps at the server level.
Now coming to the issue, I was configuring Server side sync on my CRM on-premise instance with ‘Gmail’ as host and got the below error while testing the mail delivery of a ‘Mailbox’ using ‘Test & Enable Mailbox’ option.
Reason:
- In my case, its CRM on-premise installation and were having below 3 servers
- Front End Server – Contain CRM WebApp
- Application Server – Contain Services (Async, Sandbox, etc..)
- Back End Server – Contain SQL Server and SSRS
- Issue was ‘Incoming Port’ and ‘Outgoing Port’ were not opened on my ‘Application server’ which caused the issue
Now lets see few sanity steps and how to troubleshoot issues.
[Sanity Step] Make sure Ports and Mailbox settings are correct:
- Test the settings provided in ‘Email Server Profile’ using console application
- Create a simple C# console application with below code and pass the settings (i.e., Port, host names etc) mentioned in ‘Email Server Profile’
using (MailMessage mail = new MailMessage()){
int portNumber = Convert.ToInt32(ConfigurationManager.AppSettings[“port”].ToString());
var smtpAddress = ConfigurationManager.AppSettings[“smtpAddress”].ToString();
var emailFromAddress = ConfigurationManager.AppSettings[“emailFromAddress”].ToString();
var password = ConfigurationManager.AppSettings[“password”].ToString();
var emailToAddress = ConfigurationManager.AppSettings[“emailToAddress”].ToString();mail.From = new MailAddress(emailFromAddress);
mail.To.Add(emailToAddress);
mail.Subject = “Set mail subject”;
mail.Body = “Set mail body”;
mail.IsBodyHtml = true;
using (SmtpClient smtp = new SmtpClient(smtpAddress, portNumber)){
smtp.Credentials = new NetworkCredential(emailFromAddress, password);
smtp.EnableSsl = enableSSL;
Console.WriteLine(“Sending mail…”);
smtp.Send(mail);
Console.WriteLine(“Mail has been sent…”);
}
}
App Settings:
<appSettings>
<add key=”port” value=”587″/>
<add key=”smtpAddress” value=”smtp.gmail.com”/>
<add key=”emailFromAddress” value=”rajeevpentyala@gmail.com”/>
<add key=”password” value=”*******”/>
<add key=”emailToAddress” value=”rajeevpentyala@live.com”/>
</appSettings>
[Sanity Step] Make sure your Incoming and Outgoing server locations are reachable:
- From command prompt, ping the locations with below command and make sure you are getting response
- Ping -t {location} (Ex- Ping -t smtp.gmail.com)
[Issue] If failed with “The SMTP server requires a secure connection” exception
- Connect to your Gmail account. Link
- Turn on the “Less secure app access” option

Turn on ‘Less secure app access’
- You may also get “Smtp server returned OutgoingServerSecureChannelError MustIssueStartTlsFirst exception.“ message and the fix is same.
[Issue] Client was not authenticated. The server response was: 5.5.1 Authentication Required
- You might get this issue, If the password of your Email Id requires a reset. Change your password and try by providing new password in Mail Box.
Refer this article for more exhaustive list of troubleshooting options
🙂
Dynamics 365 Portal – Troubleshoot errors hosted in Azure
Recently we got internal server error while we were accessing Portal hosted in Azure.
Microsoft Azure is providing simple and quick way to troubleshoot the errors.
There are few options to troubleshoot like ‘Log Stream’ and ‘Diagnostic Dumps’. In this Article, I am going to show how to use them.
Pre-requisite:
- Connect to your Azure account and open the Web App from ‘App Services’
- Make sure you enable “Application Logging” from ‘Diagnostics logs’ section
Log Stream:
- ‘Log Stream’ is helpful to track the ongoing exceptions and track the live Request and Response.
Diagnostic Dump:
- Connect to KUDU services by clicking on ‘Advanced Tools –> Go’
- Click on Tools –> Diagnostic Dump
- Download and extract the .zip folder
- Go to ‘LogFiles’
- Open the ‘eventlog.xml’ using any file editor
‘Application Insights’ is another option which helps you detect and diagnose quality issues in your web apps and web services.
🙂