CUSTOM WORKFLOW ACTIVITIES IN CRM ONLINE
With the Microsoft Dynamics CRM Q2 service update, developers can build custom .NET Workflow activity assemblies for Microsoft Dynamics CRM Online. These custom assemblies can be developed and deployed as part of a Microsoft Dynamics CRM solution package. Once a custom workflow activity has been deployed, it can then be utilized within the workflow engine for business process management. These new capabilities ensure parity in the developer experience between Microsoft Dynamics CRM Online and on-premises. It also empowers organizations to bring external data, custom actions and integrations into their standard CRM business processes.
the Custom Workflow Activities could be interesting, also for product development in CRM 2011. Today, the Custom Workflow Activities are running in OnPremise Deployments but not in Online Systems. Because the Microsoft SDK Example is a little too confusing for a first approach, i have decided to create this small examle (based on the SDK-Stuff).
The Problem:
Create a Custom Workflow Activity, that creates a (follow up-)Task after an Account was created. (This is just an example. You can use default workflow functionality to reach this target.)
1. Create Solution with Project
The first step is to create a Visual Studio 2010 Project.
- Select Visual C# -> Workflow -> Activity Library
- Select .NET Framework 4
- Select Name for Solution and Project
- Select Location
Visual Studio Project |
Check that the Framework-Type is NOT .NET Framework Client Profile. The correct one is .NET Framework 4.
Target framework (should be .NET Framework 4) |
Delete the automatically created class Activity1.xaml and create a class called CreateTask.cs.
The new class |
Add references to the following assemblies (Rightclick References in the project and add...).
- microsoft.xrm.sdk.dll
- microsoft.xrm.sdk.workflow.dll
Add the reference for System.Runtime.Serialization, which is part of the .NET Framework.
Serialization |
(Hint: The Add References window here is not the default VS2010 one. This one is part of the Productivity Power Tools. A (MUST HAVE-) Addon for Visual Studio 2010!)
Add the following using-Statements to your class.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Activities; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Workflow;
References and Usings |
5. Inheritance
Make our class inherit from CodeActivity (System.Activities) and implement the method Execute. You also have to make the class public.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Activities; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Workflow; namespace MK.CRM2011.CustomWorkflowStep { public class CreateTask : CodeActivity { } }
6. Input Parameters
We would like to put a parameter in our Workflow. So every customer can define the subject of the task by it's own.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Activities; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Workflow; namespace MK.CRM2011.CustomWorkflowStep { public class CreateTask : CodeActivity { #region Input Properties [Input("Task Subject")] [Default("Empty Subject" )] public InArgument<string>TaskSubject{ get; set; } #endregion protected override void Execute(CodeActivityContext context) { throw new NotImplementedException(); } } }
-------------------
The following code has to be added into the Execute-Method.
-------------------
7. Create Context and Service
The context we need for retrieving the ID of the current record, the service is required for communication with CRM-Webservice (IOrganizationService).
//Create the IWorkflowContext and the
//IOrganizationService for communication with CRM IWorkflowContext workflowContext = context.GetExtension<IWorkflowContext>(); IOrganizationServiceFactory serviceFactory = context.GetExtension<IOrganizationServiceFactory>(); IOrganizationService service = serviceFactory.CreateOrganizationService(workflowContext.UserId);
8. Business Logic
Now we have to add the logic which does the following.
- Read Text from "TaskSubject" Input Parameter
string newSubject = TaskSubject.Get<string>(context);
- Create a new task object
Entity newTask = new Entity("task");
- Set subject of the task to TaskSubject value
newTask["subject"] = newSubject;
- Set regardingobjectid of task to PrimaryEntityId of context (this is the id of the account).
newTask["regardingobjectid"] = new EntityReference("account", workflowContext.PrimaryEntityId)
- Create the new task in CRM
Guid taskId = service.Create(newTask);
9. Sign the assembly
First of all, go to project settings and sign your assembly.
Right Click the Project -> Properties -> Signing -> Sign the assembly -> New -> Enter Key file name "testkey" -> no password (for testing purposes).
Signing |
10. Compile and deploy the assembly
Rebuild your solution and deploy the assembly to the CRM System. This can be done by the PluginRegistrationTool of microsoft. You can find this as part of the current CRM-SDK. You have to compile it by your own. Choose the following settings.
Deployment |
11. Create a Workflow with the new step
Create a default Workflow in CRM. Choose the following settings.
Workflow basic configuration |
Add the new step |
Push the SetProperties-Button of the step and select a default name for the Input Parameter (TaskSubject). Because the Owner of the new task should see which account was created, we add the name of the account as input parameter.
Set input parameter |
Now save and don't forget to activate the Workflow.
12. Testing
Create a new Account in CRM.
Create Account |
Processing the workflow can take some seconds. Open the Activity List of the account and select Filter on "All". You should see our created task. The subject contains some static text and the name of the created account.
Activity View |
-------------------13. Code Overview
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Activities; using Microsoft.Xrm.Sdk; using Microsoft.Xrm.Sdk.Workflow; namespace MK.CRM2011.CustomWorkflowStep { public class CreateTask : CodeActivity { #region Input Properties [Input("Task Subject")] [Default("Empty Subject" )] public InArgument<string> TaskSubject { get; set; } #endregion protected override void Execute(CodeActivityContext context) { //Create the IWorkflowContext and the //IOrganizationService for communication with CRM IWorkflowContext workflowContext = context.GetExtension<IWorkflowContext>(); IOrganizationServiceFactory serviceFactory = context.GetExtension<IOrganizationServiceFactory>(); IOrganizationService service = serviceFactory.CreateOrganizationService(workflowContext.UserId); //Retrieve data from InputParameter string newSubject = TaskSubject.Get<string>(context); //Create the new task object (in memory) Entity newTask = new Entity("task"); newTask["subject"] = newSubject; newTask["regardingobjectid"] = new EntityReference("account", workflowContext.PrimaryEntityId); //Create task in CRM Guid taskId = service.Create(newTask); } } }
Ok, this is more detailed than i have planned. Hope it helps!
Regards,
Markus
Hi markus,
ReplyDeleteVery nice post!
Just one precision: in pluginregistrationttool, when selecting the workflow class, you can define a group name and a title instead of the predefined guid. Then, in the web designer, these group and title will be used instead of class name.
Hi Tanguy,
ReplyDeletethanks for your hint! :)
There are many features missing in this post (Output-Parameters, Details about the Context...). It should only be a "Getting Started" Guide.
Cheers,
Markus
Hi markus,
DeleteI am new and crm and not much experience in .net as well. I am working in custom workflow. while changing in status I need to do some calculation from the amount filed. I think it can be from OutPut-parameteres. So can you post your nice post like this.
Thanks
Bishnu
Could you explain your usecase a little bit?
DeleteVery good Explanation!!
ReplyDeleteAppreciate you efforts
Good Job that makes me wondering if there is anyway to modify it to work with any type of entities, not just accounts ?
ReplyDeleteHello Mostafa,
ReplyDeletethis is possible. You can access the context parameters. In your usecase the PrimaryEntityName.
http://msdn.microsoft.com/en-us/library/bb930338.aspx
Regards
Markus
Excellent Post!!!
ReplyDeleteSuperb Post !
ReplyDeleteHow to debug the workflow activity in CRM 2011 online? I know only for plugins, the registration provides only the way to debug.
ReplyDeleteVery much helpful for beginners.Thanks a lot.
ReplyDeleteawesome...SO I learned workflow activity ;) good buddy keep it up.
ReplyDeleteI have followed this tutorial end to end, using CRM 2013. But I am unable to create followup task automatically when an account is created? I am getting no error message but no task created either, kindly guide me if it is supported for dynamics crm 2013, if yes than what can be possibly wrong? using Visual Studio 2012, SDK version 6.0.0.1
ReplyDeleteSorry for my late response.
Delete@Breaking White
Solution works in 2015 so i think 2013 should work too. Please provide debug information to solve your issue.
Check following points:
- Correct version of SDK (dlls)
- Correct version of Framework (for CRM 2015 you should configure .NET Framework 4.5.2 in project properties)
- User current version of PluginRegistration Tool
- Sign your assembly
- Activate your Workflow
Best regards,
Markus
Markus,
ReplyDeleteI am working on a custom workflow that requires I interogate the write-in products of the invoice_details of a contacts invoices. I am having difficulty getting to the relationship that seems to be the only method of getting to these objects. Could you assist with pseudo code or a simple example of getting to this entity relationship using the contact context?
Steve
Markus,
ReplyDeleteSorry I forgot to add that this is for CRM 2011 but would like to try and make it a working solution going into 2013 also. So much of the information out there is either wrong or outdated for CRM. I recently spent a day and a half trying to update a web resource before finding that most of the examples failed to mention that the behavior had changed and their code would only work on html resources.
Steve
Sorry for my late response.
Delete@WrqnHrd
1. Retrieve Invoices for Contact (Relation 1:n - invoice_customer_contacts)
2. Retrieve InvoiceProducts for Invoices (Relation 1:n - invoice_details)
You should consider using LinkedEntity to avoid to many API requests and appropriate filter criteria (e.g. isproductoverridden)
Best regards,
Markus
Hi Guys, I am new in creating custom workflow in crm. I created workflow in VS and added the assembly into my CRM using Plugin Registration Tool but it is not showing in Plugin Assemblies under customization. Please give me some suggestion.
ReplyDeleteWaiting for the reply..Thanks
Jatin
Hi Jatin,
Deletedid you execute "Step 11" of this guide?
Which CRM-Version are you using? This guide was created based on CRM2011 but the basic structure should be the same,also in current versions, as far as i know also in 2016.
BR,
Markus