Friday, September 3, 2010

Handling Mappings Where Multiple Relationships Exist Between Two Entities

While running a training session earlier this week, I ran into an interesting issue ... I have a custom entity which has two many-to-one relationships with another entity in the system. For example, custom entity "Project" has two relationships to the "Contact" entity, one being a "Student" relationship and the other a "Supervisor" relationship.

All sounds pretty good so far ... but the problem arises when you create a Project record from a Contact through one of the associated records view.
You would assume that:
  • If you create a Project using the "Project (Supervisor)" relationship, the Contact you are creating this record from would only be mapped to the "Supervisor" relationship field.
  • If you create a Project using the "Project (Student)" relationship, the Contact you are creating this record from would only be mapped to the "Student" relationship field.

BUT ... this is not the case – regardless of which relationship in the navigation menu you use to create the Project record, the Contact you create the record from will default as BOTH "Supervisor" AND "Student" relationships; this obviously causes a few concerns as the Contact cannot supervise his/her own Project!

I investigated this a little further and discovered that against both of the relationships, there were two mappings to the Contact entity – to my surprise, when I attempted to delete the unexpected one in one of the relationships I was told that "One or more of the attribute maps could not be deleted because they are being used by the Microsoft Dynamics CRM Platform".

Investigating even further ... I noticed that the mappings area of the two relationships is actually showing the SAME thing (ie. it appears to be a consolidated view of all of the mappings defined against any one of the relationships) – this means that if I had a mapping on the Supervisor to Project relationship, I would also see this when looking at the Student to Project relationship mappings.

Consequently, regardless of where you create your relationship, ALL of your defined mappings will be implemented – when you look at the URL of a new Project record (created though either of the two relationships) it is also exactly the same.

So how can we work around this with a simple solution?

One way is by restricting the ability to create specific related records.
In our example, this could mean only allowing the creation of Project records from a Contact which is a Student (therefore assuming that the Student is the dominant or primary relationship element, and the Supervisor secondary), this way you can always clear the “Supervisor” relationship onLoad if the Project form is in CREATE mode:

Project onLoad:
// If form is in CREATE mode
if(crmForm.FormType == 1)
    // If "Supervisor" lookup has been defaulted though a mapping
    if(crmForm.all.new_supervisorcontactid.DataValue != null)
        // Clear the default value of the "Supervisor" lookup
        crmForm.all.new_supervisorcontactid.DataValue = null;
If you go with this approach, also remember to hide the "Project (Supervisor)" relationship from the left navigation of the Contact form so that you do not confuse the users ...

Contact onLoad:
// Hide the "Projects (Supervisor)" Left Navigation option
    document.getElementById('nav_new_supervisorcontact_new_project').style.display = "none";
Another way to do this is by having some sort of "Role" attribute on the Contact to indicate whether they are a Student or a Supervisor (note that this approach will not be valuable if a Contact can be both):

On load of a new Project record (in create mode) check the "Role" value of the associated Contact – it does not matter which lookup you use as they will be the same:
  • If the "Role" value is set to "Student", then you know to clear the value of the "Supervisor" lookup.
  • If the "Role" value is set to "Supervisor", then you know to clear the value of the "Student" lookup.

Checking values on the Contact form could be done by either calling the crm web service (retrieve multiple) or using window.opener checks.

An interesting quirk to consider when thinking about and setting up your relationships ...


  1. I'm running into the same issue with a custom entity called ASSET which has multiple relationships to ACCOUNTS: the Owner, the Supplier and the Manufacturer. I don't want the same account to be associated as the owner, supplier and manufacturer so I'm going to copy your code to clear those associations in the OnLoad on create event. Thanks!

  2. I can't believe I have not tripped over this nasty behavior before now. I am appalled and frustrated.