This comprehensive guide provides a detailed approach to automate the creation of many-to-many (N:N) relationship records in Dynamics 365 using a custom plugin and a JavaScript action. The solution streamlines the association of multiple records through a straightforward interface, making it indispensable in scenarios requiring batch processing or integration tasks.
Add and Remove Member from Marketing List Using Plugin in Dynamics 365
Introduction
Managing marketing list memberships efficiently is crucial for CRM systems like Microsoft Dynamics 365, where marketing lists play a key role in organizing campaigns and communications. This article demonstrates how to use custom plugins to add and remove contacts from marketing lists, leveraging Dynamics 365's SDK messages for optimized performance and robust error handling.
Understanding TimeSpan in Dynamics 365 Plugins
TimeSpan
is a .NET structure ideal for measuring and managing time intervals. It is frequently utilized in Dynamics 365 plugins to calculate differences between dates or manage time-based data efficiently.
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)
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:
Reference:
- https://powerusers.microsoft.com/t5/Power-Apps-Ideas/Binding-FetchXML-to-Subgrid-with-reference-to-RecordID-to-filter/idi-p/675538
- 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);
}