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

New entity is not saved under certain conditions #26937

Closed
Tracked by #22954
tikhomirovp opened this issue Dec 8, 2021 · 2 comments · Fixed by #28395
Closed
Tracked by #22954

New entity is not saved under certain conditions #26937

tikhomirovp opened this issue Dec 8, 2021 · 2 comments · Fixed by #28395
Assignees
Labels
area-change-tracking closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Milestone

Comments

@tikhomirovp
Copy link

tikhomirovp commented Dec 8, 2021

We have a similar descendant from the DbContext:

using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;

namespace DetachedEntity {
    public class OneToManyDbContext : DbContext {
        public DbSet<OneToMany_Many> OneToMany_Many { get; set; }
        public DbSet<OneToMany_One> OneToMany_One { get; set; }
        protected override void OnModelCreating(ModelBuilder modelBuilder) {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<OneToMany_Many>()
                .HasOne(p => p.One)
                .WithMany(p => p.Collection)
                .OnDelete(DeleteBehavior.ClientCascade);
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseInMemoryDatabase("InMemory");
        }
    }
    public class OneToMany_One {
        public OneToMany_One() {
            Collection = new List<OneToMany_Many>();
        }
        public int Id { get; set; }
        public List<OneToMany_Many> Collection { get; set; }
    }
    public class OneToMany_Many {
        public int Id { get; set; }
        public OneToMany_One One { get; set; }
    }
}

In the program, we are trying to create an entity as follows:

using System.Linq;

namespace DetachedEntity {
    class Program {
        static void Main(string[] args) {
            using(OneToManyDbContext context = new OneToManyDbContext()) {
                OneToMany_One one = new OneToMany_One();
                OneToMany_Many many = new OneToMany_Many();
                context.Attach(many);
                many.One = one;                
                var entry1 = context.Entry(many);
                //entry1.State == Microsoft.EntityFrameworkCore.EntityState.Added
                many.One = null;
                 var entry2 = context.Entry(many);
                //entry2.State == Microsoft.EntityFrameworkCore.EntityState.Detached
                context.SaveChanges(); // the many entity is not saved
            }
            using(OneToManyDbContext context = new OneToManyDbContext()) {
                var manyCount = context.OneToMany_Many.Count(); //0
                var oneCount = context.OneToMany_One.Count(); //1
            }
        }
    }
}

Current behavior:
The entity 'Many' is not saved, the entity 'One' is saved.

Expected behavior:
The entity 'Many' is saved, the entity 'One' is not saved.

If you do not explicitly detect change after changing the reference property 'One', everything works as expected:

using System.Linq;

namespace DetachedEntity {
    class Program {
        static void Main(string[] args) {
            using(OneToManyDbContext context = new OneToManyDbContext()) {
                OneToMany_One one = new OneToMany_One();
                OneToMany_Many many = new OneToMany_Many();
                context.Attach(many);
                many.One = one;                
                many.One = null;
                context.SaveChanges(); // the many entity is saved
            }
            using(OneToManyDbContext context = new OneToManyDbContext()) {
                var manyCount = context.OneToMany_Many.Count(); //1
                var oneCount = context.OneToMany_One.Count(); //0
            }
        }
    }
}

This behavior is reproduced in version 5.0.12.

@ajcvickers
Copy link
Member

@tikhomirovp This is a case where a full DetectChanges is needed for correct behavior. However, it may be a place where we can in the future enhance local change detection to be sufficient.

With full DetectChanges like so:

context.Attach(many);
many.One = one;
context.ChangeTracker.DetectChanges();

many.One = null;
context.ChangeTracker.DetectChanges();

context.SaveChanges(); // the many entity is not saved

the sequence of events is:

  • The dependent (many) is attached, and put in the Added state since it's PK has not been set.
  • The principal (one) is detected, and also put in the Added state for the same reason. At this point, the tracked entities are:
OneToMany_Many {Id: -2147482647} Added
  Id: -2147482647 PK Temporary
  OneId: -2147482647 FK Temporary
  One: {Id: -2147482647}
OneToMany_One {Id: -2147482647} Added
  Id: -2147482647 PK Temporary
  Collection: [{Id: -2147482647}]
  • The relationship between the principal and dependent is severed by setting the navigation property to null. This leaves the dependent orphaned, and since it doesn't yet exist in the database, it is detached. This leaves only the principal still tracked:
OneToMany_One {Id: -2147482647} Added
  Id: -2147482647 PK Temporary
  Collection: []

In your second example, the change to set the navigation property to null is never seen by EF, and hence there are no changes of state.

@ajcvickers
Copy link
Member

In the current code base, local DetectChanges is sufficient for this case; will add a test.

@ajcvickers ajcvickers added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Jul 7, 2022
ajcvickers added a commit that referenced this issue Jul 8, 2022
Fixes #27497

Also tests for already fixed issues:

Fixes #26937
Fixes #26827
@ajcvickers ajcvickers modified the milestones: 7.0.0, 7.0.0-preview7 Jul 10, 2022
@ajcvickers ajcvickers modified the milestones: 7.0.0-preview7, 7.0.0 Nov 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-change-tracking closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants