Saturday, September 3, 2016

An Evening with Me and #XRMToolbox Development

Image result for SocialI’ve got a couple GitHub issues on for my XrmToolBox Plugins that have been gnawing at me for a while.  In an effort to have a more enjoyable time, I thought I’d attempt to do a social programming event.  This will be unscripted event, open to anyone.  There will be 0 Slides, and 0 prep work.  It will offer people an opportunity to ask questions, see what’s involved with XTB development, and just generally hang out, with an underlying theme of attempting to knock out as many GitHub issues as I can.  It could be fun!  On the other hand, it could be incredibly boring.  Tons of people could log in!  On the other hand maybe no one will show up.  Whatever the case, we won’t know until we try!

  • Who: Myself and anyone that wants to join.  
  • What: Social programming (if that’s not already I thing, I’m making it one now!)  Feel free to un-mute, ask questions, make suggestions, or tell me I'm missing a semi-colon!
  • Where: Skype For Business Link.
  • When: September 8, 2016 from 9pm to midnight EDT
  • Why:  I’ve got stuff I want to do, and thought this could be a fun-er way of doing it! :-)

Friday, September 2, 2016

Allow Native Mapping When Referencing a Parent Entity

Normal Out of Box (OOB) CRM Relationship mapping can be extremely beneficial, but it has a very big “gotcha”.  They only work when creating a child entity from the parent form.  So if the user is on the Parent Entity Form, and adding a new entity via a Grid, then the mapping will automatically be populated for the user.  But what happens if you have an entity that is serving as a M:M entity (think Lead Products).   Generally, a user would create a Lead Product from the Lead Form.  This means if any mappings are setup from the Product to the Lead Product, they will not be applied.

There is no way for this to get applied natively, since the mapping takes place before the Lead Product is loaded, but the user doesn’t select a Product until after the Lead Product is loaded.  There is however, a simple partial work around.  Create a plugin that performs the mapping using what is defined in the OOB Field Mapping for the Relationship.  For extensibility, define the parent field(s) in the plugin step.  Below is a working example of such a plugin (utilizing the DLaB.Xrm.2016 NuGet Package).
public override void RegisterEvents()
{
    RegisteredEvents.AddRange(new RegisteredEventBuilder(PipelineStage.PreOperation, MessageType.Create).Build());
}
 
protected override void ExecuteInternal(ExtendedPluginContext context)
{
    if (UnsecureConfig == null)
    {
        context.Trace("No Fields listed in the Unsecure Configuration.  Nothing from which to initialize the entity!");
        return;
    }
    var target = context.GetTarget<Entity>();
    foreach (var field in UnsecureConfig.Split(new [] {",","|",Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries))
    {
        InitializeFromField(context, target, field);
    }
}
 
/// <summary>
/// Loads the configured mappings for the entity from the given field.  
/// Only attributes that do not exist in the target are set, and only if the field contains an EntityReference in the target
/// </summary>
/// <param name="context">The context.</param>
/// <param name="target">The target.</param>
/// <param name="field">The field.</param>
private void InitializeFromField(ExtendedPluginContext context, Entity target, string field)
{
    context.TraceFormat("Initializing from field {0}", field);
    var parent = target.GetAttributeValue<EntityReference>(field);
    if (parent == null)
    {
        context.TraceFormat("No Parent found for field {0}", field);
        return;
    }
    var mappedEntity = context.SystemOrganizationService.InitializeFrom(parent, target.LogicalName, TargetFieldType.ValidForCreate);
    foreach (var att in mappedEntity.Attributes.Where(a => !target.ContainsAllNonNull(a.Key)))
    {
        context.TraceFormat("Adding attribute {0}:{1}", att.Key, att.Value);
        target.Attributes.Add(att);
    }
}

The Details

  • The Plugin must be registered for each entity to be initialized
  • The Unsecure Configuration Settings must contain a comma (or pipe or newline) delimited list of parent lookup attributes to initialize the entity from. i.e. “ProductId”
  • The parent field lookup field must be populated on the initial save.
  • Only un-populated fields will be mapped.  If a user has defined a value already, it will not be overridden.
  • An end user will not see the mapped values on the form until after the initial save refresh occurs.
That’s it.  A few lines of code, and now your Entity Relations Field Mappings can be used in many more places!