Aggregate Root support in Entity Framework
Asked Answered
M

2

13

How can we tell Entity Framework about Aggregates?

  1. when saving an aggregate, save entities within the aggregate
  2. when deleting an aggregate, delete entities within the aggregate
  3. raise a concurrency error when two different users attempt to modify two different entities within the same aggreate
  4. when loading an aggregate, provide a consistent point-in-time view of the aggregate even if there is some time delay before we access all entities within the aggregate

(Entity Framework 4.3.1 Code First)

Moray answered 15/6, 2012 at 15:23 Comment(3)
What you want is Event Sourcing cqrs.wordpress.com/documents/events-as-storage-mechanism . EF is pretty much useless here, just an expensive overheadDomiciliate
Thanks. Unfortunately we are not in a environment that would accept event sourcing.Moray
After two years using EF in a Domain Driven Design app: EF is called "Entity Framework" not "Aggregate Root Framework" for a reason.Moray
S
7

EF provides features which allows you defining your aggregates and using them:

  1. This is the most painful part. EF works with entity graphs. If you have an entity like Invoice and this entity has collection of related InvoiceLine entities you can approach it like aggregate. If you are in attached scenario everything works as expected but in detached scenario (either aggregate is not loaded by EF or it is loaded by different context instance) you must attach the aggregate to context instance and tell it exactly what did you changed = set state for every entity and independent association in object graph.
  2. This is handled by cascade delete - if you have related entities loaded, EF will delete them but if you don't you must have cascade delete configured on the relation in the database.
  3. This is handled by concurrency tokens in the database - most commonly either timestamp or rowversion columns.
  4. You must either use eager loading and load all data together at the beginning (= consistent point of view) or you will use lazy loading and in such case you will not have consistent point of view because lazy loading will load current state of relations but it will not update other parts of aggregate you have already loaded (and I consider this as performance killer if you try to implement such refreshing with EF).
Smug answered 16/6, 2012 at 16:41 Comment(5)
#3 - will EF detect conflict when two users modify different InvoiceLines on the same Invoice? #4 - eager loading by using .Include() on every query is painful and error proneMoray
#3 in such case you will have to ensure that changing InvoiceLine will set Invoice to modified state and update of invoice will be performed.Smug
#4 eager loading is maybe painful in coding time but it is explicit and it shows what really happens. You can alway refactor your code to use common query base where eager loading will be defined. Achieving consistent point of view with lazy loading requires consistent requerying of all loaded data from aggregate. Lazy loading can be bad but this will be extremely bad.Smug
Thanks Ladislav. Sounds like we can model real Aggregates with EF though I'd say it isn't directly supported.Moray
#4 - I'd say it is not explicit because we are duplicating information about aggregate-to-child-entity relationships. The .Include() also doesn't help if you navigate to another Aggregate through a lazy-loaded property unless the query eager loads every aggregrate and navigatable aggregate the caller will use.Moray
P
6

I wrote GraphDiff specifically for this purpose. It allows you to define an 'aggregate boundary' on update by providing a fluent mapping. I have used it in cases where I needed to pass detached entity graphs back and forth.

For example:

// Update method of repository
public void Update(Order order)
{
    context.UpdateGraph(order, map => map
        .OwnedCollection(p => p.OrderItems);
}

The above would tell the Entity Framework to update the order entity and also merge the collection of OrderItems. Mapping in this fashion allows us to ensure that the Entity Framework only manages the graph within the bounds that we define on the aggregate and ignores all other properties. It supports optimistic concurrency checking of all entities. It handles much more complicated scenarios and can also handle updating references in many to many scenarios (via AssociatedCollections).

Hope this can be of use.

Parasynapsis answered 7/1, 2014 at 16:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.