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

Consider making it unnecessary to call AsNoTracking() on queries that return owned entities without their owners #17836

Closed
divega opened this issue Sep 13, 2019 · 4 comments

Comments

@divega
Copy link
Contributor

divega commented Sep 13, 2019

Per a decision we made for the 3.0 release, we require that queries that return owned entities without their owners are explicitly marked as non-tracking. If AsNoTracking() isn't included in the query, we throw.

Our reasoning was that the exception helps make the users aware that owned entities cannot be tracked without their owners, in case their intention wasn't that. We also said (as we usually do) that if we get enough feedback that this doesn't lead to the best possible experience, we will reconsider.

Since then, we have started seeing some feedback indicating that this decision may cause more pain than good, and complains that we didn't record it as a breaking change. One example I can find right now is #17617 (comment), but there have been others on email.

From my point of view it is too early and we have few data points to justify revisiting now, but I decided to create this issue anyway to facilitate collecting the feedback.

Rather than making another one-off decision on this, I would like to propose we try to come up with a coherent set of minimal rules around tracking behavior of queries that can describe what happens on all the known cases, and then we try to stick as much as possible to these rules.

Here is an early attempt at this:

From the tracking vs. no-tracking perspective, currently query results can contain several types of objects:

  1. Trackable objects, like regular entity types obtained from query roots (DbSet instances and in the future possibly table-valued functions)
  2. Non-trackable objects, like key-less entities, non-mapped types, anonymous types, primitive types, etc.
  3. Objects that are trackable conditionally to the owner object being present in the same result, like owned entities and projected collection navigation properties
  4. Objects that are mapped as trackable (e.g. regular entity types or owned types) but that become non-trackable because instead of being materialized by EF Core from a query root, they are created explicitly in the query via a constructor or method invocation.

And here is an attempt at some principles that describe what happens with these objects in tracking and non tracking queries:

a. Query results can contain arbitrary combinations of trackable and non-trackable objects. The only constraint is that while non-trackable objects can reference or contain/be the vessel for trackable objects, trackable objects cannot reference or contain non-trackable objects.

b. Whatever shape is returned, a tracking query will make sure that all the returned trackable and conditionally trackable objects connected to their parents will be tracked if they are materialized directly from query roots.

c. In general, a tracking query can also return non-trackable objects. For example, a query can return a DTO that references entity types. The DTO will not be tracked, but the entity types contained by it will be tracked. Another example: a query that returns a Product entities constructed with new alongside Category entities from a query root, will track the Category entities but not the Product entities.

d. It is not an error for a tracked query to not return trackable objects. If there is nothing to track, it becomes implicitly a non-tracking query.

Last, we need a principle that governs what happens when a query returns conditionally tracked objects without their owners. Here are some options:

  1. Silently return the objects without tracking them (what we today for collections navigation properties when they are projected on their own)
  2. Throw (what we do today for owned entities that are projected on their own)
  3. Figure out a way to track these objects.
@marcwittke
Copy link

Since owned entities fit best with what is called a value object in DDD, that by law should be designed as immutable, returning it implicitly as not tracked would be the correct solution IMHO.

@smitpatel smitpatel removed this from the Backlog milestone Dec 14, 2019
@AndriySvyryd
Copy link
Member

@marcwittke Owned types are not a good fit for value types as they have identity and cannot be assigned to a different type without being marked as modified. A much better fit is multi-column value converters

@marcwittke
Copy link

while I agree, multi column value converters aren't a thing yet. Currently in EF3.1 the representation of value objects as owned entities is the best match. The identity, though, is an implementation detail when it comes to mapping the model to the database, so there is no need to pollute your domain with it, which makes it at least acceptable. Assigning a value object to a different entity is no DDD concept, AFAIK.

@smitpatel
Copy link
Member

@ajcvickers @JeremyLikness to give this a read and close as duplicate of #18876 which has detailed discussion which happened in team.

@ajcvickers ajcvickers removed this from the Backlog milestone Nov 10, 2021
@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
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

5 participants