What is N:N relationship?
It's relationship between two entities which allows you to associate many records from both sides. Example: N:N between Student and Class
A Student can attend to multiple Classes and a Class can have multiple Students.
N:N relationship is technically stored in intersect table, which has two lookups (StudentId and ClassId).
How to associate records in Dynamics 365 or Power Apps (Model-driven)?
Here is the sample code:
IList<EntityReference> classes;
EntityReference student;
EntityReferenceCollection relatedEntities = new EntityReferenceCollection(classes);
Relationship relationship = new Relationship("tulo_tulo_student_tulo_class");
_organizationService.Associate(
entityName: student.LogicalName,
entityId: student.Id,
relationship: relationship,
relatedEntities: relatedEntities
);
Here is the sample code:
IList<EntityReference> students;
EntityReference classs;
EntityReferenceCollection relatedEntities = new EntityReferenceCollection(students);
Relationship relationship = new Relationship("tulo_tulo_student_tulo_class");
_organizationService.Associate(
entityName: classs.LogicalName,
entityId: classs.Id,
relationship: relationship,
relatedEntities: relatedEntities
);
How will it look like if we have a self-referential (reflexive) n:n relationship?
Let's start with an example. We have documents such as contracts. These contracts can be different types, such as:
One contract could have association to others. Example:
The Framework agreement will be associated with:
and the Implementation agreement will be associated with:
Let's imagine that you have added Contract sub-grid in the Contract form.
What will happen if we use on Framework agreement record Add Existing button and add NDA record? Will the Framework agreement be visible on the NDA record in the Contract sub-grid?
No. It won't be visible. Why? Because each n:n relationship has two sides (A and B). As in the previous example we had related Classes and related Students, that here in the Contract we have both sides on the same Contract entity (related Contracts (A) and related Contracts (B)). We have added on the Framework agreement in (related Contracts A) sub-grid NDA record. It means that on the NDA record on the opposite side of relationship (related Contracts B) we will see the Framework agreement record. Why is it not shown on the form in the sub-grid? Because we have added sub-grid showing related Contracts A and our record is shown in related Contracts B, which were not added as a sub-grid to the form.
Most likely, the customer will not want to have two sub-grids in the Contract form and see associated records that will appear once in the left sub-grid and at other times in the right sub-grid.
What can we do?
Let's write plugin on Associate message! But… how will the system know in which direction we want to associate the records? In previous examples, the system was able to “gues” in which direction we want to associate records based on LogicalName property, but here both records have the same LogicalName.
What happens if we try to execute the code below?
EntityReference fmkAgr;
IList<EntityReference> fmkAgrList = new List<EntityReference>() { fmkAgr };
EntityReference nda;
EntityReferenceCollection relatedEntities = new EntityReferenceCollection(fmkAgrList);
Relationship relationship = new Relationship("tulo_tulo_contract_tulo_contract");
_organizationService.Associate(
entityName: nda.LogicalName,
entityId: nda.Id,
relationship: relationship,
relatedEntities: relatedEntities
);
Entity role must be specified for reflexive relationship ‘tulo_tulo_contract_tulo_contract.’
Solution:
We have to set PrimaryEntityRole on the relationship object, whose type is Nullable<EntityRole>, where EntityRole is enum with two options:
and the code will look:
EntityReference fmkAgr;
IList<EntityReference> fmkAgrList = new List<EntityReference>() { fmkAgr };
EntityReference nda;
EntityReferenceCollection relatedEntities = new EntityReferenceCollection(fmkAgrList);
Relationship relationship = new Relationship("tulo_tulo_contract_tulo_contract");
relationship.PrimaryEntityRole = EntityRole.Referenced;
// ... or relationship.PrimaryEntityRole = EntityRole.Referencing;
_organizationService.Associate(
entityName: nda.LogicalName,
entityId: nda.Id,
relationship: relationship,
relatedEntities: relatedEntities
);