Saturday, April 25, 2015

Step by step plugin tutorial using Developer's Toolkit

I was looking at developer’s toolkit shipped with CRM2011 SDK. There are few blogs out there on how to create plugins using developer's tool kit. Most of the blogs (more or less) are the copy of  “developer toolkit user's guide” that comes with the toolkit.

Here is link to Sam’s blog that explains each project in developer’s toolkit. MSDN blog also have similar contents. In this blog I am going to create a sample plugin using developer’s toolkit. It is a copy of one of my earlier blog(Step by step plugin tutorial for CRM 2011) .

Install the developer’s toolkit.The Developer toolkit for Microsoft Dynamics CRM 2011 was released as part of UR5 SDK release and is available for download here.


  1. Create a new solution in CRM2011. I named my solution “CRM Plugin Solution”. This is optional but I would recommend you do that.
  2. Open Visual Studio 2010. Select File—New –Project. It will display new project templates dialog as shown in the screen sheet belowp1
  3. Select “Dynamics CRM 2011 Package” project. This is also optional. You can go ahead and select “Dynamics CRM 2011 Plugin Library”, But then you cannot deploy the plugin straight from the Visual Studio. You have to use Plugin Registration tool to register the plugin. Enter the name of project/solution.
  4. VS studio will display following dialog.Enter you CRM 2011 server details.Select the solution name we created in step 1 and click okp2
  5. Now right click on the solution and add “Dynamics CRM 2011 Plugin Library” project to the solution. The plugin project will already have a plugin.cs file.
  6. Now sign the plugin assembly. Right click on Plugin Project and select properties. Select Signing tab from left navigation of project property page. On the Signing tab, select the Sign the assembly check box and set the strong name key file of your choice.At a minimum, you must specify a new key file name. Do not protect your key file by using a password.
  7. If You cannot see the “CRM Explorer” window on the upper left side of the VS studio, click on View menu and select “CRM Explorer”. p3
  8. Now expand “Entities” Node. Right Click the entity on want to create the plugin for and select “Create Plugin”.p4
  9. It will display a following screen.It is equivalent to “Create Step” screen in plugin registration tool. The dialog will pick up the name of the entity and other information. Choose the message and the pipeline stage. You can also change of the Class attribute. Press Ok.pn5
  10. It will create a .cs file with name mentioned in “Class” attribute in screen shot above. Double click on the class file(PostAccountCreate) and scroll down to following lines of code. You write your business logic here.
    protected void ExecutePostAccountCreate(LocalPluginContext localContext)
    {
       if (localContext == null)
       {
           throw new ArgumentNullException("localContext");
       }
       // TODO: Implement your custom Plug-in business logic.
    }
  11. The first thing you will do is to get the plugin context, CRMService instance and TracingService instance using localContext passed to the function. All these objects are defined in the built in plugin.cs class.
    IPluginExecutionContext context = localContext.PluginExecutionContext;
    IOrganizationService service = localContext.OrganizationService;
    //ITracingService tracingService = localContext.TracingService;
  12. Here is code. It will check if the “account number” is null or empty and  create a task for a user to enter the account number.
    protected void ExecutePostAccountCreate(LocalPluginContext localContext)
    {
        if (localContext == null)
        {
            throw new ArgumentNullException("localContext");
        }
    
        // TODO: Implement your custom Plug-in business logic.
        // Obtain the execution context from the service provider.
        IPluginExecutionContext context = localContext.PluginExecutionContext;
        IOrganizationService service = localContext.OrganizationService;
        //ITracingService tracingService = localContext.TracingService;
    
    
        // The InputParameters collection contains all the data passed in the message request.
        if (context.InputParameters.Contains("Target") &&
        context.InputParameters["Target"] is Entity)
        {
            // Obtain the target entity from the input parmameters.
            Entity entity = (Entity)context.InputParameters["Target"];
                    
            //EntityReference pp = entity.GetAttributeValue("primarycontactid");
            //tracingService.Trace(pp.LogicalName);
                   
                    
            try
            {
                //check if the account number exist
    
                if (entity.Attributes.Contains("accountnumber") == false)
                {
    
                    //create a task
                    Entity task = new Entity("task");
                    task["subject"] = "Account number is missing";
                    task["regardingobjectid"] = new EntityReference("account", new Guid(context.OutputParameters["id"].ToString()));
    
                    //adding attribute using the add function
                    // task["description"] = "Account number is missng for the following account. Please enter the account number";
                    task.Attributes.Add("description", "Account number is missng for the following account. Please enter the account number");
                            
                    // Create the task in Microsoft Dynamics CRM.
                    service.Create(task);
    
    
                }
            }
    
            catch (FaultException ex)
            {
                throw new InvalidPluginExecutionException("An error occurred in the plug-in.", ex);
            }
    
        }
    
    }
    I also left few commented lines in the code to show “How to use tracingservice to write in trace log”. You will able to see the trace only if there is an error in the plugin.
  13. Now right click on CRM Package project we created in step 2 and select deploy. It will register the plugin assembly as well as step for the plugin. Click on CRM Explorer to check the deployed plugin as shown in the following screen shot.pn1
  14. Create a new account and test the plugin.

Creating a Simple Plug-in

This walkthrough demonstrates how to create a plug-in for a Microsoft Dynamics CRM 4.0 server and register it using the Plug-in Registration Tool. When you run the sample plug-in, it creates a follow up task activity after a new account is created. The task reminds the account owner to follow up with the account customer in 7 days.
During this walkthrough you will learn how to do the following:
  • Use Visual Studio to create a new Microsoft Dynamics CRM 4.0 plug-in solution.
  • Add required Web service references to the project.
  • Digitally sign the plug-in.
  • Author a Visual C# plug-in class that will create an activity whenever a new account is created.
  • Use the Plug-in Registration Tool to register the plug-in with the Microsoft Dynamics CRM server.
  • Test the plug-in.

Prerequisites

To complete this walkthrough, you will need the following:
  • Visual Studio 2005 or Visual Studio 2008.
  • A pre-built version of the Plug-in Registration tool.
  • A Microsoft Dynamics CRM SDK installation.
  • Network access to a Microsoft Dynamics CRM 4.0 server.
  • A Microsoft Dynamics CRM system account with either the System Administrator or System Customizer security role, which is also a member of the Deployment Administrators group in Deployment Manager.
You can build the sample directly on the Microsoft Dynamics CRM server assuming all of the prerequisites are met. The Plug-in Registration Tool can be built by following the related walkthrough.

Step 1: Creating a Visual Studio Solution

First you will create a new Visual Studio project and create a Visual C# class file.
To create a Visual Studio solution
  1. In Microsoft Visual Studio, on the File menu, point to New, and then click Project to open the New Project dialog box.
  2. In the Project types pane, select Visual C#.
  3. In the Templates pane, click Class Library.
  4. Type a name for your project and then click OK. Use the name AccountCreatePlugin.
  5. In Solution Explorer, rename the Class1.cs file to Plugin.cs.
  6. When you are prompted to rename all references to this file, click Yes.

Step 2: Adding References

You need to add references to the required Microsoft Dynamics CRM assemblies.
To add the necessary assembly references
  1. In Solution Explorer, right-click the References folder and select Add Reference.
  2. Click the Browse tab and navigate to the location of your Microsoft Dynamics CRM DLLs. This may be in the SDK\Bin folder or on your server in the GAC folder. The GAC folder is located on the Microsoft Dynamics CRM server at <crm-root>\Server\i386\GAC.
  3. Select both Microsoft.Crm.Sdk.dll and Microsoft.Crm.SdkTypeProxy.dll and click OK.
  4. In Solution Explorer, right-click the References folder and select Add Reference.
  5. Click the .NET tab, select System.Web.Services, and click OK.

Step 3: Sign your Plug-in

  1. Right-click the AccountCreatePlugin project in Solution Explorer and click Properties.
  2. Create a new strong key file by clicking the Signing tab, select the Sign the assembly check box and select <New…> in the drop down list.
  3. Type AccountCreateStrongKey and clear the Protect my key file with a password check box. Click OK.
  4. Save your changes by clicking the Save All button.

Step 4: Write your Plug-in Code

This plug-in will create a new task to send an e-mail to the new customer after an account is created. It also shows you an example of throwing plug-in exceptions to the client, which displays an exception message to the user. The complete project source code can be found at SDK\Walkthroughs\Plugin\CS\AccountCreate.
To add required namespaces
  1. Open the Plugin.cs file and add Microsoft.Crm.Sdk and Microsoft.Crm.SdkTypeProxy to the imported namespaces, as shown in the following code example.
  2. Add a semicolon and the word IPlugin after the class declaration.
Your code should now look like this:
using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Crm.Sdk;
using Microsoft.Crm.SdkTypeProxy;

namespace Microsoft.Crm.Sdk.Walkthrough
{
    public class AccountCreateHandler : IPlugin
    {
    }
}
To Add the Execute method
The execute method is called when your plug-in is triggered.
  1. Add the Execute method to the CreateAccountHandler class using the following code.
public void Execute(IPluginExecutionContext context)
{
   DynamicEntity entity = null;

   // Check if the input parameters property bag contains a target
   // of the create operation and that target is of type DynamicEntity.
   if (context.InputParameters.Properties.Contains("Target") &&
      context.InputParameters.Properties["Target"] is DynamicEntity)
   {
      // Obtain the target business entity from the input parmameters.
      entity = (DynamicEntity)context.InputParameters.Properties["Target"];

      // Verify that the entity represents an account.
      if (entity.Name != EntityName.account.ToString()) { return; }
   }
   else
   {
      return;
   }

   try
   {
      // Create a task activity to follow up with the account customer in 7 days. 
      DynamicEntity followup = new DynamicEntity();
      followup.Name = EntityName.task.ToString();

      followup.Properties = new PropertyCollection();
      followup.Properties.Add(new StringProperty("subject", "Send e-mail to the new customer."));
      followup.Properties.Add(new StringProperty("description", 
         "Follow up with the customer. Check if there are any new issues that need resolution."));

      followup.Properties.Add(new CrmDateTimeProperty("scheduledstart", 
         CrmTypes.CreateCrmDateTimeFromUniversal(DateTime.Now.AddDays(7))));
      followup.Properties.Add(new CrmDateTimeProperty("scheduledend", 
         CrmTypes.CreateCrmDateTimeFromUniversal(DateTime.Now.AddDays(7))));

      followup.Properties.Add(new StringProperty("category",
         context.PrimaryEntityName));
 
      // Refer to the new account in the task activity.
      if (context.OutputParameters.Properties.Contains("id"))
      {
         Lookup lookup = new Lookup();
         lookup.Value = new Guid(context.OutputParameters.Properties["id"].ToString());
         lookup.type = EntityName.account.ToString();

         followup.Properties.Add(
            new LookupProperty("regardingobjectid", lookup));
      }

      TargetCreateDynamic targetCreate = new TargetCreateDynamic();
      targetCreate.Entity = followup;

      // Create the request object.
      CreateRequest create = new CreateRequest();
      create.Target = targetCreate;

      // Execute the request.
      ICrmService service = context.CreateCrmService(true);
      CreateResponse created = (CreateResponse)service.Execute(create);      
   }
   catch (System.Web.Services.Protocols.SoapException ex)
   {
      throw new InvalidPluginExecutionException(
         "An error occurred in the AccountCreateHandler plug-in.", ex);
   }
}
  1. Compile the project by clicking Build, and then Build Solution.
To deploy the plug-in on your server
If you intend to debug your plug-in, follow these steps to place a copy of the plug-in assembly at the correct location on the Microsoft Dynamics CRM server's disk.
  1. On your development machine, navigate to the bin\Debug folder to find your newly compiled plug-in assembly. This may be in the bin\Release folder depending on your default project settings. Check your project's Build Output path for the correct location.
  2. Copy the compiled plug-in assembly AccountCreatePlugin.dll to the <crmroot>\Server\bin\assembly folder of your Microsoft Dynamics CRM server.
Your plug-in code is now complete.

Step 5: Register your Online Plug-in with the PluginRegistration tool

The plug-in registration tool enables you to register your plug-in with a specific event in Microsoft Dynamics CRM. When that particular event fires, your plug-in will execute. This walkthrough uses database deployment of the plug-in for simplicity.
Note:  There is a security restriction that allows only privileged users to register plug-ins. The system user account under which the plug-in is being registered must exist in theDeployment Administrators group in Deployment Manager. The account must also have either the System Administrator or System Customizer role in Microsoft Dynamics CRM.
To register your plug-in
  1. In Visual Studio, click Tools, and then click CRM Plug-in Registration Tool to run the plug-in registration tool. If you have not added the Plug-in Registration Tool to the Visual Studio Tools menu, then manually run the tool.
    Plug-in Registration Tool
  2. In the Connections panel, enter a descriptive label for the connection. Fill in the other fields as appropriate for your Microsoft Dynamics CRM 4.0 server.
  3. Click Connect. A connection to the server is established and a list of available organizations for the specified system account is displayed.
  4. Double-click the desired organization in the connections list. The list of all assemblies, steps, and plug-ins currently registered for the target organization is displayed. Clicking any assembly, step, or plug-in displays a list of information about that selected item.
    Assemblies and plug-ins registered with an organization
  5. Select Register, and then select Register New Assembly.
  6. In the Register New Plugin form, click the ellipsis button (…) and navigate to the location of your plug-in assembly.
  7. Select the assembly.
    Note If an exception dialog box is displayed containing a "System.IO.FileNotFoundException: Could not load file or assembly 'Microsoft.Crm.Sdk'" message, the plug-in registration tool cannot find the Microsoft.Crm.Sdk.dll assembly that it requires. Copy that assembly from the SDK\Bin folder to the folder containing the tool's executable file and try again.
  8. Verify that the Select All and Database options are checked. Click Register Selected Plugins.
    Register New Plugin form
  9. The Registration Log will display the status of the plug-in registration process. Click OK to dismiss the Registered Plugins dialog box.
The assembly and plug-ins are now registered.
To Register a Step for your plug-in
This next procedure registers a step that defines the conditions under which the plug-in is to execute.
  1. Select the plug-in (AccountCreateHandler) in the tree view.
  2. Select Register, and then click Register New Step.
  3. In the Register New Step dialog, enter the information as shown in the figure below and select Register New Step.
    Registering a step
You have just created a synchronous post-event plug-in that will run when an account is created while in online mode. For further information about using the tool, press the F1 key while the tool's window has focus to display online help. You can dismiss the plug-in registration tool at this point.
Note For this SDK release, pressing F1 in the PluginRegistration tool window displays this walkthrough in a browser window. Future SDK releases will include online Help for the PluginRegistration tool.

Test the Plug-in

To test the plug-in you can create an account. This will trigger the plug-in code to run.
  1. Open Internet Explorer, navigate to your Microsoft Dynamics CRM server, click New, and then click Account.
  2. Enter your Account Name and click Save.
  3. After Microsoft Dynamics CRM finishes saving, click the Activities link under Details.
  4. You should see the new activity created for this account with the subject Send e-mail to the new customer.

Comparison: Using Workflows vs. JavaScript vs. Plugins in Dynamics CRM

There are three ways to automate actions in Microsoft Dynamics CRM: workflows, JavaScript, or plugins. In this blog we will discuss the difference between them and how to choose which option is appropriate for your task.
  • Workflows can perform tasks such as updating data or sending email. They are triggered by saving records, creating records, or changing specific fields on a form, and once triggered, they run on their own in the background. As you can see in the example ofHow to Assign a Territory to a Lead in Dynamics CRM, you can even look up data in another entity.
  • JavaScript runs on the screen while the user is using a form. JavaScript is triggered by events within the page, updating a field or saving, and it is commonly used to hide or show different fields on the forms. You can also, for instance, Populate a CRM 2011 Lookup or PartyList Field Using JavaScript by having a lookup automatically linked to the record based on what is entered in another data field.
  • Plugins are separate .Net programs that respond to saving records or creating records. They can be set to run immediately on the screen or on their own behind the scenes depending on the need. Plugins are often used to do advanced tasks because they can be programmed to do anything within the limits of .Net and the SDK.
So, how do you decide using workflows vs. JavaScript vs. plugins when so there are many ways to update the information you want? There are a few factors to help the decision:
  • Does the user need to see the results before moving on?
    This is known as synchronous processing. If the user needs to see the change on the screen, it should be done with a plugin or JavaScript and not a workflow. A workflow can take several seconds or minutes to process and should not be used in a process where users must wait for it to complete before they can continue.
  • Who will the maintenance fall to? Users or IT?
    Workflows are much easier for business users to modify than JavaScript or plugins, which require some code. JavaScript or plugins should be avoided in processes that may change frequently.
  • Does it need to be run on demand or only responding to system events?
    Workflows can easily run on demand (a.k.a manual workflow), but JavaScript and plug-ins require custom buttons in order to run on demand.
  • What is the relationship between the primary record and the records that need to be updated?
    Workflows can update records with a N:1 relationship (a lookup on the record), but they cannot run on 1:N relationships (those on the left navigation or in a sub-grid). So using a workflow, you cannot automatically update all the contact addresses when the parent account address changes. But this can be done with a plug-in.
Here is a table that will help you identified the difference between workflows, JavaScript, and plugins for use within Microsoft CRM.
WorkflowJavaScriptPlugin
SynchronousAsynchronousSynchronousEither
Can Get External DataNoYesYes
MaintenanceBusiness UsersProgrammersProgrammers
Can Run AsUserUserCRM System
On DemandYesNoNo
Nested Child ProcessYesNoYes
Executed After SavingAfterBeforeAfter
TriggersCreate, Field Change, Status Change, Assign to Owner, On DemandField change or Form LoadCreate, Field Change, Status Change, Assign to Owner, Delete
For different cases when the user may need to walk through a process and make decisions, dialogs may instead be the right answer. To help make that decision, our blog on CRM Workflows and Dialogs: What’s the Difference?  provides great comparison.

CRM 2011 Plugin to Create and Send Email on Opportunity update.

Today i’ll start with the plugin which I often use and is considered very commonly used for various projects.
One of my client had a requirement in which, when the Opportunity Probabilityexceeds 90% then the related Account must be send an email regarding the requirement of Opportunity Products. Accounts were Vendors in my scenario.
Since I require multiple lines of Opportunity Products in my email, so it was not possible through workflow as we don’t have any option for selecting Opportunity Products.
  • I started with creating the Class Library Project in Visual Studio 2010. Remember to select .NET Framework 4.

  • Added the References from CRM-SDK and .Net

  • Then used the following directives in the following Plugin

  • The main code used in this plugin is to create Email along with fieldvalues “TO” and “FROM” using ‘partyId’ attribute from ‘activityparty’ entity.

  • And the code used to send mail using service requests.

If the Email router is set foe sending mails or configured through outlook then the following created email will be send.
  • The final code will look something like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Discovery;
using Microsoft.Crm.Sdk.Messages;
using System.ServiceModel;

namespace EmailOppr
{
    public class Oppr:IPlugin
    {
        IOrganizationService service;
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
            {
                Entity entity = (Entity)context.InputParameters["Target"];

                try
                {
                    if (entity.LogicalName == "opportunity")
                    {
                        if (entity.Contains("closeprobability"))
                        {
                            if ((int)entity.Attributes["closeprobability"] >= 90)
                            {
                                Guid opprid = entity.Id;
                                Entity Opprt = service.Retrieve("opportunity", opprid, new Microsoft.Xrm.Sdk.Query.ColumnSet(true));
                                Guid ownerId = ((EntityReference)Opprt.Attributes["ownerid"]).Id;
                                Entity owner = service.Retrieve("systemuser", ownerId, new Microsoft.Xrm.Sdk.Query.ColumnSet(true));
                                String ownerName = (String)owner.Attributes["fullname"];
                                Guid custId = ((EntityReference)Opprt.Attributes["customerid"]).Id;
                                String oppName = (String)Opprt.Attributes["name"];
                                int prob = (int)Opprt.Attributes["closeprobability"];
                                DateTime dt = (DateTime)Opprt.Attributes["estimatedclosedate"];
                                String subject = "An Opportunity has been created by " + ownerName + " , estimated to be closed by " + dt + " with the Probability of " + prob + ".<br><br>";
                                subject = subject + "The following profiles are required in the Opportunity.<br><br>";

                                var fetchXml = "<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>";
                                fetchXml += "<entity name='opportunityproduct'>";
                                fetchXml += "<attribute name='opportunityproductid' />";
                                fetchXml += "<attribute name='productid' />";
                                fetchXml += "<attribute name='quantity' />";
                                fetchXml += "<filter type='and'>";
                                fetchXml += "<condition attribute='opportunityid' operator='eq' value='" + opprid + "' />";
                                fetchXml += "</filter>";
                                fetchXml += "</entity>";
                                fetchXml += "</fetch>";

                                var result = service.RetrieveMultiple(new FetchExpression(fetchXml));
                                if (result.Entities.Count > 0)
                                {
                                    subject = subject + "[Opportunity Product Name] [Quantity]<br>";

                                    for (int i = 0; i < result.Entities.Count; i++)
                                    {

                                        Guid opprPd = ((Guid)result.Entities[i].Attributes["opportunityproductid"]);
                                        Guid opprPdId = ((EntityReference)result.Entities[i].Attributes["productid"]).Id;
                                        Entity opppd = service.Retrieve("product", opprPdId, new Microsoft.Xrm.Sdk.Query.ColumnSet(true));
                                        var opprPdName = opppd.Attributes["name"];
                                        Decimal qty = ((Decimal)result.Entities[i].Attributes["quantity"]);
                                        int q = (int)Math.Round(qty);
                                        subject = subject + opprPdName + "--------" + q + "<br>";
                                    }
                                }
                                subject = subject + "<br>Kindly check/ensure availability of the required Products by " + dt + "<br><br>";

                                Entity fromParty = new Entity("activityparty");
                                fromParty["partyid"] = new EntityReference("systemuser", ownerId);
                                Entity toParty = new Entity("activityparty");
                                toParty["partyid"] = new EntityReference("account", custId);

                                Entity Email = new Entity("email");
                                Email.Attributes["from"] = new Entity[] { fromParty };
                                Email.Attributes["to"] = new Entity[] { toParty };
                                Email.Attributes["subject"] = "New Opportunity Requirements";
                                Email.Attributes["regardingobjectid"] = new EntityReference("opportunity", opprid);
                                Email.Attributes["description"] = subject;
                                Email.Attributes["ownerid"] = new EntityReference("systemuser", ownerId);
                                Guid EmailId = service.Create(Email);

                                SendEmailRequest req = new SendEmailRequest();
                                req.EmailId = EmailId;
                                req.IssueSend = true;
                                req.TrackingToken = "";

                                SendEmailResponse res = (SendEmailResponse)service.Execute(req);

                            }

                        }

                    }

                }

                catch (Exception ex)
                {
                    throw new InvalidPluginExecutionException("An error occured in the Plugin.", ex);
                }

            }

        }

    }
}

Hope this will help someone to write the plugin for creating and sending email in CRM very easily now.

How to Deploy your API as a web app or API app

  Before you can call your custom API from a logic app workflow, deploy your API as a web app or API app to Azure App Service. To make your ...