Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Take advantage of ownership to enable aggregate behaviors in model #1985

Open
13 tasks
kennytordeur opened this issue Apr 3, 2015 · 20 comments
Open
13 tasks

Comments

@kennytordeur
Copy link

kennytordeur commented Apr 3, 2015

This is a grouping of related issues. Feel free to vote (👍) for this issue to indicate that this is an area that you think we should spend time on, but consider also voting for individual issues for things you consider especially important.


This an "epic" issue for the theme of automatic aggregate behaviors in the model based on ownership. Specific pieces of work will be tracked by linked issues.

Backlog

Original issue

Define included properties

When you get an entity from the database and you also want to get a "child entity", you need to pass that "child object" as an expression in order to also get it from the database (when not using lazy loading).

public class Parent
{
    public Child Child { get; set;}
}
public class Child
{   
}

var dataContext = new DataContext();
var parents = dataContext.Parents.Include(p => p.Child).ToList();

Would it be possible to add an new attribute that will be read by the datacontext and add the include statements automatic?

public class Parent
{
   [Include]
    public Child Child { get; set;}
}
public class Child
{   
}

var dataContext = new DataContext();
var parents = dataContext.Parents.ToList();  // Child entity is also read from the database
@popcatalin81
Copy link

@kennytordeur this is a very dangerous feature. In a large application someone would "think" they always want to load B & C together with A. However in other parts of the code this creates issues due to the fact that it creates overly complex queries (perf) or loads more than necessary (attach/detach scenarios over the wire/web services) or breaks some logic IE: someone checks if B is not loaded (== null) then loads B and B1+B2 etc (an entire graph) after this change application is broken because B is loaded but not B1+B2.

Also there are alternative solutions to this, like:
A repository pattern:
List<Parent> GetParents(){return datacontext.Parents.Include(p => p.Child);}

Which is also more flexible because it allows you to customize the query with more than just includes.

@divega
Copy link
Contributor

divega commented Apr 18, 2015

@kennytordeur Your request maps to some of our thinking around being able to declaratively define the shapes of aggregates in an EF model. We don't know exactly how this feature would look like or the timeframe in which we will try to tackle it. In the meantime I will move your suggestion to our backlog, since it seems to provide one way to define an aggregate that we could consider.

While I agree with @popcatalin81 that in many applications it is dangerous to assume that aggregates are fixed and always need to be loaded together, I also believe that many applications can take advantage of this behavior and be constrained to it. After all, loading full aggregates only is the primary behavior you get when you use a document database. Of course if the database is relational, the overhead of loading from multiple tables is higher, but at least in EF Core the queries that we would generate are much simpler than in EF6.

As @popcatalin81 mentioned, writing repository methods that execute queries with calls to Include can address the requirement of loading fixed shapes of graphs without sacrificing the fine grained control. However a more declarative way of specifying the shape of the aggregate would allow for other automatic behavior, e.g.:

  • A delete orphans policy can be automatically applied to non root entities in the aggregate.
  • Smarter logic can be applied to infer the intended state of objects while merging a detached graph into a context: non root objects within the aggregate that are no longer reachable can be marked as deleted, while non root objects that appear for the first time within the aggregate being merged can be marked as added. The same assumptions can't be made with the same confidence across aggregate boundaries.

@divega
Copy link
Contributor

divega commented Apr 18, 2015

cc @rowanmiller in case he knows if we already have something covering aggregates in the backlog (I couldn't find it) and to suggest whether we should have it as a separate item to this.

@rowanmiller
Copy link
Contributor

I thought we had something... but I can't find it... so we should just keep this one open to track it 😄

@divega divega added this to the Backlog milestone Apr 20, 2015
@divega divega changed the title Define included properties Allow for declaring aggregates in the model (e.g. defining included properties or by some other means) Apr 20, 2015
@divega
Copy link
Contributor

divega commented Apr 20, 2015

Thanks. I moved it to backlog and changed the title to make it more general about aggregates.

@kennytordeur
Copy link
Author

Thanks. It would be nice to define fetch strategies for aggregates.

@GrantErickson
Copy link

I have worked around this issue using extension methods that include a defined subset of the children I want to load.

public static IQueryable<Adult> IncludeAll (this IQueryable<Adult> query)
{
    return query.Include(f => f.Relationships).ThenInclude(f => f.Child)
        .Include(f => f.CreatedBy)
        .Include(f => f.ModifiedBy);
}

@marcwittke
Copy link

I really, really liked the idea implemented in this package for EF6. Basically it's using expressions to define what is part of the aggregate and what is referencing other aggregates of the model, e.g.:

context.UpdateGraph(order, map => map
    .OwnedCollection(ord => ord.LineItems)
    .AssociatedItem(ord => ord.Customer));

Unfortunately the project is a) abandoned and b) not ported to EF Core. A package inspired by this exists, but it seems to be in an early unstable phase.

@HappyNomad
Copy link

HappyNomad commented Nov 3, 2016

Just because an entity's property is an aggregate part of the entity, doesn't mean you always want to include it. But sometimes you do. Instead of [Include], consider introducing [Part] to indicate a part-whole relationship. After appropriately dispersing [Part] throughout your model, you query like this:

var parents = dataContext.Parents.IncludeParts().ToList();

This recursively loads parts and parts of parts, etc..

Also consider adding a RequiresFilter property to [Part] like this:

public class PartAttribute : Attribute
{
    public bool RequiresFilter { get; set; }
}

Setting this property to true indicates that a filter must be specified for the property as part of a query with IncludeParts, or else an exception is thrown upon loading.

@divega
Copy link
Contributor

divega commented May 17, 2018

Note for triage: @bricelam called out that most of what is described in this issue is either implemented already (owned entities, [Owned] attributes) or captured elsewhere:

I would like to propose that we close this and make sure additional issues exist for other aggregate goodness, for example:

  • Delete orphans/cascade delete policy can be applied by convention to owned entities (if it isn't the case already).
  • Smarter logic can be applied to infer the intended state of objects while merging a detached graph into a context: non root objects within the aggregate that are no longer reachable can be marked as deleted, while non root objects that appear for the first time within the aggregate being merged can be marked as added. The same assumptions can't be made with the same confidence across aggregate boundaries.
  • Concurrency control could be delegated to the aggregate root.

@ajcvickers ajcvickers changed the title Allow for declaring aggregates in the model (e.g. defining included properties or by some other means) Take advantage of aggregate semantics defined in model. May 18, 2018
@ajcvickers ajcvickers added this to the 3.0.0 milestone May 21, 2018
@ajcvickers
Copy link
Member

See #24267, which is about determining whether or not an aggregate has been modified.

@ajcvickers
Copy link
Member

See also discussion in #24535.

@ajcvickers ajcvickers removed this from the Epics milestone Nov 2, 2021
@ajcvickers ajcvickers self-assigned this Nov 3, 2021
@ajcvickers ajcvickers added this to the Backlog milestone Nov 3, 2021
@AndriySvyryd AndriySvyryd modified the milestones: Backlog, Epics Jan 14, 2022
@ajcvickers ajcvickers added the composite-issue A grouping of multiple related issues into one issue label Dec 6, 2022
@ajcvickers ajcvickers modified the milestones: Epics, Backlog Dec 7, 2022
@ajcvickers ajcvickers removed their assignment Aug 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants