Skip to content

Associations

Leonardo Porro edited this page Feb 12, 2023 · 24 revisions

Associations are relationships between entities. For the scope of this library we work with two subsets of associations: Aggregations & Compositions

Aggregation

Is a weak type of association where each entity has business logic meaning by itself and doesn't need to be linked to another entity. For example User has a Role but both may exist before they are associated and can continue existing if one of them is deleted.

In a map operation, aggregation fields are not updated, and aggregation entity must exist before the call to Map is executed.

In the previous example, let's define an UpdateUser() operation that calls dbContext.Map<User>([some dto..]). It is expected that:

  • All Roles exists before UpdateUser() is called, as it is not correct to create new Roles in an UpdateUser() operation.
  • UpdateUser() doesn't change fields of Role entity (e.g.: Name), that's why Role fields are not updated.

Composition

Is a strong type of association, in which one entity is a root or parent and owns one or more entites which won't have any business value if the parent is removed. For example Invoice owns many InvoiceRow containing the details of each item in a sell operation, so InvoiceRow doesn't have any meaning and can't exist without Invoice. Usually, deleting the Invoice would also delete the associated InvoiceRows, as if it were a whole document.

In a map operation, compositions are updated recursively. Let's define an UpdateInvoice() for the previous example. It is expected that a call to UpdateInvoice() updates the Invoice and its InvoiceRows, as a whole document.

InoviceRows are merged by key, so if the entity exist in:

  • both, the source dto and the target database entity: their fields are merged.
  • the source DTO only, it is created
  • the target database entitiy only, it is deleted.

Graph boundaries

The library maps entity graphs, that are usually a subset of bigger graphs containing more entities. If all entities are associated, the entire database might be one single large graph.

The most important thing about associations is that aggregations define the boundaries of a map operation while compositions define nodes of the graph that will be included in the subset that is being updated.

Diagram

In this example, UpdateUser(), UpdateRole() and UpdateGroup() should be handled as separate map operations and won't update fields of entities out of the scope (i.e.: out of the composition).

Configuration

Aggregation and composition are business concepts so need to be manually defined for each association.

Annotated configuration

[Aggregation] and [Composition] attributes can be used to configure the association type of a property that points to another entity or collection of entities. Attributes reside in Detached.Attributes nuget package and doesn't force to add EntityFramework or any other package to the project containing models (if any).

public class Invoice
{
    public int Id { get; set; }

    [Aggregation]
    public InvoiceType InvoiceType { get; set; }

    [Composition]
    public List<InvoiceRow> Rows { get; set; }
}
Fluent configuration

If attributes are not desired in models, or multiple profiles are needed, then fluent configuration can be used to set the association type.

services.AddDbContext<MainDbContext>(dbContextOptions =>
{
   ...
   dbContextOptions.UseMapping(mappingOptions => {

      mappingOptions.Default(profileOptions => {

          profileOptions.Type<Invoice>()
                        .Member(i => i.InvoiceType).Aggregation()
                        .Member(i => i.Rows).Composition();
      });   
   });         
});