I recently came across an in issue with a Dynamics 365 Workflow that called a plugin for advanced logic. The premise was, when a lead entity record is created via user input, if the email address given matched an existing contact to associate the lead with the contact. Then, send an email to that contact notifying them they were on a new lead. I created a plugin that used fetchxml to pull all contacts with a matching email, and if 1 and only 1 contact matched I would associate it.

My plugin code was pretty straight forward. and I thought I had it easily accomplished. The plugin would use the lead record and if it found a match to contact on email, the plugin would update the lead’s contact and pass back ‘true’ to the workflow. If true, the workflow would send an email to the lead’s contact, since it was just updated in the plugin.

[Output("HasContact")]
public OutArugment<bool> HasContact {get;set;}

public static bool Main(IorganizationService service, Guid leadid){
  var lead = service.Retrieve("lead",leadid, new ColumnSet(true));
  
  string getContactsByEmail = String.Format(@"<fetch mapping='logical'>
    <entity name='{0}'>
     <attribute name='contactid'/>
     <filter type='and'>
       <condition attribute='emailaddress1' operator='eq' value'{1}'/>
     </filter>
    </entity>
  <fetch/>","contact", lead["emailaddress"]);
  
  EntityCollection results = service.RetrieveMultiple(new 
  FetchExpression(getContactsByEmail));
  
  if(results.Entities.Count == 1)
  {
    lead['contact'] = results.Entities[0].ToEntityReference();
    service.Update(lead;)
    return true;
  }
  
  return false;
}

And the workflow looked something like:

This however, never worked and failed for any case where the contact was being updated via the plugin.

This is when I realized I fell for an old trick I had forgotten. If a workflow is triggered on an entity, that entity and its data is loaded into the workflow. Changes outside the workflow are not realized by the workflow as it now is operating off of in memory data. Simple example on a contact entity:
-Contact with name “Jay” is created.
-Workflow fires off plugin that updates name to “Bob.”
-Workflow then writes name to another field.
-The field will have name “Jay”.

So by changing the contact lookup record in the plugin, the following steps in the workflow still had no reference as they were already in memory. The solution was to simply change my plugin and workflow logic to return the EntityReference to the contact from the plugin, and allow the workflow to do the update on the contact lookup.

The proper solution:

[Output("OutContact")]
public OutArugment<EntityReference> OutContact {get;set;}

public static bool Main(IOrganizationService service, Guid leadid){
  var lead = service.Retrieve("lead",leadid, new ColumnSet(true));
  
  string getContactsByEmail = String.Format(@"<fetch mapping='logical'>
    <entity name='{0}'>
     <attribute name='contactid'/>
     <filter type='and'>
       <condition attribute='emailaddress1' operator='eq' value'{1}'/>
     </filter>
    </entity>
  <fetch/>","contact", lead["emailaddress"]);
  
  EntityCollection results = service.RetrieveMultiple(new 
  FetchExpression(getContactsByEmail));
  
  if(results.Entities.Count == 1)
  {
    return results.Entities[0].ToEntityReference();
  }
  
  return null;
}

And then the workflow that actually updates the contact lookup:

The workflow does the update.

Allowing plugins to update data is fine, but if that data needs to be used further down the workflow, let the workflow do the data updates.

I hope this saves someone else some time or frustration in the future.