0
votes

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.

enter image description here

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?

2

2 Answers

2
votes

The Target entity itself contains only basic entity identity data. If you want to access attributes for the entity you should configure a suitable PreImage or PostImage with the attributes that you want to access context.PreEntityImages, and you can then retrieve this via context.PreEntityImages or context.PostEntityImages. Alternatively (and less optimally) you can retrieve the latest version of the entity from within the PlugIn with the attributes you desire.

1
votes

Do this

 // GET CURRENT ACCOUNT
 var account = (Account)service.Retrieve("account", entity.Id, new ColumnSet(new string[] { "accountid", "parentaccountid" })); 

Instead of this

// GET CURRENT ACCOUNT
var account = entity.ToEntity<Account>();

As Josh Painter indicated in his comment, its possible that the event you registered against isn't passing in this value to the PluginExecutionContext.InputParameters["Target"], and thus the attribute becomes null when casting the Entity to an Account. Above is how to do a query to get the full record within the plugin, and that will always be populated, unless the plugin is registered pre-stage create (in which case your current code will work just fine, but since you're getting a null value, I assume you're not registering on pre-stage create).