-
Notifications
You must be signed in to change notification settings - Fork 22
Associations
Associations are relationships between entities. For the scope of this library we work with two subsets of associations: Aggregations & Compositions
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 beforeUpdateUser()
is called, as it is not correct to create newRoles
in anUpdateUser()
operation. -
UpdateUser()
doesn't change fields of Role entity (e.g.:Name
), that's whyRole
fields are not updated.
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.
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.
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).
Aggregation and composition are business concepts so need to be manually defined for each association.
[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; }
}
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();
});
});
});