{"action":"created","issue":{"url":"https://api.github.com/repos/aspnet/EntityFramework/issues/2726","labels_url":"https://api.github.com/repos/aspnet/EntityFramework/issues/2726/labels{/name}","comments_url":"https://api.github.com/repos/aspnet/EntityFramework/issues/2726/comments","events_url":"https://api.github.com/repos/aspnet/EntityFramework/issues/2726/events","html_url":"https://github.com/aspnet/EntityFramework/issues/2726","id":97584970,"number":2726,"title":"Add() behavior with graphs","user":{"login":"divega","id":1253414,"avatar_url":"https://avatars.githubusercontent.com/u/1253414?v=3","gravatar_id":"","url":"https://api.github.com/users/divega","html_url":"https://github.com/divega","followers_url":"https://api.github.com/users/divega/followers","following_url":"https://api.github.com/users/divega/following{/other_user}","gists_url":"https://api.github.com/users/divega/gists{/gist_id}","starred_url":"https://api.github.com/users/divega/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/divega/subscriptions","organizations_url":"https://api.github.com/users/divega/orgs","repos_url":"https://api.github.com/users/divega/repos","events_url":"https://api.github.com/users/divega/events{/privacy}","received_events_url":"https://api.github.com/users/divega/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/aspnet/EntityFramework/labels/pri0","name":"pri0","color":"e11d21"},{"url":"https://api.github.com/repos/aspnet/EntityFramework/labels/task","name":"task","color":"d4c5f9"}],"state":"closed","locked":false,"assignee":{"login":"divega","id":1253414,"avatar_url":"https://avatars.githubusercontent.com/u/1253414?v=3","gravatar_id":"","url":"https://api.github.com/users/divega","html_url":"https://github.com/divega","followers_url":"https://api.github.com/users/divega/followers","following_url":"https://api.github.com/users/divega/following{/other_user}","gists_url":"https://api.github.com/users/divega/gists{/gist_id}","starred_url":"https://api.github.com/users/divega/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/divega/subscriptions","organizations_url":"https://api.github.com/users/divega/orgs","repos_url":"https://api.github.com/users/divega/repos","events_url":"https://api.github.com/users/divega/events{/privacy}","received_events_url":"https://api.github.com/users/divega/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/aspnet/EntityFramework/milestones/16","html_url":"https://github.com/aspnet/EntityFramework/milestones/7.0.0-beta8","labels_url":"https://api.github.com/repos/aspnet/EntityFramework/milestones/16/labels","id":1259452,"number":16,"title":"7.0.0-beta8","description":"","creator":{"login":"bricelam","id":1226749,"avatar_url":"https://avatars.githubusercontent.com/u/1226749?v=3","gravatar_id":"","url":"https://api.github.com/users/bricelam","html_url":"https://github.com/bricelam","followers_url":"https://api.github.com/users/bricelam/followers","following_url":"https://api.github.com/users/bricelam/following{/other_user}","gists_url":"https://api.github.com/users/bricelam/gists{/gist_id}","starred_url":"https://api.github.com/users/bricelam/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/bricelam/subscriptions","organizations_url":"https://api.github.com/users/bricelam/orgs","repos_url":"https://api.github.com/users/bricelam/repos","events_url":"https://api.github.com/users/bricelam/events{/privacy}","received_events_url":"https://api.github.com/users/bricelam/received_events","type":"User","site_admin":false},"open_issues":0,"closed_issues":94,"state":"closed","created_at":"2015-08-17T22:02:33Z","updated_at":"2015-11-04T02:05:17Z","due_on":"2015-09-25T07:00:00Z","closed_at":"2015-11-04T02:05:17Z"},"comments":28,"created_at":"2015-07-28T00:26:23Z","updated_at":"2015-11-11T20:39:34Z","closed_at":"2015-09-17T21:34:17Z","body":"Unlike in previous versions of EF, currently calling `Add()` on an object using EF7 won't mark any of its related objects as added. \r\n\r\nWe know this is not the only behavior that customers expect from `Add()` (see #2434 and #2387 as examples), however this is certainly a behavior we wanted to expose because in past versions of EF we had a lot of feedback from customers not expecting certain related objects to be marked as added.\r\n\r\nOn one hand this kind of feedback has been very common among customers trying to work with disconnected graphs: Since EF does not provide a high level API for disconnected graphs, in those situations it is quite usual to use an algorithm that walks the graph and explicitly marks each individual object with a specific state, making it desirable to use an API that won't have any side effects on the state of other objects.\r\n\r\nOn the other hand even customers that used `Add()` in a standard unit of work pattern (and for whom it is very useful that the method automatically adds related objects at once) were often surprised that previous versions of EF would mark the whole graph as added. \r\n\r\nFrom the perspective of those customers we seem to fundamentally lack the ability to differentiate between objects which should be added from those that shouldn't, however there doesn't seem to be a single rationale that informs their expectations. \r\n\r\nAs a general approach, we have talked about having two different APIs for the two different behaviors, e.g.: \r\n\r\n- `Add()` for single objects would be for customers writing lower level logic and needing the finer control\r\n- `Add()` for graphs would be for customers using the context on a unit of work pattern and wanting the API to smartly mark additional objects as added \r\n\r\nWe could simply call them `AddSingleObject()` and `AddGraph()`, but another option is to just make the `Add()` method the one that tries to automatically infer what should be added and have a separate lower level/fine control API that allows to set the state to added on a single object, e.g. what we already have in the EntityEntry.State. \r\n\r\nRegardless of the shape of the API, we need to figure out what behavior to use for `Add()` for graphs. \r\nHere are some options:\r\n\r\n1. __Leverage containment/aggregates:__ \r\n [Aggregate-oriented databases](http://martinfowler.com/bliki/AggregateOrientedDatabase.html) (e.g. document databases) offer a good frame of reference. Adding an aggregate root object inherently adds the whole aggregate but doesn't add anything external to the agregate. \r\n\r\n From an O/RM perspective the key to emulate this behavior would be to have knowledge of the shape of the aggregate. As mentioned in the comments below, [GraphDiff](http://blog.brentmckendrick.com/introducing-graphdiff-for-entity-framework-code-first-allowing-automated-updates-of-a-graph-of-detached-entities/) is an example of an extension for EF6 that layers this type of knowledge on top of the EF model to enable smarter decisions on state transitions, e.g. if a navigation property is \"painted\" with a call to `AssociatedCollection()` or `AssociatedEntity()`, then adds won't spread through the relationship in the direction of the navigation property. On the other hand if the navigation property is touched by a call to `OwnedCollection()` or `OwnedEntity()` then adds will spread through that relationship in the direction of the navigation property.  \r\n\r\n We currently don't have the ability to model aggregates on EF (it something that we have talked about adding in the future, see #1985), but we actually do have at least a rudimentary understanding of child vs. non-child objects, which we use (or are currently planning to use) to drive cascading/orphan deletes for `Remove()`. In that sense @tonysneed offered [interesting feedback](https://github.com/aspnet/EntityFramework/issues/1368#issuecomment-70846496) some time ago saying that he expected the behavior of `Add()` for graphs to be more consistent with `Remove()`:\r\n\r\n > with the vCurrent behavior ... if I add a territory to an employee, then the territory will be marked as Added and EF will attempt to add it to the Territories table. If I want to just add the relationship to an existing territory, then I need to explicitly mark it as Unchanged, which makes sense but isn't all that intuitive. It's also inconsistent with the behavior for removing entities from a relationship, where removing a territory from an employee will not mark it as Deleted, without the need to explicitly mark it as Unchanged.\r\n\r\n We could indeed use this kind of rudimentary \"aggregates by convention\" based on FK relationships as the basis of the behavior of `Add()` for graphs. If we later improve EF to be more aware of aggregates, `Add()` for graphs would automatically pick up the new information. \r\n\r\n2. __Leverage key values:__ \r\n Another approach @rowanmiller suggested to explore is to use key values to determine which objects need to be added. \r\n\r\n Note that basing the rules just on aggregates, the following code would insert a new _Post_, but would assume the associated _Blog_ is existing:\r\n\r\n ``` C#\r\nvar blog = new Blog { Title = \"Blogging from the leaf\" };\r\ncontext.Posts.Add(new Post { Contents = x, Blog = blog});\r\n ``` \r\n In order to get the _Blog_ object also added, you would need to indicate it explicitly:\r\n\r\n ``` C#\r\nvar blog = context.Blogs.Add(new Blog { Title = \"Blogging from the leaf\" });\r\ncontext.Posts.Add(new Post { Contents = x, Blog = blog});\r\n ``` \r\n\r\n Note that if the key of _Blog_ is generated, we would actually have enough information to know whether it is an existing entity by just looking at its key. Indeed, if we leverage key values we can infer what objects need to be Added irrespective of the shape of the aggregate and the direction of relationships: assuming generated keys, the first snippet would add both _Blog_ and _Post_, regardless of the fact that Blog is the principal and Post is being added. \r\n\r\n Note that this would have equivalent semantics to the aggregate approach for a number of cases, e.g. for identifying relationships any child object of an added principal should have temporary keys too and therefore would become added automatically.\r\n\r\n The approach per se won't work well for entities that don't have generated keys as those will look to EF as existing entities. We need to understand if that is ok or whether we would fallback to a different strategy. \r\n\r\n3. __Hybrid:__ \r\n The idea is to figure out a way in which can leverage both aggregates and keys, e.g. leverage keys but fallback to aggregates for objects that don't have generated keys.  \r\n\r\n__Modified objects on the edge__\r\nIt seems that for all cases any dependent entity that we won't add but that points to a principal that is added should be updated to be able to update the FK value."},"comment":{"url":"https://api.github.com/repos/aspnet/EntityFramework/issues/comments/155903478","html_url":"https://github.com/aspnet/EntityFramework/issues/2726#issuecomment-155903478","issue_url":"https://api.github.com/repos/aspnet/EntityFramework/issues/2726","id":155903478,"user":{"login":"Yanal-Yves","id":1196669,"avatar_url":"https://avatars.githubusercontent.com/u/1196669?v=3","gravatar_id":"","url":"https://api.github.com/users/Yanal-Yves","html_url":"https://github.com/Yanal-Yves","followers_url":"https://api.github.com/users/Yanal-Yves/followers","following_url":"https://api.github.com/users/Yanal-Yves/following{/other_user}","gists_url":"https://api.github.com/users/Yanal-Yves/gists{/gist_id}","starred_url":"https://api.github.com/users/Yanal-Yves/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Yanal-Yves/subscriptions","organizations_url":"https://api.github.com/users/Yanal-Yves/orgs","repos_url":"https://api.github.com/users/Yanal-Yves/repos","events_url":"https://api.github.com/users/Yanal-Yves/events{/privacy}","received_events_url":"https://api.github.com/users/Yanal-Yves/received_events","type":"User","site_admin":false},"created_at":"2015-11-11T20:39:34Z","updated_at":"2015-11-11T20:39:34Z","body":"@julielerman, I am not sure to understand, could you share some code that shows what you are explaining in your last comment?\r\nWhat is wrong with what I am doing in my code sample https://github.com/Yanal-Yves/ConsoleApp2 and why is it working as I expect on EF 6 but not on EF7?"}}