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

SaveChanges throws exception when SetValues is used and a child object exists in change tracker #35745

Open
rayknlcs opened this issue Mar 6, 2025 · 3 comments

Comments

@rayknlcs
Copy link

rayknlcs commented Mar 6, 2025

Bug description

I'm not really sure if this is a bug or just a change in the EF that caused some issues in our app. Hoping someone may be able to shed some light on it.

I have a method that creates or updates a database record that started throwing an exception when I updated the app from EF 5 to EF 8. Here's the scenario:

A DTO is passed in via an API and used to update a database record. I'm using the SetValues method to update the properties on the entity with the properties on the DTO. Everything works as it should unless I also loop through and add child objects to the entity.

If I assign things manually without using SetValues, EF adds the parent to the database, returns the parent Id, updates the child navigation property and saves the child to the database. Just like it should.

However, if I update the parent using the SetValues method, EF does not return the parent Id and assign it to the navigation property of the child. Instead it throws a foreign key violation exception.

Your code

public class Parent
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public List<Child> Children { get; set; } = [];
    }

    public class Child
    {
        public int Id { get; set; }
        public string Name { get; set; } = "";
        public int ParentId { get; set; }
    }

    public class HomeController : Controller
    {
        private readonly DatabaseContext _databaseContext;

        public HomeController(DatabaseContext context)
        {
            _databaseContext = context;
        }

        public IActionResult Index()
        {
            var parentDto = new Parent
            {
                Id = 0,
                Name = "Test1",
                Children = [new Child { Id = 0, Name = "ChildName1" }]
            };

            var newParent = new Parent();
            _databaseContext.Add(newParent);
            _databaseContext.Entry(newParent).CurrentValues.SetValues(parentDto);
            foreach (var child in parentDto.Children)
            {
                var newChild = new Child();
                newParent.Children.Add(newChild);
                _databaseContext.Entry(newChild).CurrentValues.SetValues(child);
            }
            _databaseContext.SaveChanges();

            return View();
        }
    }

Stack traces


Verbose output


EF Core version

8.0.13

Database provider

No response

Target framework

.NET 8

Operating system

No response

IDE

No response

@rayknlcs
Copy link
Author

rayknlcs commented Mar 6, 2025

This works as expected:

            var newParent = new Parent();
            _databaseContext.Add(newParent);
            //_databaseContext.Entry(newParent).CurrentValues.SetValues(parentDto);

            newParent.Id = parentDto.Id;
            newParent.Name = parentDto.Name;

            foreach (var child in parentDto.Children)
            {
                var newChild = new Child();
                newParent.Children.Add(newChild);
                
                newChild.Id = child.Id;
                newChild.Name = child.Name;
            }
            _databaseContext.SaveChanges();

The SQL generated with using SetValues:

SET NOCOUNT ON;
INSERT INTO [Parent] ([Name])
OUTPUT INSERTED.[Id]
VALUES (@p0);
INSERT INTO [Child] ([Name], [ParentId])
OUTPUT INSERTED.[Id]
VALUES (@p1, @p2);

The SQL generated when manually updating the properties (as shown above)

SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
INSERT INTO [Parent] ([Name])
OUTPUT INSERTED.[Id]
VALUES (@p0);
Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (2ms) [Parameters=[@p1='?' (Size = 4000), @p2='?' (DbType = Int32)], CommandType='Text', CommandTimeout='30']
SET IMPLICIT_TRANSACTIONS OFF;
SET NOCOUNT ON;
INSERT INTO [Child] ([Name], [ParentId])
OUTPUT INSERTED.[Id]
VALUES (@p1, @p2);

@cincuranet
Copy link
Contributor

This issue is lacking enough information for us to be able to fully understand what is happening. Please attach a small, runnable project or post a small, runnable code listing that reproduces what you are seeing so that we can investigate.

@rayknlcs
Copy link
Author

rayknlcs commented Mar 7, 2025

Set Values Test.zip

I've uploaded a small project that shows what I am experiencing. I tested this with EF Core versions 2 through 6 and it works as expected. The change appears with version 7, which is where the uploaded project is. It also throws an exception with version 8.

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

2 participants