Saving a graph of entities doesn't work for depth greater than 1
Type of issue
Code doesn't work
Description
I am trying to save a graph just like in the example shown. But there are multiple issues with the example:
- whatever I do, it throws an error when saving the
Post, because the executedINSERThas stillBlogId=0 - providing the Blog to the Post didn't help
- using Sync/Async processing didn't change anything
- the Post has many objects that are expected to be not null, yet aren't required to be set in constructor - this generates compilator warnings
Page URL
https://learn.microsoft.com/en-us/ef/core/saving/related-data#adding-a-graph-of-new-entities
Content source URL
https://github.com/dotnet/EntityFramework.Docs/blob/main/entity-framework/core/saving/related-data.md
Document Version Independent Id
c7517e7d-f011-a24c-53da-913771913998
Article author
@ajcvickers
I tried to reproduce this, and I initially received the following error
Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'Content', table 'EFDocs.dbo.Posts'; column does not allow nulls. UPDATE fails.
This occurs because I created a new project with Nullable reference types enabled. Reading the comment "this generates compilator warnings", I expect this is also the case for you.
To fix the example, either remove the <Nullable>enable</Nullable> config in your csproj file, or set the content of the posts.
using (var context = new BloggingContext())
{
var blog = new Blog
{
Url = "http://blogs.msdn.com/dotnet",
Posts = new List<Post>
{
new Post { Title = "Intro to C#", Content = string.Empty},
new Post { Title = "Intro to VB.NET", Content = ""},
new Post { Title = "Intro to F#", Content = "Intro to F#"},
}
};
context.Blogs.Add(blog);
context.SaveChanges();
}
If wanted, I can update the docs by assigning a value to the content properties.
The primary issue is rather that there is no clarification what happens with the post.BlogId - does it just magically figure out it is from the object above? Also that part in particular did not work for me - I wasn't able to save it like that and instead it always threw an error about breaking FK constraint - precisely because the posts didn't get the BlogId set.
@litoj it's normal that the entity has id 0, during the insert it should receive the appropriate id.
For example, with Sql Server the Id column will be the Primary Key, which is auto incremented ([BlogId] [int] IDENTITY(1,1) NOT NULL)
Yes, but how does the post.BlogId get set? Because in my tests it didn't and that's why I got the errors.
Sorry @litoj , I don't know - normally, that should also work automatically.
I resolved to using a workaround by saving each entity manually. After getting the parent entity ID by saving it, I only then added the child collection to it and then it worked.
After finding out only the root entity needs to get saved separately, I simplified the code to this:
// duplicate `entity` while duplicating all of its children (.Bundles and .Bundles.Slots)
// ...
await DbContext.TestTemplate.AddAsync(entity);
var bundles = entity.Bundles;
entity.Bundles = [];
await DbContext.SaveChangesAsync();
entity.Bundles = bundles;
await DbContext.SaveChangesAsync();
In my previous comment, I actually am using this feature successfully, but only for the grandchildren (Bundles->Slots). It seems that it works fine for one layer of children, but doesn't cascade for multiple layers (Template->Bundles->Slots).
Is that maybe something that is planned to enable?