Microsoft Dynamics CRM 2011

Microsoft Dynamics CRM 2011

Sunday, August 18, 2013

Step-By-Step call a custom child workflow from a parent workflow over a dynamic Marketing List


by Carmel Schvartzman
  1. In this walkthrough we will see  How-to  develop a workflow which receives a DYNAMIC Marketing List and calls another custom workflow on the list's records. The DYNAMIC Marketing List is  a CRM 2011 feature that enables to fetch an on-the-fly list, not know in advance, of items according to some pre-determined requirements defined in a QUERY. For the sake of the sample, let's suppose that we have a custom CHILD workflow which clones data from an existing account to a new account, and we want both use this workflow on its own, and as a child workflow called by a PARENT workflow over a DYNAMIC Marketing List.
  2. First, start VISUAL STUDIO 2010 and create a WF project:1
  3. Next, delete the XAML file,
    2
    and create a CS. new class "DynamicMarketingListActivity" (SHIFT-ALT-C):3
  4. IMPORTANT: the class we just created IS NOT PUBLIC: we ough to declare it PUBLIC , elsewhere we'll get a SETUP ERROR at the time of deploying the plug-in to the CRM server:4
  5. We want to create a custom workflow, so we'll inherit from the base class CodeActivity: let's also add the System.Activities using directive:5
  6. The CodeActivity class is ABSTRACT, that means we'll have to implement in this case  the method "Execute":
    6
  7. Delete the "throw new" code. We'll write code of our own:
    7
  8. Now we'll write our custom activity. First of all, we'll need a CONTEXT that allows us to communicate with the CRM webservices. We'll need the CRM's  IOrganizationService, an interface that provides programmatic access to the metadata and data for our CRM Organization. In order to do that, we'll use the IOrganizationServiceFactory,  as stated at the MSDN documentation:  we'll create it using the  GetExtension<......>() method of the context. So write the following code:
    8
    However, if after writing the code, you BUILD the solution (F6), you'll face the "Are you missing a using directive or an assembly reference":
    9
    That's because we have to tell VS where the CRM specific interfaces that we just  wrote are. They are included in the following two assemblies : Microsoft.Xrm.Sdk.dll and Microsoft.Xrm.Workflow.dll. So add  the references to both DLLs (you can find them in the CRM2011 SDK Bin folder : download from here):
    10
    Again, while REBUILDING the solution, we get an error message: "...XRM does not exists...":
    11 That's because the target framework default option for a new workflow project is "CLIENT PROFILE": go to PROPERTIES and under "APPLICATION" change the target framework to ".NET Framework 4":
    12
    Rebuild the solution.
  9. Now we need to sign the assembly  using the "signing" tab of the project's  properties:27
  10. Using the IOrganizationServiceFactory, we'll create an instance of the IOrganizationService:
    13
  11. We'll also need to call another CRM web service, which will allow us to get accurate info about what happened in case of error: it's called ITracingService and again we'll create it using the  GetExtension<ITracingService>() method of the context:14Rebuild the solution. We have all we need to use our workflow.
  12. Now let's add some METADATA: the CRM environment uses this metadata at runtime to link our code to the workflow engine. This way we can also declare a parameter as REQUIRED and even set DEFAULT values just in case the user do not provide them.
    Usually, plug-ins need INPUT and OUTPUT parameters. We'll add both of them to our plug-in. For the INPUT parameter, let's add a public InArgument<string> automatic property:15Here we declare our "PluginInput" property to be an InArgument of type string, that the CRM workflow engine will know by the name of "PluginInput" , and which its default value is set as "Default Plugin Input". The parameter name will appear in the workflow form assistant, so that the users can map the attribute as anWe can also state that the input parameter will be required, using the following attribute:
    [RequiredArgument]Output parameters are declared the same way as input parameters:16
    And in the code, we use this properties the following way:17That means, we GET the input string parameter calling the Get<some type >(context) method of the INPUT property, and SET the OUTPUT parameter using the Set(context, <some object>) method of the output parameter.
  13. OK, we have our workflow built. This plug-in will receives a Marketing List and do some custom action over each of the list items. But since those items aren't there yet, because we're talking of a DYNAMIC list, HOW DO WE GET THE RECORDS TO UPDATE?All we need to do is get the GUID of the DYNAMIC LIST. With this ID we'll get the FETCH XML declared at the graphic CRM UI when the Marketing List was created:18Also, we get the Entity type we receives from the CRM engine: as we declared when creating the workflow, the Entity will be a "Marketing List":19Accordingly we get a Marketing List as the 'PRIMARY ENTITY'. We'll get this primary entity GUID and use it to retrieve the dynamic QUERY : this query is an object of type FETCHEXPRESSION: In order to create an instance of FetchExpression, we need to add a using directive to our workflow:20
    The same goes for the class ColumnSet: we need to add a reference to the DLL System.Runtime.Serialization:22
    We'll get this primary entity GUID and use it to retrieve the dynamic QUERY:23After we got the guid of the dynamic Marketing list, we are ready to get the QUERY that defines who are the MEMBERS of the list:24
    A dynamic Marketing list has an "query" attribute , which is not present in case of a STATIC Marketing list. Using the organization web service's  Retrieve() method, we fetch the "query" attribute:25
    Accordingly, we instantiate a FetchExpression object with the "query" string. Next, we just fetch the members of the list using the RetrieveMultiple() method.Now that we have the guids of the dynamic list members, we can retrieve them and apply the changes we want to each record:26 
  14. Now we can call the CHILD workflow , and apply its functionality over each of the dynamic Marketing List items. Refer to this BLOG to develop the CHILD workflow. We already have the NAME of the CHILD workflow in an INPUT parameter:1
  15. We create the conditions for the query to get the required workflow based on its NAME:2
  16. In order to get the CHILD workflow we define the conditions for the query:3
  17. We get the required workflow (notice that we used RetrieveMultiple() method, so we could have applied this approach over MORE THAN ONE CHILD WORKFLOW):4And run all the workflows fetched for each of the items in the Marketing List:5
  18. Finally , we run the CHILD workflow on the whole dynamic list:6
  19. The next stage is to deploy the DLL wokflow to the Crm server. We need the Plugin Registration Tool that comes with the CRM 2011 SDK, therefore copy that GUI to some directory at the CRM server:000I copied the app to the C:\Plugintool directory.
  1. Double click the PluginRegistration tool:1
  2. Now, press CONNECT to discover your CRM webservice:2If your CRM server hosts more than one Organization, select the one to wich you developed your plug-in
  3. Check that the selected CRMService URL is the one you want to deploy your plug-in
  4. Now press "REGISTER" and next "REGISTER NEW ASSEMBLY":4
Now select the assembly you want to deploy:
6
It's strongly recommended that you set up the ISOLATION MODE to "SANDBOX": the SANDBOX feature gets code executed in an isolated and trusted environment. In the case your plug-in consumes too much server resources, SANDBOX mode will remove it from the event pipeline where you registered  your code. Also, the same will be done in case of continuous failing.
IMPORTANT: the assembly can be stored in the DISK or in the DATABASE. At the development stage, it's recommended you store it at the DISK, because there you can also copy the .PDB file used for debugging purposes. Later, when you finished testing your code, YOU MUST DEPLOY IT TO THE DATABASE!! Why? Some reasons are:
  • The assemblies registered on database can be included in a solution hosted in sandbox or in CRM ONLINE.
  • If your CRM server is load balancing, storing the DLLs in DATABASE will allow CRM to automize the process of updating and deploying changes.
  •  There's no need to restart the CRM servicesmaking IISRESET on the applicationpool because the update of an assembly will take effect automatically after you press the UPDATE button.7So instead of selecting "DATABASE" select the option "DISK" for now.
 That's all
Happy programming    :-)

כרמל שוורצמן

No comments:

Post a Comment