Monday, July 9, 2012

Triggering plugins / workflows when associating entities (N:N)

A question I get asked often is “What is the best way to trigger some custom logic when entities are associated in CRM”? While there is no “good” answer, I will provide a set of different approaches in this post which can be helpful to select the best strategy to implement your custom logic.


When you think of N:N relationships, there are multiple ways to implement them in CRM:


1. Native N:N relationships: These are the relationships you can easily create between 2 entities by customizing the entity relationships.


2. Custom “Intersect” entities: These are custom entities that act as the “glue” between 2 entities. For example if you have multiple students and multiple teachers, you might create a custom entity “Student Teacher Association” which contains a lookup to the student, a lookup to the teacher and then additional relationship metadata such as the course name and classroom. Your custom entity would act as an “intersect” entity representing an N:N relationship between teachers and students.


3. Connections or Relationships: Connections can also be used to relate any 2 entities and assign a “connection role” to the relationship (such as “partner” or “friend”).


I won’t go into details and the pros and cons of the above 3 alternatives since Richard has already done a great job at that in this post. The point I want to make is that depending on how you model your N:N relationships, then the answer around how to implement custom logic when entities are associated changes.


Native N:N Relationships


Consider for example, that you want to send an email every time a user gets assigned a new security role. There as a system N:N relationship between users and system roles called “systemuserroles_association”. If you wanted to intercept these events, you would have to register a plugin on the “Associate” message.


However, because the Associate message does not allow you to specify the primary or secondary entities then you’d need to register it as a “global plugin” (no primary entity). Therefore, your plugin will be triggered every time any 2 (or more) entities get associated by any native N:N relationship. For this reason, you must make your plugin smart enough to be able to identify if the current association is the one you are interested in or not. For the user / security roles example, you would need to add the following code to your plugin:

IExecutionContext context = (IExecutionContext)serviceProvider.GetService(typeof(IExecutionContext));
string useRoleRelationshipName = "systemuserroles_association";
            
if (context.InputParameters.Contains("Relationship") &&
    useRoleRelationshipName.Equals((context.InputParameters["Relationship"] as string), StringComparison.InvariantCultureIgnoreCase))
{
    // Assigning new role(s) to a user
    // Send a notification email
}
else
{
    // Another association, not interested, exit
    return;
}


You will need to filter in your plugin to only execute when the “Associate” is called for the relationship(s) in question.


The problem with native N:N relationships is that while you can register plugins, it is not possible to trigger workflows on these kind of events. The only workaround would be that you register a plugin which will then start a specific workflow on-demand, but the workflow engine will not be able to automatically start a workflow when you associate 2 entities with a native N:N relationship.


Custom Intersect Entities


In this case, triggering custom logic is easy because you only need to register a plugin on Create of your custom intersect entity. You will also be able to register workflows in this case because workflows can trigger on Create of any entity. Workflows will also have the ability to access fields from both sides of the relationship since each side is considered a “parent” entity of the intersect entity and you can always access parent entities and their fields from the workflow designer. This approach would also allow you to easily build queries and reports of your relationships.


This approach gives you in general much more flexibility to implement your relationships and the custom logic behind it. One disadvantage that I find with this approach is that you lose some of the neat OOB functionality to relate records. For example, if you want to associate 150 students with one teacher, you will have to create 150 records of these intersect entities one-by-one, while native N:N relationships allow you to select multiple at once and associate them all with one click. You might also miss the “Add Existing” button that you get with native N:N relationships. There are also more limitations in this approach when you want to show a related view because you would have to show a view of the intersect entity and not a view of the related record type (so things like sorting by fields of the related record type is not possible).


Connections (or Relationships)


These are similar to the custom intersect entities and you can define plugins and workflows on create of connection records. However there are a few subtle differences:


1. Connection Roles are solution aware so you can package them in your solution.
2. The connection entity is used for all connections for all entity types. For custom intersect entities you would need one custom entity per association type (which can be good and bad depending on your scenario).


3. You cannot access the fields of the entities getting connected from the workflow designer (you can do this with custom intersect entities).


4. If you register a workflow on Create of Connection, it will trigger every time any 2 records are connected and you would need some conditions in your workflow to filter out the connections you are not interested in.


5. You can customize the Connection entity but it would be strange to add fields to capture metadata about a specific relationship because the fields will be there for all relationships. In that case you should probably consider customizing your own custom intersect entity.


Note that I would not recommend using the CRM “Relationships” as these have been replaced with a more powerful model (Connections) in CRM 2011.



Conclusion
While it is possible to trigger custom logic on association (or disassociation) of entities, you should consider how you model your N:N relationships in CRM and how that would affect your automation possibilities in terms of plugins and workflows.


Note: If you are interested in triggering custom logic for 1:N or N:1 relationships, this is quite simple: You only need to register a plugin (or a workflow) on the “Update” event of the child entity, so whenever the “parent” field of the child is updated then your plugin / workflow will trigger.

7 comments:

  1. Hi,
    I have implemented the custom logic through JavaScript, this approach does not have any limitations and it works in crm 2011 too.

    crmsaurin.blogspot.com/2012/07/capture-and-handle-event-when-one.html?m=1

    ReplyDelete
  2. Hi hero. Thanks for sharing. Unfortunately that approach uses unsupported customizations which will most likely break in UR12 and future versions of CRM.

    ReplyDelete
  3. Hi Gonzalo
    Agreed, though I have implemented the same
    Javascript approach in a slightly different
    way. Since in CRM 2011 we have Subgrids so
    instead of attaching events on Add Existing/
    Remove buttons which have specific id, We
    just capture the Subgrid Refresh Event in any
    case : Whether 1 or N related records are
    linked or removed.

    ReplyDelete
  4. how to fire custom workflow when record is associated to parent entity

    ReplyDelete
    Replies
    1. this is a simple update on the field which holds the relationship to the parent record

      Delete
  5. Hi Gonz
    So I have a scenario where a contact record has a N:N association using the native system table with another entity and the business wants to make it business required that no contact record can be created without an association first being made. Best way to do this as the contact record needs to be created before an association can be made? I was thinking create a primary lookup field that is mandatory then have a plugin fire which also replicates that value within the system intersect table? What are the possible options for implanting such a mandatory requirement?

    ReplyDelete
    Replies
    1. This is weird, I don't think the requirement makes sense, how can you associate records if they do not exist yet?

      Delete