I'm writing a plugin in C# for Dynamics CRM 2011 (On Prem).
The plugin is to prevent grandfathering in Account records (Accounts can have children OR a parent, but not both at the same time).
I'm struggling to check whether the Account has an associated parent or not, the attribute for parent ID is always null and I don't understand why.
I've correctly generated the early bound classes using crmsvcutil.exe
and added the file to my project.
The generated code for the parent ID looks like this:
/// <summary>
/// Unique identifier of the parent account.
/// </summary>
[Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("parentaccountid")]
public Microsoft.Xrm.Sdk.EntityReference ParentAccountId
{
get
{
return this.GetAttributeValue<Microsoft.Xrm.Sdk.EntityReference>("parentaccountid");
}
set
{
this.OnPropertyChanging("ParentAccountId");
this.SetAttributeValue("parentaccountid", value);
this.OnPropertyChanged("ParentAccountId");
}
}
A basic version of my plugin looks like this:
namespace NoGrandparentAccounts
{
using CRM2011GeneratedBusinessModel; // Namespace for crmsvcutil generated early bound classes
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using System;
using System.ServiceModel;
public class Main : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
// OBTAIN EXECUTION CONTEXT
var context(IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
// TRACING SERVICE FOR LOGGING
var tracingService =
(ITracingService)serviceProvider.GetService(typeof(ITracingService));
// WILL REPRESENT THE CURRENT ENTITY
Entity entity = null;
// INPUTPARAMETERS COLLECTION CONTAINS ALL DATA PASSED IN THE MESSAGE REQUEST
if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
{
// SET ENTITY TO THE CURRENT ENTITY
entity = (Entity)context.InputParameters["Target"];
// CHECK WE'RE IN ACCOUNTS ENTITY
if (context.PrimaryEntityName != "account")
return;
// CHECK WE'RE CREATING OR UPDATING THE ENTITY
if (context.MessageName != "Create" && context.MessageName != "Update")
return;
}
else
{
return;
}
try
{
// GET ORGANIZATION SERVICE
var serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
var service = serviceFactory.CreateOrganizationService(context.UserId);
// GET CURRENT ACCOUNT
var account = entity.ToEntity<Account>();
// HAS A PARENT ACCOUNT?
var hasParent = (account.ParentAccountId != null && account.ParentAccountId.Id != Guid.Empty);
}
catch (FaultException<OrganizationServiceFault> ex)
{
tracingService.Trace($@"Exception: {ex.Message} - {ex.StackTrace}");
throw new InvalidPluginExecutionException(@"An error occurred in the plugin.", ex);
}
catch (Exception ex)
{
tracingService.Trace($@"Exception: {ex.Message} - {ex.StackTrace}");
throw new InvalidPluginExecutionException(@"An error occurred in the plugin.", ex);
}
}
}
}
Unfortunately hasParent
is always false because account.ParentAccountId
is null. I don't know why because there is most definitely a parent account associated with this record and the attribute name is definitely parentaccountid
.
The call to find the child Accounts works fine using parentaccountid
:
// FETCH CHILDREN
var cExpr = new ConditionExpression("parentaccountid", ConditionOperator.Equal, account.Id);
var qExpr = new QueryExpression(Account.EntityLogicalName);
qExpr.ColumnSet = new ColumnSet(new string[] { "accountid" });
qExpr.Criteria.AddCondition(cExpr);
var results = service.RetrieveMultiple(qExpr);
// HAS CHILDREN
var hasChildren = results.Entities.Count > 0;
The entity count shows correctly as 3 and therefore hasChildren
is true.
I've checked whether the attributes for this entity contains parentaccountid
and it returns false which I assume is related to the problem:
if (entity.Attributes.Contains("parentaccountid"))
{
// Never gets here as this is false!?
}
What am I doing wrong?