Mastering Microsoft Dynamics 365 CE (CRM) Plugins: Context, Target, and IOrganizationService
Plugins in Dynamics 365 CRM let developers add custom business logic into the event pipeline, ensuring business rules are enforced.
In this guide, we’ll explore:
How to use IPluginExecutionContext to access context and target entity.
How to use IOrganizationService for CRUD operations.
How to run queries using QueryExpression and FetchXML (with advanced examples).
How to execute special requests using the service.
Practical use cases where each technique is applied.
Whether you’re starting with plugins or looking to refine your expertise, this blog will serve as a comprehensive reference.
Understanding Context and Target
When a plugin executes, the platform provides important runtime information through the IPluginExecutionContext. This includes details such as:
The message (e.g., Create, Update, Delete).
The primary entity name.
The stage in the pipeline (PreValidation, PreOperation, PostOperation).
Input and output parameters.
The most important input parameter is often the Target, which represents the record that triggered the event.
Example: Accessing Context and Target
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)
{
string logicalName = entity.LogicalName;
Guid id = entity.Id;
// Example use case: Log the target entity
tracingService.Trace($"Plugin triggered on entity: {logicalName}, Id: {id}");
}
}
Use case: You can use context and Target to determine whether a specific attribute was updated and conditionally run your logic. For example, only trigger follow-up actions when the status of a Case changes.
Retrieve a Single Record
Entity account = service.Retrieve("account",
new Guid("00000000-0000-0000-0000-000000000001"),
new ColumnSet("name", "telephone1", "primarycontactid"));
string accountName = account.GetAttributeValue<string>("name");
Use case: Retrieve account details when a related contact is updated, ensuring consistent synchronization between entities.
Retrieve Multiple Records (QueryExpression)
QueryExpression query = new QueryExpression("contact")
{
ColumnSet = new ColumnSet("fullname", "emailaddress1", "parentcustomerid")
};
query.Criteria.AddCondition("statecode", ConditionOperator.Equal, 0);
query.Criteria.AddCondition("emailaddress1", ConditionOperator.NotNull);
query.Orders.Add(new OrderExpression("fullname", OrderType.Ascending));
EntityCollection contacts = service.RetrieveMultiple(query);
foreach (var contact in contacts.Entities)
{
string fullname = contact.GetAttributeValue<string>("fullname");
}
Use case: Fetch all active contacts with valid email addresses for syncing into a mailing system.
Retrieve Multiple Records (FetchXML – Advanced)
FetchXML allows more complex queries that are sometimes easier to write than QueryExpression, especially when dealing with joins or aggregates.
Example 1: Retrieve All Active Opportunities With Related Account Name
string fetchXml = @"
<fetch>
<entity name='opportunity'>
<attribute name='name' />
<attribute name='estimatedvalue' />
<filter>
<condition attribute='statecode' operator='eq' value='0' />
</filter>
<link-entity name='account' from='accountid' to='customerid' alias='acc'>
<attribute name='name' alias='accountname' />
</link-entity>
</entity>
</fetch>";
EntityCollection opportunities = service.RetrieveMultiple(new FetchExpression(fetchXml));
Use case: Useful when building dashboards or reports that need both opportunity and account details in one query.
Example 2: Aggregate – Count Active Contacts Per Account
string fetchXml = @"
<fetch distinct='false' aggregate='true'>
<entity name='contact'>
<attribute name='contactid' aggregate='count' alias='contactcount' />
<link-entity name='account' from='accountid' to='parentcustomerid' alias='acc'>
<attribute name='name' groupby='true' alias='accountname' />
</link-entity>
</entity>
</fetch>";
EntityCollection results = service.RetrieveMultiple(new FetchExpression(fetchXml));
Use case: Helps managers quickly see how many contacts each account has, without exporting data.
Create a Record
Entity newContact = new Entity("contact");
newContact["firstname"] = "John";
newContact["lastname"] = "Doe";
newContact["emailaddress1"] = "john.doe@example.com";
Guid contactId = service.Create(newContact);
Use case: Automatically create a new Contact record when a lead is qualified.
Update a Record
Entity updateContact = new Entity("contact", contactId);
updateContact["telephone1"] = "123-456-7890";
service.Update(updateContact);
Use case: Update a contact’s phone number whenever the related account’s main phone number changes.
Delete a Record
service.Delete("contact", contactId);
Use case: Automatically clean up orphaned records when the parent entity is deleted.
Execute a Special Request
Dynamics 365 CRM includes specialized messages that can be executed through service.Execute().
Example: Win an Opportunity
WinOpportunityRequest winRequest = new WinOpportunityRequest
{
OpportunityClose = new Entity("opportunityclose")
{
["subject"] = "Opportunity Closed as Won",
["opportunityid"] = new EntityReference("opportunity", opportunityId)
},
Status = new OptionSetValue(3) // Won
};
service.Execute(winRequest);
Use case: Automate opportunity closure workflows, such as updating pipeline metrics or notifying stakeholders.
Conclusion
Plugins in Microsoft Dynamics 365 CRM give developers the ability to enforce business rules directly within the platform’s execution pipeline. By mastering context, target, and the use of IOrganizationService, you can perform everything from simple CRUD operations to complex queries with FetchXML or QueryExpression.
With these skills, you can:
Validate and enforce business rules before data is saved.
Automatically create or update related records.
Perform advanced queries for reporting or automation.
Execute system messages to integrate seamlessly with CRM processes.
Learning to harness these capabilities ensures your plugins are both powerful and efficient, helping organizations get the most from their Dynamics 365 CRM investment.

