"Mastering Data Types in Dynamics CRM Plugin Development: A Comprehensive Guide"
To effectively develop plugins for Dynamics CRM (also known as Dynamics 365 or Dataverse), a comprehensive understanding of the platform’s supported data types is essential. Plugins, written in C#, are executed in response to specific events—such as record creation or updates—enabling developers to manipulate data programmatically within the system. This guide provides a detailed explanation of how to manage the most common data types when interacting with entity attributes in a plugin, accompanied by precise examples for each.
Key Concepts:
Before delving into specific data types, it is critical to understand the foundational elements of plugin development in Dynamics CRM:
Plugin Execution Context: The IPluginExecutionContext interface provides essential details about the triggering event, including the Target entity—an Entity object representing the record being processed.
Entity Attributes: The Entity class includes an Attributes collection, which holds the field values of the entity. Developers access and modify these values through this collection.
Reading Values: The GetAttributeValue<T>("fieldname") method ensures safe retrieval of attribute values, casting them to the appropriate type.
Writing Values: Values are assigned directly to the Attributes collection using the field’s logical name, e.g., entity["fieldname"] = value;
Common Data Types and Their Management:
The sections below detail the primary data types in Dynamics CRM and provide guidance on their handling within a plugin.
1. String (Text)
Represents singleline text fields.
Reading: Retrieve the value using GetAttributeValue<string>("fieldname").
Writing: Assign a string directly to the attribute.
Example:
// Reading a string field
string name = targetEntity.GetAttributeValue<string>("name");
// Writing to a string field
targetEntity["name"] = "New Name";
2. Integer: Represents whole numbers.
Reading: Retrieve the value using GetAttributeValue<int>("fieldname").
Writing: Assign an integer directly to the attribute.
Example:
// Reading an integer field
int quantity = targetEntity.GetAttributeValue<int>("quantity");
// Writing to an integer field
targetEntity["quantity"] = 100;
3. Decimal
Represents numbers with decimal precision.
Reading: Retrieve the value using GetAttributeValue<decimal>("fieldname").
Writing: Assign a decimal value, using the m suffix for literals.
Example:
// Reading a decimal field
decimal price = targetEntity.GetAttributeValue<decimal>("price");
// Writing to a decimal field
targetEntity["price"] = 99.99m;
4. Boolean
Represents true/false values.
Reading: Retrieve the value using GetAttributeValue<bool>("fieldname").
Writing: Assign true or false directly to the attribute.
Example:
// Reading a boolean field
bool isActive = targetEntity.GetAttributeValue<bool>("isactive");
// Writing to a boolean field
targetEntity["isactive"] = true;
5. DateTime:
Represents date and time values.
Reading: Retrieve the value using GetAttributeValue<DateTime>("fieldname").
Writing: Assign a DateTime object, typically in UTC.
Example:
// Reading a DateTime field
DateTime createdOn = targetEntity.GetAttributeValue<DateTime>("createdon");
// Writing to a DateTime field
targetEntity["customdate"] = DateTime.UtcNow;
6. Lookup (Entity Reference)
Represents a reference to another record, such as a relationship.
Reading: Retrieve the EntityReference object using GetAttributeValue<EntityReference>("fieldname"), which provides the logical name and GUID of the referenced entity.
Writing: Assign an EntityReference object containing the logical name and GUID of the target record.
Example:
// Reading a lookup field
EntityReference account = targetEntity.GetAttributeValue<EntityReference>("parentaccountid");
if (account != null)
{
Guid accountId = account.Id;
string logicalName = account.LogicalName;
}
// Writing to a lookup field
EntityReference newAccount = new EntityReference("account", new Guid("someguidhere"));
targetEntity["parentaccountid"] = newAccount;
7. OptionSet (Picklist)
Represents a predefined set of options, such as dropdown menus.
Reading: Retrieve the integer value using GetAttributeValue<OptionSetValue>("fieldname").
Writing: Assign an OptionSetValue object with the integer value of the desired option.
Example:
// Reading an OptionSet field
OptionSetValue status = targetEntity.GetAttributeValue<OptionSetValue>("statuscode");
if (status != null)
{
int statusValue = status.Value;
}
// Writing to an OptionSet field
targetEntity["statuscode"] = new OptionSetValue(1); // Assumes 1 is a valid option
8. Money (Currency)
Represents monetary values.
Reading: Retrieve the Money object using GetAttributeValue<Money>("fieldname"), then access its Value property.
Writing: Assign a Money object with a decimal value.
Example:
// Reading a Money field
Money totalAmount = targetEntity.GetAttributeValue<Money>("totalamount");
if (totalAmount != null)
{
decimal amount = totalAmount.Value;
}
// Writing to a Money field
targetEntity["totalamount"] = new Money(500.00m);
9. Memo (Multiline Text)
Represents large text fields, managed similarly to strings.
Reading: Retrieve the value using GetAttributeValue<string>("fieldname").
Writing: Assign a string directly to the attribute.
Example:
// Reading a Memo field
string description = targetEntity.GetAttributeValue<string>("description");
// Writing to a Memo field
targetEntity["description"] = "This is a detailed description.";
10. File (Attachment)
Represents file attachments, typically managed via the Annotation entity.
Handling: File fields are not directly manipulated within the target entity. Instead, create or update an Annotation record linked to the entity.
Example (Simplified):
// Creating an annotation for a file attachment
Entity annotation = new Entity("annotation");
annotation["objectid"] = new EntityReference(targetEntity.LogicalName, targetEntity.Id);
annotation["subject"] = "Attachment";
annotation["filename"] = "document.pdf";
annotation["documentbody"] = Convert.ToBase64String(fileBytes); // fileBytes is the file content in bytes
service.Create(annotation);
General Plugin Structure
Below is a structured example of a plugin that handles various data types:
using Microsoft.Xrm.Sdk;
using System;
public class SamplePlugin : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// Retrieve the execution context and organization service
IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
IOrganizationService service = factory.CreateOrganizationService(context.UserId);
// Ensure the target entity is available
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
Entity targetEntity = (Entity)context.InputParameters["Target"];
// Example: Handle an update event
if (context.MessageName == "Update")
{
// Read and modify a string field
string name = targetEntity.GetAttributeValue<string>("name");
if (!string.IsNullOrEmpty(name))
{
targetEntity["name"] = name + " Updated";
}
// Read and modify an integer field
int quantity = targetEntity.GetAttributeValue<int>("quantity");
targetEntity["quantity"] = quantity + 1;
// Set a lookup field
EntityReference account = new EntityReference("account", new Guid("someguidhere"));
targetEntity["parentaccountid"] = account;
}
}
}
}
Best Practices
Attribute Existence Verification: Always confirm an attribute exists before accessing it to prevent runtime errors:
if (targetEntity.Contains("fieldname"))
{
// Safely read or write the attribute
}
Null Value Handling: The GetAttributeValue<T> method returns null if the field is unset, requiring null checks:
if (targetEntity.GetAttributeValue<string>("name") != null)
{
// Process the value
}
Type Accuracy: Ensure the data type in the code aligns with the CRM field type to avoid mismatches.
Execution Stages: Consider whether the plugin operates in the preoperation stage (modifying data before saving) or postoperation stage (executing actions after saving).
Testing: Rigorously test plugins with diverse data types and edge cases to ensure robustness.