Showing posts with label Plugin. Show all posts
Showing posts with label Plugin. Show all posts

Safely Handling AliasedValue in Dynamics 365 Plugins

 Developing plugins for Dynamics 365 often involves extracting and manipulating data from linked entities using FetchXML. One common challenge in this scenario is handling the AliasedValue objects that Dynamics 365 returns when querying attributes from these linked entities.

Enhancing Dynamics 365 Plugin Reliability with Specific Exception Logging

 When developing plugins for Dynamics 365, handling exceptions robustly and providing clear, actionable error messages can significantly improve the maintenance and usability of your solutions. This article discusses a practical tip for logging specific exceptions to streamline troubleshooting and enhance system reliability.

Invoking Dynamics 365 Custom Actions from JavaScript to Trigger Plugins

 Custom actions in Dynamics 365 are a versatile tool to extend your CRM capabilities, allowing you to encapsulate complex operations into callable functions. These actions are particularly useful when you need to trigger plugins that execute server-side logic in response to client-side events. Here’s a simplified guide on how to invoke these actions from JavaScript, covering both basic and advanced scenarios.

Optimizing Large-Scale Data Updates in Dynamics 365 with Asynchronous Plugins and Flexible FetchXML

Optimizing Large-Scale Data Updates in Dynamics 365 with Asynchronous Plugins and Flexible FetchXML

Introduction

In Dynamics 365, it's not uncommon to encounter scenarios where vast quantities of records require updates or complex manipulations. Naively processing large volumes in a single operation can strain performance and lead to timeouts. This article presents a strategy to tackle such challenges, emphasizing asynchronous processing, plugin chaining, and flexible FetchXML-driven initiation.

Understanding Relationship Types in Dynamics 365: N:N vs. 1:N & N:1

 

Understanding Relationship Types in Dynamics 365: N:N vs. 1:N & N:1

When modeling data relationships in Dynamics 365, understanding the distinction between N:N (Many-to-Many) and 1:N & N:1 (One-to-Many & Many-to-One) relationships is crucial. These relationship types serve different purposes and have unique characteristics.

Dynamics 365 Data Retrieval: Comprehensive Guide

 

Dynamics 365 Data Retrieval: Comprehensive Guide

Retrieving data effectively in Dynamics 365 (D365) is crucial for custom solutions' performance and scalability. This article explores different methods for data retrieval - QueryExpression, FetchXML, RetrieveMultiple, and direct record retrieval, providing complete sample codes for each scenario and a comparative analysis to help choose the right approach for specific needs.

Integrating Client-Side Scripts with Server-Side Plugins in Dynamics 365

 

Integrating Client-Side Scripts with Server-Side Plugins in Dynamics 365

Introduction

Integrating front-end actions with back-end server plugins in Dynamics 365 allows for robust process automation and enhanced user interaction. This article presents a generalized approach, showcasing a client-side script that triggers a custom action, subsequently invoking a server-side plugin for comprehensive business logic execution.

Call third party webapi from plugin using secure/ unsecure configuration (Sample C# code)

 Call third party webapi from plugin using secure/ unsecure configuration (Sample C# code)

Reference

Utilising Pre/Post Entity Images in a Dynamics CRM Plugin

 Utilising Pre/Post Entity Images in a Dynamics CRM Plugin

Lets take a closer look at how Pre and Post Images can be implemented as part of a CRM Plugin…

Tutorial: Write and register a plug-in

 Tutorial: Write and register a plug-in

This tutorial is the first in a series that will show you how to work with plug-ins. 

Customize the queries for subgrids of Dynamics 365 form

 Subgrids are great but the limitation of needing a direct relationship between the sub entity and the main entity makes it very restrictive. There are many different use cases, if instead of using a pre-configured view that only allows linking to the active Record via a predefined relationship, we could instead bind a custom FetchXML do a subgrid and this custom FetchXML would allow for one token called #RecordID# to reference the currently open record on the form by returning the GUID. 

In Classic UI people used the subgrid.control.SetParameter("fetchxml") function but this is now broken in Unified Interface. In Unified Interface it appears a Retrieve Multiple Plugin is the only way to achieve this which is extremely complicated for something as simple as binding a query to a subgrid. 

 Here is the only way to currently do this: 

https://sank8sinha.wordpress.com/2020/01/07/adding-filtered-views-in-uci-in-dynamics-365-crm-finally...


 Reference:

  1. https://powerusers.microsoft.com/t5/Power-Apps-Ideas/Binding-FetchXML-to-Subgrid-with-reference-to-RecordID-to-filter/idi-p/675538
  2. https://cloudblogs.microsoft.com/dynamics365/no-audience/2012/04/16/deep-queries-for-subgrids/?source=crm


How to Retrieve the value of an optionset field within a plugin

 

In Dynamics 365 C# Plugin, if retrieve optionset type fields value, you may need to convert the object to optionsetvalue type before quote its value. See below bold red code.

namespace UpdateTasksByOpportunity.Plugin

{

    public class ChangeAllKidEntityRecordsStatusByParentEnty : IPlugin

    {

        public void Execute(IServiceProvider serviceProvider)

        {

            // Obtain the tracing service

            ITracingService tracingService =

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


            // Obtain the execution context from the service provider.  

            IPluginExecutionContext context = (IPluginExecutionContext)

                serviceProvider.GetService(typeof(IPluginExecutionContext));


            // The InputParameters collection contains all the data passed in the message request.  

            if (context.InputParameters.Contains("Target") &&

                (context.InputParameters["Target"] is Entity ||

                context.InputParameters["Target"] is EntityReference))

            {


                Guid regardingobjectid = Guid.NewGuid();


                if (context.InputParameters["Target"] is Entity)

                {

                    // Obtain the Parent entity from the input parameters.  

                    Entity entity = (Entity)context.InputParameters["Target"];


                    // Refer to the opportunity in the task activity.

                    // Get the current opportunity ID / Parent Entity GUID            

                    regardingobjectid = new Guid(entity.Id.ToString());

                }


                else if (context.InputParameters["Target"] is EntityReference)

                {

                    // Obtain the Parent entity reference from the input parameters.  

                    EntityReference entityReference = (EntityReference)context.InputParameters["Target"];


                    // Refer to the opportunity in the task activity.

                    // Get the current opportunity ID / Parent Entity GUID            

                    regardingobjectid = new Guid(entityReference.Id.ToString());

                }


               


                // Obtain the organization service reference which you will need for  

                // web service calls.  

                IOrganizationServiceFactory serviceFactory =

                    (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));

                IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);


                //Check if Opportunity status is changed to "OPEN". If not, return.

                //0 - "OPEN";  1 - "WON";  2 - "LOST"

                Entity entityOpportunity = service.Retrieve("opportunity", regardingobjectid, new ColumnSet("statecode"));

                if(((OptionSetValue)entityOpportunity["statecode"]).Value != 0)

                {

                    tracingService.Trace("Opportunity new status: {0} is not OPEN", ((OptionSetValue)entityOpportunity["statecode"]).Value);

                    return;

                }



                //0 Open;  1 Completed;  2 Canceled

                int stateCode = 2;

                string kidEntity = "task";


                try

                {

                    // Retrieve all tasks with regarding opportunity is current opportunity

                    var queryExpression = new QueryExpression(kidEntity);

                    var qeFilter = new FilterExpression(LogicalOperator.And);

                    qeFilter.AddCondition(new ConditionExpression("statecode", ConditionOperator.Equal, "Open"));

                    qeFilter.AddCondition(new ConditionExpression("subject", ConditionOperator.Like, "%F/U%"));

                    qeFilter.AddCondition(new ConditionExpression("description", ConditionOperator.Like, "%Automated Task%"));

                    qeFilter.AddCondition(new ConditionExpression("regardingobjectid", ConditionOperator.Like, regardingobjectid));

                    queryExpression.Criteria = qeFilter;

                    queryExpression.ColumnSet = new ColumnSet("regardingobjectid");


                    //Get results:

                    var result = service.RetrieveMultiple(queryExpression);

                    foreach (var relatedTask in result.Entities)

                    {

                        

                        // Create the Request Object

                        var state = new SetStateRequest();

                        state.State = new OptionSetValue(stateCode);


                        // Point the Request to the case whose state is being changed

                        state.EntityMoniker = new EntityReference(kidEntity, relatedTask.Id);


                        // Execute the Request

                        var stateSet = (SetStateResponse)service.Execute(state);                                             

                    }

                }


                catch (FaultException<OrganizationServiceFault> ex)

                {

                    throw new InvalidPluginExecutionException("An error occurred in FollowUpPlugin.", ex);

                }


                catch (Exception ex)

                {

                    tracingService.Trace("FollowUpPlugin: {0}", ex.ToString());

                    throw;

                }

            }

        }

    }


    [Serializable]

    internal class FaultException<T> : Exception

    {

        public FaultException()

        {

        }


        public FaultException(string message) : base(message)

        {

        }


        public FaultException(string message, Exception innerException) : base(message, innerException)

        {

        }


        protected FaultException(SerializationInfo info, StreamingContext context) : base(info, context)

        {

        }

    }

}

Query Expression Add Criteria for look up field (Dynamics 365

Microsoft Dynamics 365 Plugin C# code.

If you wanna retrieve multiple records, you may use RetrieveMultiple function.

You can set filters and select the columns(fields) in the retrieve response.

When I set a filter for a lookup field (GUID), I tried two ways.





Both options work normally, the second way is better, greatly reduced the Amount of retrieved records, better for system performance. But the first way is still a good try for reading and comparing GUID value from retrieve response.

Option 1:  Add filter in retrieve result procession. (Bad performance.)

// Retrieve all tasks with regarding opportunity is current opportunity

var queryExpression = new QueryExpression(kidEntity);

var qeFilter = new FilterExpression(LogicalOperator.And);

qeFilter.AddCondition(new ConditionExpression("statecode", ConditionOperator.Equal, "Open"));

qeFilter.AddCondition(new ConditionExpression("subject", ConditionOperator.Like, "%F/U%"));

qeFilter.AddCondition(new ConditionExpression("description", ConditionOperator.Like, "%Automated Task%"));

queryExpression.Criteria = qeFilter;

queryExpression.ColumnSet = new ColumnSet("regardingobjectid");

//Get results:

var result = service.RetrieveMultiple(queryExpression);

foreach (var relatedTask in result.Entities)

{

    if(((EntityReference)relatedTask.Attributes["regardingobjectid"]).Id.ToString() == (regardingobjectid).ToString())

    {

        // Create the Request Object

        var state = new SetStateRequest();

        state.State = new OptionSetValue(stateCode);

        // Point the Request to the case whose state is being changed

        state.EntityMoniker = new EntityReference(kidEntity, relatedTask.Id);

        // Execute the Request

        var stateSet = (SetStateResponse)service.Execute(state);

    }                        

}


Option 2:  Add the filter into the query expression criteria. (Good performance.)

// Retrieve all tasks with regarding opportunity is current opportunity

var queryExpression = new QueryExpression(kidEntity);

var qeFilter = new FilterExpression(LogicalOperator.And);

qeFilter.AddCondition(new ConditionExpression("statecode", ConditionOperator.Equal, "Open"));

qeFilter.AddCondition(new ConditionExpression("subject", ConditionOperator.Like, "%F/U%"));

qeFilter.AddCondition(new ConditionExpression("description", ConditionOperator.Like, "%Automated Task%"));

qeFilter.AddCondition(new ConditionExpression("regardingobjectid", ConditionOperator.Like, regardingobjectid));

queryExpression.Criteria = qeFilter;

queryExpression.ColumnSet = new ColumnSet("regardingobjectid");


//Get results:

var result = service.RetrieveMultiple(queryExpression);

foreach (var relatedTask in result.Entities)

{    

        // Create the Request Object

        var state = new SetStateRequest();

        state.State = new OptionSetValue(stateCode);

        // Point the Request to the case whose state is being changed

        state.EntityMoniker = new EntityReference(kidEntity, relatedTask.Id);

        // Execute the Request

        var stateSet = (SetStateResponse)service.Execute(state);                 

}