Showing posts with label Plug-in. Show all posts
Showing posts with label Plug-in. Show all posts

Jul 10, 2026

Call Azure Function directly from Plugin

This is step by step guide to explain how to call an Azure Function directly from a Plugin. When I say diretly, we do it in the code level. Other way of doing it is via  webhook. Lets jump in.

Scenario

When we create a Contact in Dataverse (model-driven app), I need to register the contact in the financial system which is best done using an Azure Function. So Approach is I am writing a pre create plugin for Contact which should call Azure Function to get the registration done. For this I am passing Contacts' full name to the function and expecting registration numbers to be passed back to Dataverse.

Create Azure Function

(Refer: https://www.youtube.com/watch?v=NhhmLt0YGqY&t=218s for step by step create of function app)

Here I retrieve name and returning registered number back. (Registering logic is not implemented but to ocestrate the action I am generating a randon number as registration number)

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace Sume4FunctionApp;

public class SumeFunction1
{
    private readonly ILogger<SumeFunction1> _logger;

    public SumeFunction1(ILogger<SumeFunction1> logger)
    {
        _logger = logger;
    }

    [Function("SumeFunction1")]
    public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest req)
    {
        // Retrieve Value
        string name = req.Query["name"];
        _logger.LogInformation($"Azure Function retrieved: {name}");

        // Logic
        // TODO - Register name in Financial System
        string FSID = Guid.NewGuid().ToString("N")[..10];

        // Retern Reg Number 
        return new OkObjectResult(FSID);
    }
}

After local testing, publish the Azure Function in Azure.


Now you can see the Azure Function under Function App.


Under Function Keys, you can retrieve the code which will be used in Plugin code.


Create Plugin

Now its time to call the Azure Function from Plugin code. Plugin is registered in Pre Create with below settings.


Notice how I compile the full name to be passed to function. Also how I retrieve the registration number and set to relevant field which will be populated (since we are in pre-stage just assigning is enough). Notice below steps;

  • We create a class with contact name as attribute to be passed to serializer
  • Serviceurl consist of below 3 components
    • Site Address (from Azure registration of Function App)
    • Function name (As we know from the code)
    • Code (read from function)
(Refer https://www.youtube.com/watch?v=KdGZ2Oo-4L8 to get explanation of steps)

if (execCtx.InputParameters.Contains("Target") && execCtx.InputParameters["Target"] is Entity target 
    && target.LogicalName == "contact")
{
    // Retrieve Name
    var contactDetail = new ContactDetail();
    string firstName = target.Contains("firstname") ? target.GetAttributeValue<string>("firstname") : string.Empty;
    string lastName = target.Contains("lastname") ? target.GetAttributeValue<string>("lastname") : string.Empty;
    ctx.Trace($"Contact Created - First Name: {firstName}, Last Name: {lastName}");
    contactDetail.name = firstName + " " + lastName;

    // Call Azure Function and Pass Contact Name
    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(ContactDetail));
    MemoryStream memoryStream = new MemoryStream();
    serializer.WriteObject(memoryStream, contactDetail);
    var jsonObject = Encoding.Default.GetString(memoryStream.ToArray());
    var webClient = new WebClient();
    webClient.Headers[HttpRequestHeader.ContentType] = "application/json";
    var code = "eAy22Izpd6kF5VTNHyArUu-I86MkbpVWOJYG9k8M-3L1AzFuGBtDxA==";
    var serviceUrl = "https://sume4functionapp.azurewebsites.net/api/SumeFunction1?code=" + code;
    string response = webClient.UploadString(serviceUrl, jsonObject);

    // Set the retrurned value (Financial system bymber) back to Contact record
    ctx.Trace($"response {response}");
    if (response != null)
        target.Attributes["so_financialsystemid"] = response;
    else
        throw new InvalidPluginExecutionException("Failed registering to FS via Azure Fuction");
}
        public class ContactDetail
        {
            public string name { get; set; }
        }

How it works

Now create Contacts and see how Financial System Id is being populated.


We can monitor this in two places of Azure.

1) Under Invocation of Azure Function as below.     


2) Under Log Stream of Function App. 


This is just a simple example, but hope this gives idea on what needs to do in each component and the sequance. 

Oct 18, 2023

Write a Custom API and call from Workflow

A Custom API is comparatively a new mechanism to create your own API in Dataverse. This is like a Action where you can package few steps to accomplish something. Advantage is you can then call from anywhere, either client side or server side etc. Obviously it can be called via a workflow or Cloud Flow too. Custom API is more flexible since coding is possible.

In this scenario we will see how to create a simple Custom API and call it from a classic workflow.

Writing a Custom API

1) We need to write a code which is pretty similar to a Plug-in. This is where we define our logic. In my case I am accepting a parameter string (i.e. DetailStr). Logic is to update Description (i.e. so_description) field of the custom entity called Office (i.e. so_office) with parameter value.

namespace AccAdmin.Plugins.CustomApi
{
    public class UpdateOffice : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context;
            IOrganizationServiceFactory factory;
            IOrganizationService service;

            try
            {
                context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
                ITracingService tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

                if (!context.InputParameters.Contains("DetailStr"))
                    throw new InvalidPluginExecutionException($"DetailStr not designated or Invalid..");

                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is EntityReference target)
                {
                    if (target.LogicalName != "so_office") return;
                    factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                    service = factory.CreateOrganizationService(context.UserId);

                    Entity entity = new Entity();
                    entity.LogicalName = "so_office";
                    entity.Id = context.PrimaryEntityId;
                    entity["so_description"] = (string)context.InputParameters["DetailStr"];
                    service.Update(entity);
                }
            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                throw e;
            }
            finally
            {
                service = null;
                factory = null;
                context = null;
            }
        }
    }
}

2) Once register the Plug-in, Custom API step will be visible as a step as below.


3) Now we start configuration. First add new Custom API to your solution.
    

Once save my Custom API was seen like below. It will have an unique name which we use to call this. We have set Enable for Workflow = Yes since we plan to call this from a Workflow. Most importantly, we have set our Plug-in step as the Plugin Type which is the linking point of our logic to the API. 


* Idea of all other settings

4) Same way we added the Custom API, we now add a parameter to our solution. Parameter is optional. In our case we plan to pass a string parameter.


Once created, parameter was seen as below. Nothing complex about this. You may notice we have selected Custom API we defined in previous step is set here for Custom API field.


Now we are done with our Custom API

Calling the Custom API from Workflow

Here I am create just an On Demand workflow so we can easily check it.

5) I am initiating a very simple Async workflow against Office entity.


6) Now I am adding a step of performing an Action. Interestingly, when I check the list of available Actions, I am seeing my Custom API by its unique name.


I see two parameters here. One is Target where we need to pass the current office record. Other one is what we define in Custom API which is needed for our logic.


7) Now its a matter of running our Workflow On Demand for any selected Office record.

* 
Unique Name: Unique name for the Custom API (Start with the publisher’s prefix which you have specified on your solution).

Name: Name for the Custom API.

Display Name: Display Name for this Custom API. In case we have enabled multi-languages on the System, we can provide a specific name for the custom API based on the language.

Description: This field is to store the description of this Custom API.

Binding Type: The Binding Type is the Options set field and could be set as Global, Entity, and Entity Collection. This should be specified as per your operation requirement (Entity Collection is only supported in Function Type Custom API).

Bound Entity Logical Name: This field is required when we select the Binding Type as Entity or Entity Collection. For the binding type as Global, it can be empty.

Is Function: It defines whether your custom API is a Function (this can only be called using Get Method) or Action (this can only be called using POST method).

Is Private: To define the Custom API is private or public (setting it private makes it accessible to only the owner of the Custom API).

Allowed Custom Processing Step Type: Allowed Custom Processing Step Type is another Option set field with options like None, Async Only, Sync, and Async. These options let you define whether the other plugins could be registered on this Custom Message and also lets you define its behavior. Like Async Only allows the plugins to register only onPost operation with Execution Mode to Asynchronous, and same for the Sync and Async option.

Execute Privilege Name: We can define the privilege that allows execution of the custom API. As per Microsoft docs, we can also create custom privileges and it is currently in development. We can use OOB privileges, for example,prvCreateLead, prvWriteLead, etc.

Plugin Type: Set the reference of your plugin for this API.

Warning
This example works fine. Anyway, there has been some deployment issues reported as per now we write this. So it is always advisable to call your Custom API from a Cloud Flow than a classic workflow. That worked perfectly.

Sep 16, 2022

Run an Update Plug-ins On Demand

One of the main questions asked in D365/ Dataverse jobs interviews, event today,  is differences of Plug-ins and Workflows. I used to say Plugins cannot be executed on Demand, but workflows. Its not wrong, but there is one indirect way of executing Update plugins on Demand. That's through bulk data updated.

Within the update options of the tool, you will find an option called Touch which really doesn't update the field, but it triggers other business logic bind to that operation.  (How it is done? I don't know!)


Anyway, this is an interesting option. Below are a couple of things to note;

1. This operation doesn't add anything to Audit.

2. One limitation is if there are many custom business logics (More than one Plug-ins, WFs), they all will get executed. No way of selecting what you want.

Oct 8, 2019

Passing Unsecure Configurations to a Plugin

We discussed how to pass Configurations to a JavaScript in a previous post. Click this to read that.

Lets see how we can pass Configurations to a Plug-in. In this scenarios, we'll see how we pass some Key Value pairs to a plug-in written for Lead entity.. We are going to store these values in a XML format within Unsecure Configuration section in registered plugin step.


This is the XML data format.

<leadConfig>
  <setting name="RegionCode" value="000X23AA55" />
  <setting name="IntegrationKey" value="1200-6753-0980-0901" />
</leadConfig>

Here is the Plug-in code that reads above values to be used in whatever the logic within the Plug-in. Interestingly, you will notice how we use a constructor class where configurations are being read within. Then we use GetUnsecureConfigValue() method to read each value in the XML.

using System;
using Microsoft.Xrm.Sdk;
using System.ServiceModel;
using System.Xml;

namespace TrialPlugin
{
    public class LeadPreCreate : IPlugin
    {
        private string UnsecureConfig { get; set; }

        public LeadPreCreate(string unsecureConfig, string secureConfig)
        {
            if (string.IsNullOrEmpty(unsecureConfig))
                throw new InvalidPluginExecutionException("Plugin Configuration missing.");
            UnsecureConfig = unsecureConfig;
        }

        public void Execute(IServiceProvider serviceProvider)
        {
            try
            {
                IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
                if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                {
                    Entity TargetEnt = (Entity)context.InputParameters["Target"];
                    if (TargetEnt.LogicalName != "lead")
                        return;

                    IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
                    IOrganizationService service = factory.CreateOrganizationService(context.UserId);

                    string regionCode = GetUnsecureConfigValue("RegionCode");
                    //string regionCode = Regex.Replace(GetUnsecureConfigValue("RegionCode"), @"\t|\n|\r", "").Replace(" ", String.Empty);
                    string integrationKey = GetUnsecureConfigValue("IntegrationKey");
                    //string regionCode = Regex.Replace(GetUnsecureConfigValue("IntegrationKey"), @"\t|\n|\r", "").Replace(" ", String.Empty);

                    throw new InvalidPluginExecutionException("RegionCode: " + regionCode + ", IntegrationKey: " + integrationKey);
                }
            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                throw e;
            }
        }
        
        private string GetUnsecureConfigValue(string key)
        {
            const string XPATH = "leadConfig/setting[@name='{0}']";
            string configVal = string.Empty;
            try
            {
                var xmlDoc = new XmlDocument();
                xmlDoc.LoadXml(UnsecureConfig);
                var node = xmlDoc.SelectSingleNode(string.Format(XPATH, key));
                return node == null ? configVal : node.Attributes["value"].Value;
            }
            catch
            {
                return configVal;
            }
        }
    }
}

Caution
If you have lengthy values in XML, there is a chance that line-breaks and spaces being added without your intention. In such cases, you may use some Regular Expression functions to omit those. For above code, I have read the configuration data in two ways and commented out one. If explained issue likely to be hitting your situation, you may use the commented line instead of what is used.

Anyway, this method of passing configuration data is can be used to keep parameters differently for different environments. What is important to know is solution imports are overriding these vales.

Aug 29, 2017

Retrieve Multiple - FetchXML with Alias

This is not a complex thing, but though of posting the code snippet since I found it important to explain a bit. When we need to retrieve data in related records few levels away from our current context, best method is using the FetchXML and use ReiveMultiple method. This will avoid us using many server calls to reach our entity.

Reason for writing this post is to explain one point. When we do complex FetchXMLs we definitely need to use Alias for linked entities. So reading the data in the code can be little different.

Check my example; I have an entity called job. Job got a lookup field to Project. Project got a lookup to User. Suppose we need to read the Mobile Number of the user (3 levels ahead) while we have only Job Id in hand, we use FetchXML as below;

internal static EntityCollection RetriveProjectAdminMobileByJobId(IOrganizationService service, Guid Id)
{
var fetchXml = string.Format(@"<fetch mapping='logical' output-format='xml-platform' version='1.0' distinct='false'>  
               <entity name='new_job'>
               <filter type='and'>
               <condition attribute='new_jobid' operator='eq' value='{0}' />
               </filter>
                   <link-entity name='new_project' to='new_jobprojectid' from='new_projectid' alias='PROJ' link-type='outer' visible='false'>
                       <link-entity name='systemuser' to='new_projectadmin' from='systemuserid' link-type='outer' alias ='PROJADMIN'>
                            <attribute name='mobilephone' />
                        </link-entity>
                   </link-entity>
               </entity>
               </fetch>", Id);
            return service.RetrieveMultiple(new FetchExpression(fetchXml));

Please notice, how I have used Aliases meaningfully. Now what we need to keep in mind is resulting fields would come with that Alias. In fact, mobile phone field is like PROJADMIN.mobilephone. Now check how I read it in C#;

var entityCollection = RetriveProjectAdminMobileByJobId(service, workOrderRef.Id);

if (entityCollection == null || entityCollection.Entities == null || entityCollection.Entities.Count <= 0)
    return;

if (entityCollection.Entities[0].Attributes.Contains("PROJADMIN.mobilephone"))
    string AdminMobile = (string)((AliasedValue)entityCollection.Entities[0].Attributes["PROJADMIN.mobilephone"]).Value;

I advice always try the FetchXmls before using. Best tool to do so is FetchXM Tester of XrmToolBox.


There is a one pitfall. I have seen some tools which are not returning the correct field names as expected. For example, I have tested same FetxhXML in DataSet creating tool in SSRS report authering extensions in VS2012 that returned like PROJADMIN-mobilephone, which is WRONG.

Jul 10, 2017

Call WFs and Actions from Plug-ins

When developing server-side custom functionalities for Dynamics 365, our initial though is whether jump in to plug-ins, WFs or Actions. We always need to check the pros and cons of them depending on the scenario.

By the meantime, separating the functionalities among them and using them harmoniously would add more value in terms of flexibility. Below code snippets could be helpful in such an approach.

Call a WF from a Plug-in;

ExecuteWorkflowRequest request = new ExecuteWorkflowRequest()
{
  WorkflowId = new Guid("019813bc-104b-4dc9-93d5-54d93d79908e"), //WF Id
  EntityId = Id
};
ExecuteWorkflowResponse executeWorkflowResponse = (ExecuteWorkflowResponse)service.Execute(request);

Call an Action from a Plug-in;

OrganizationRequest req = new OrganizationRequest("new_profitcalculator");
req["Amount"] = amount; //Parameter
req["Target"] = new EntityReference(new_office.EntityLogicalName, Id);
OrganizationResponse response = service.Execute(req);

Jan 4, 2017

Calling third party Web service from Dynamics CRM online plug-in

Just thought of sharing this important code snippet. Please have a closer look at Binding Configuration part which is the essence of the exercise.

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

    if (context.Depth > 1)
    {
        return;
    }

    try
    {
        BasicHttpBinding myBinding = new BasicHttpBinding();
        myBinding.Name = "BasicHttpBinding_Service";
        myBinding.Security.Mode = BasicHttpSecurityMode.Transport;
        myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
        myBinding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
        myBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

        EndpointAddress endPointAddress = new EndpointAddress(@"https://XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.asmx");
        ClassLibrary1.XXXXX.XXXClient serviceClient = new ClassLibrary1.XXXXX.XXXClient(myBinding, endPointAddress);
        string xmlRequest = @"XXX";

        string result = serviceClient.<Method>(xmlRequest);
        XmlDocument resultXML = new XmlDocument();
        resultXML.LoadXml(result);

     }
     catch (Exception ex)
     {
        throw new InvalidPluginExecutionException(ex.Message);
     }
     finally
     {

     }
  }
}

Jun 11, 2015

Exception handling – Process failed status in Asynchronous process

We discussed about handling of exception of plug-ins in few occasions. We also discussed and   recommended the usage of tracing mechanism and you can check it here.

It could be important to show a formal message to the user if a process is failed, especially in Asynchronous processes because it doesn’t throw any exception as in synchronous processors. User may not have sufficient permission to check system jobs either. Tracing information is not for end users, but for developers. In this case, we can use a custom status field with one value as process failed.

Please check the code snippet and notice how that status change happened just before exception is thrown.

try
{
  //Any information you need to trace
  // ex;
  //tracer.Trace("Account:{0} and Guid:{1}", _account.Name, _account.accountid);
}
catch (Exception ex)
{
  // ***status change to Failed status ****
  throw new InvalidPluginExecutionException("[" + ex.Message + "]" + ex.StackTrace, ex);
}
finally
{
}

Now, this worked fine for lengthy processes for me. Yet, there is one exception: if issue occur with SQL time out/ DB connection this will become a problem. Why? Simply this update won’t happen and system will stuck in that point… bitter truth is we are not getting any trace information either, because it come after this step.

So you need to decide depending on your scenario. If the SQL time out is a possible culprit, just forget about this status change.

Sep 21, 2014

Plug-in concerns: synchronous or asynchronous, transactional or not …

When developing plug-in we have to make few main decisions based on the requirement. Most fundamental thought comes to our mind is whether we need to do in Pre stage or Post stage, which is usually not hard.

Synchronous or Asynchronous

Then we need to decide, whether it is a synchronous or asynchronous. In my opinion, unless there is very specific reason, it’s good to work synchronous, which is real time. Meaning is once synchronous process is started, user can’t proceed to next step till it finishes. Best fruit is you know if there is any error sooner than later. You will simply see an exception thrown if something is wrong.

Yet, there are specific scenarios you will be forced to go for an asynchronous ones. Best example I encountered is some batch processes that needs a long time to complete. Asynchronous process works in the background, allowing user to proceed to next step. I have done very successful batch processes in asynchronous mode that took 5 to 10 hours to complete.

Problem with asynchronous execution is, you don’t know if an error occur because it’s happening in the background. You may need to check system jobs to see if there are errors.

Transactional or Not

When we discuss about the synchronous and asynchronous modes, we are also forced to discuss about transactions. All asynchronous plug-ins are none-transactional, which means.. if plug-in fails in some point, all the operations happened till that failure are valid. Suppose my batch process needs to process 100 records in one go. If it fails in 61st record due to an data issue, first 60 records have already processed correctly.. Only remaining 40 are un-processed. That’s because asynchronous plug-ins are none-transactional.

In fact, my golden rule for asynchronous ones is use a proper tracing mechanism inside the plugin code. So you are able to see what went wrong after the execution, in case of a failure. Otherwise, how do you know what processed and what not? Knowing Point of failure is important here.

It is best to work synchronous plug-ins for important calculations of payment & etc. for example. Since its best to have nothing as the outcome than having half of the steps… (Ex: Paying the commission for a sale and not doing the sale actually could be horrible than happening nothing.) Fruit of synchronous plug-ins is (with one exception…a special case) they are transactional. That means, if it fails in some point all the previous operations would be reversed. Outcome is 100% or nothing. No need to worry about the point of failure.

Importance of Pre-validation

Let’s talk about the “special case”.. if you register the synchronous plug-in in the pre-validation, it is none transactional. Please refer this article, which explains it really well. It sounds like, the transaction starts in some point and Pre-validation stage occurs before that.

Please note if you write a Pre-Validation Plugin for Create message you will not have the Id since record is not yet created, you need to deal with other fields those available in create.

http://mscrmtools.blogspot.com.au/2011/01/crm-2011-plugins-welcome-to.html

How to throw exception while keeping the changes

Depending on the situation, transactional operations could be annoying too. Recently I wanted to write a plug-in that needs to throw an error to show a message to user, but I didn’t want to roll back what I did so far. Since I need to throw the exception, I have to go for a synchronous plug-in definitely, but it rollbacks everything as soon as they through the exception. Only solution was to register it in pre-validation. (Synchronous in pre-validation is not transactional.. this is the special case we discussed)

So these are some concerns about the plug-in designing.

Sep 2, 2014

Update the same entity on Deactivation

This is the code snippet to de activate an entity and it’s straightforward;


SetStateRequest setStateRequest = new SetStateRequest()
{
    EntityMoniker = new EntityReference
    {
        Id = _accountid,
        LogicalName = "account"
    },
    State = new OptionSetValue(1),
    Status = new OptionSetValue(2)
};
crmService.Execute(setStateRequest);

Now, we will see the tricky part. Sometimes we are asked to update some fields in an entity when deactivated. For example, you are required to modify the field values before deactivating an account. Problem is, once you deactivate it, you are not allowed to modify the values. Hence, you need to do it before deactivation. For this we need to register a plug-in for pre- stage of account for state change. (register two steps; setState and setState Dynamic Entity). Below is the code;

public class AccountDeactivation : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
    IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

    try
    {
        IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
        IOrganizationService crmService = factory.CreateOrganizationService(context.UserId);
        Entities.OrgOrionDatamodel orgSvcContext = new Entities.OrgOrionDatamodel(crmService);

        if ((context.InputParameters.Contains("EntityMoniker")) && (context.InputParameters["EntityMoniker"] is EntityReference))
        {
            var targetEntity = (EntityReference)context.InputParameters["EntityMoniker"];
            var state = (OptionSetValue)context.InputParameters["State"];
            var status = (OptionSetValue)context.InputParameters["Status"];

            if (targetEntity.LogicalName != "account")
            { return; }

            if ((state.Value == 1) && (status.Value == 2)) //Identify when the Deactivation is occuring
            {
                //Logic here
            }
        }
    }
    catch (Exception ex)
    {
        throw new InvalidPluginExecutionException("[" + ex.Message + "]" + ex.StackTrace, ex);
    }
    finally
    {
        context = null;
    }
}
}

This plug-in fires on (actually before) deactivation of account. Why?
To fire this below conditions should be met;
- State change should occur
- Resulting values of state and status should be 1 and 2 respectively.
(in fact, this satisfies only when deactivation). Since we register the plug-in in pre stage we still can modify the entity itself if needed.

Note:
This code is working fine for account and many other entities including custom entities. Please check if you need to implement this to entities with complex state and status values such as lead and opportunity. Change the values of conditions (state and status) to pick the correct occurrence you are interested in.

Related Posts;
Sample Plug-in: State change

Jul 27, 2014

Using same plug-in for all Create, Update and Delete events

In some cases, we may need to execute same plug-in for Create, Update and Delete. Is this possible?

Yep, you don’t need to write many plug-ins if it’s the same logic. Only thing, we need to have two different ways of triggering the logic in same code. If you need to refresh the knowledge, check Create, Update and Delete code snippets.

You can write a one code accommodating both needs. Different is, in Create/Update we get the target entity in the context while in Delete we get only the reference.

try
{
  context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

  // Create, Update
  
  if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
  {
     TargetEnt = (Entity)context.InputParameters["Target"];

     //Logic here
     //Read Guid like this; 
     //Guid _officeid = (Guid)TargetEnt.Attributes["new_officeid"];

  }

  // Delete

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

     //Logic here
     //Read Entity reference like this; 
     //EntityReference _officeidRef = (EntityReference)context.InputParameters["Target"];

  }
  
}
(*I suppose we use a custom entity called new_office in this example)

Hope this is Clear…  register this all Create, Update and Delete steps.

Note:
In Create/Update section always get field values by retrieve method (passing Guid), rather than reading via target entity. If you can remember, though we get all fields in the context for Create, we get only updated fields in Update. Remember?

Jul 3, 2014

Test Plug-in execution using trace

One of the pervious posts,  we discussed few ways of testing how plug-in is executing and whether expected values are being retrieved to the right variables and etc.

Anyway, recently I found tracing as one of the other effective way of testing a plug-in.
What is done here is usage of tracer object (use Microsoft.Xrm.Sdk) to trace any value within the flow.

Below code will give an idea how it’s being done in your code. Please have a good look at the throw of exception line within the catch block. See how the traced information has been passed with the exception.

using System;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System.ServiceModel;

namespace ABCandComSol
{
public class AccountQualifier : IPlugin 
{
  public void Execute(IServiceProvider serviceProvider)
  {

    ITracingService tracer = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

    try
    {
        //Any information you need to trace
        // ex;
        //tracer.Trace("Account:{0} and Guid:{1}", _account.Name, _account.accountid);
    }
    catch (Exception ex)
    {
        tracer.Trace(ex.Message);
        throw new InvalidPluginExecutionException("[" + ex.Message + "]" + ex.StackTrace, ex);
    }
    finally
    {
    }

  }
}
}

Once plug-in is failed all the details you put in the tracer is viewable.  So positive side is, you don’t need to remove any code once code starts working fine. In future, if client say something failed, you got all the information you need in hand!

How to see the trace values

Synchronous Plug-in: Once exception is thrown, check information, you may see all the tracer information within error information.

Asynchronous Plug-in: If plug-in fails, a system jobs will show relevant failed entry. Once you open it, you may see all the tracer information. Further reading here.

Note
In fact, if you want to test something for a working code, you may need to forcefully throw an exception.

Nov 17, 2013

Plug-in – Check context in Early bound & Late bound approaches

I previously provided a nice descriptive article on developing plug-ins using Dynamics CRM toolkit. I tried the same approach for CRM 2013 online in both early bound and late bound approaches which worked fine. New SDK\Tools\DeveloperToolkit provides the latest toolkit you can install. Then you get the project templates for CRM 2013 in Visual Studio.


Here I am coding to create a task when opportunity is updated.

Early Bound Sample code;

protected void ExecutePostOpportunityUpdate(LocalPluginContext localContext)
{
if (localContext == null)
{
    throw new ArgumentNullException("localContext");
}

IPluginExecutionContext context = localContext.PluginExecutionContext;
IOrganizationService service = localContext.OrganizationService;

if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
    Opportunity _Opportunity = ((Entity)context.InputParameters["Target"]).ToEntity<Opportunity>();
    try
    {
        // Create Task
        Task _task = new Task();
        _task.Subject = "opportunity changed";
        _task.RegardingObjectId = new EntityReference(Opportunity.EntityLogicalName, _Opportunity.Id);
        _task.Description = "Please look into this opportunity changes";
        service.Create(_task);
    }
    catch (FaultException ex)
    {
        throw new InvalidPluginExecutionException("Plug-in error : ", ex);
    }

    finally
    {
        _Opportunity = null;
    }
}
}

Late Bound Sample code;

protected void ExecutePostOpportunityUpdate(LocalPluginContext localContext)
 {
  if (localContext == null)
  {
      throw new ArgumentNullException("localContext");
  }

  IPluginExecutionContext context = localContext.PluginExecutionContext;
  IOrganizationService service = localContext.OrganizationService;

  if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
  {
      Entity entity = (Entity)context.InputParameters["Target"];
     try
      {
          // Create Task
          Entity task = new Entity("task");
          task["subject"] = "opportunity changed (Late)";
          task["regardingobjectid"] = new EntityReference("opportunity", (Guid)entity.Attributes["opportunityid"]);
          task["description"] = "Please look into this opportunity changes";
          service.Create(task);
      }
      catch (FaultException ex)
      {
          throw new InvalidPluginExecutionException("Plug-in error : ", ex);
      }
      finally
      {
          entity = null;
      }
  }
}

Late Bound – Early Bound argument could be as meaningless as Cannon-Nikon argument in photography!

Whatever you do, if you can do it wiser, it matters. Anyway, I am happy to illustrate one simple mistake one could do if not understood clearly, when coding with early bound approach.

When coding plug-ins one of the crucial things is to identify if an attribute exists in the context. Typically, in update message, we need to know if a field is changed by inspecting the context. In late bound, we do it this way and it works fine;

Entity entity = (Entity)context.InputParameters["Target"];
if (entity.Contains("currentsituation"))
{
    //Code
}

In early bound, we usually cast the context in to our entity type.


After casting we see all the fields in intelligence, yet it doesn’t say they are available. They are available only if they exist in the context.

This can be misleading. In other words below is NOT the way to do something if currentsituation field is changed.

Opportunity _Opportunity = ((Entity)context.InputParameters["Target"]).ToEntity<Opportunity>();
if (_Opportunity.CurrentSituation != null)
{
    // Code
}

So, checking the context has to be done in the late bound manner (as shown, using Contains keyword). Keep in mind, when we say field exist in the context, that means “its presence”. Still its value can be NULL or NOT NULL, which has to be checked as next step, depending on your requirement.

Nov 10, 2013

Dynamics CRM toolkit for CRM online

This is an amazing step by Microsoft to leverage the development effort especially in online department. From here you can download the needful stuff.

Also check this article for one of the clear explanations on how to get started;

http://mscrmshop.blogspot.com.au/2012/01/step-by-step-plugin-tutorial-using.html

Jun 27, 2013

Plugin for opportunity Win / Lose

It can be a common requirement to execute plug-ins when opportunities are closed as Win or Lose. In most cases you need to identify two cases separately. Ironically, I didn’t find many resourceful articles about it. In fact, I thought of sharing my experience.

These are the relevant statecode and statuscode combination for opportunity.


First one could think of having a State Change plug-in for this. But I learned it’s not successful.

Whether it is Win or Lose, opportunity will be closed. So it creates a record in OpportunityClose entity. Then I though, we could do a create plug-in for OpportunityClose entity. Now the problem is you are not able to catch whether it’s a Win or Lose. If your requirement is just to do something when opportunity is closed, this works.

Then only I decided to do two different plugin for Win message and Lose message which triggers the plugin in the correct action.

So correct plugin registration steps would be seen as below;



Then I checked the plug-in context which made me shocked again. It doesn’t have opportunity record but it does have an OpportunityClose.


OpportunityClose is of course an unfamiliar entity for me. For you too obviously! Then only I realised it should be called a “Black Sheep”. You know why? Could you guess the primary Key of this entity? Your obvious answer should be opportunitycloseid which is completely wrong! Check below picture. It is Activityid!

Anyway, strangeness of primary key was explained only for your knowledge. The good side is this entity contains opportunityid which is the gateway for all the attributes of our current record. So for both Win and Lose plugins I started coding as below, by passing the OpportunityClose instead of Opportunity, knowing that it contains opportunityid.

context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
service = factory.CreateOrganizationService(context.UserId);

if (context.InputParameters.Contains("OpportunityClose"))
{
   oppCloseEnt = (Entity)context.InputParameters["OpportunityClose"];
   .........
   .........
}

In summery I am passing opportunityclose, read opportunityid in it, retrieve opportunity fields I need using the service. In a way, it’s like asking something about your home from your neighbour! Anyway, it worked for me!