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

Call to change tracker changes unchanged many to many relationship to deleted #26831

Open
Tracked by #22954
luizfbicalho opened this issue Nov 25, 2021 · 3 comments
Open
Tracked by #22954

Comments

@luizfbicalho
Copy link

luizfbicalho commented Nov 25, 2021

I have one update in some entities
I clear a list of entities , then I update it with the new entities (in this examples the new list has the same old entities)

If I call context.ChangeTracker.Entries(); after the list list clear, and before the new entities added, I get that the relationship is deleted

Left {Id: 111} Modified
Right {Id: 151} Unchanged
Right {Id: 152} Unchanged
Right {Id: 153} Unchanged
LeftRight (Dictionary<string, object>) {LeftsId: 111, RightsId: 151} Deleted FK {LeftsId: 111} FK {RightsId: 151}
LeftRight (Dictionary<string, object>) {LeftsId: 111, RightsId: 152} Deleted FK {LeftsId: 111} FK {RightsId: 152}
LeftRight (Dictionary<string, object>) {LeftsId: 111, RightsId: 153} Deleted FK {LeftsId: 111} FK {RightsId: 153}

If I don't call the context.ChangeTracker.Entries(); the result is this

Left {Id: 114} Modified
Right {Id: 155} Unchanged
Right {Id: 156} Unchanged
Right {Id: 157} Unchanged
LeftRight (Dictionary<string, object>) {LeftsId: 114, RightsId: 155} Unchanged FK {LeftsId: 114} FK {RightsId: 155}
LeftRight (Dictionary<string, object>) {LeftsId: 114, RightsId: 156} Unchanged FK {LeftsId: 114} FK {RightsId: 156}
LeftRight (Dictionary<string, object>) {LeftsId: 114, RightsId: 157} Unchanged FK {LeftsId: 114} FK {RightsId: 157}

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;


namespace TesteManyToManyErro
{
    public class Program
    {
        static void Main(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<ModelContext>().UseLazyLoadingProxies()
                .UseSqlServer("Server=.\\sql2019;Database=ManyToMany;Trusted_Connection=false;user id=sa;pwd=sa!2019;MultipleActiveResultSets=true");
            using (var context = new ModelContext(optionsBuilder.Options))
            {
                context.Database.EnsureCreated();
            }

            var leftid = 0;
            var rightid1 = 0;
            var rightid2= 0;
            var rightid3 = 0;

            using (var context = new ModelContext(optionsBuilder.Options))
            {
                var left1 = new Left();
                var left2 = new Left();
                var left3 = new Left();

               var right1 = new Right();
               var right2 = new Right();
               var right3 = new Right();
               var right4 = new Right();

                left1.Rights = new List<Right>();
                left2.Rights = new List<Right>();
                left3.Rights = new List<Right>();

                left1.Rights.Add(right1);
                left1.Rights.Add(right2);
                left1.Rights.Add(right3);

                left2.Rights.Add(right1);
                left2.Rights.Add(right2);
                left2.Rights.Add(right3);

                // create new item
                context.Left.Add(left1);
                context.Left.Add(left2);
                context.Left.Add(left3);

                context.Right.Add(right1);
                context.Right.Add(right2);
                context.Right.Add(right3);
                context.Right.Add(right4);
                // make relation

                //save
                context.SaveChanges();

                leftid = left1.Id;

                rightid1 = right1.Id;
                rightid2 = right2.Id;
                rightid3 = right3.Id;


            }

            using (var context = new ModelContext(optionsBuilder.Options))
            {
                var left = context.Left.Find(leftid);
                context.Entry(left).State = EntityState.Modified;
                var x = left.Rights;
                left.Rights = new List<Right>();

                context.ChangeTracker.Entries();// Comment this line to work fine
                
                var right1 = context.Right.Find(rightid1);

                left.Rights.Add(right1);

                var right2 = context.Right.Find(rightid2);

                left.Rights.Add(right2);

                var right3 = context.Right.Find(rightid3);

                left.Rights.Add(right3);

                foreach (var item in context.ChangeTracker.Entries())
                {
                    Console.WriteLine(item.ToString());
                }

                context.SaveChanges();

                Console.WriteLine($"Left {left.Id}");
                Console.WriteLine($"Rights {left.Rights.Count}");

            }
            // view result

            Console.ReadLine();
        }
    }

    public class ModelContext:DbContext
    {
        public ModelContext(DbContextOptions<ModelContext> options): base(options)
        {
        }

        public DbSet<Right> Right { get; set; }
        public DbSet<Left> Left { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Right>().HasMany(x => x.Lefts).WithMany(x => x.Rights).UsingEntity(a => a.ToTable("LeftRight"));
        }
    }

    [Table("Left")]
    public class Left
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Key]
        public int Id { get; set; }

        public virtual IList<Right> Rights { get; set; }
    }
    [Table("Right")]
    public class Right
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Key]
        public int Id { get; set; }

        public virtual IList<Left> Lefts { get; set; }

    }
}

Include provider and version information

EF Core version: 6.0
Database provider: (Microsoft.EntityFrameworkCore.SqlServer)
Target framework: ( .NET 6.0)
Operating system: Windows 10
IDE: (e.g. Visual Studio 2022 Version 17.1.0 Preview 1.1)

@ajcvickers
Copy link
Member

@luizfbicalho This line:

left.Rights = new List<Right>();

removes all the existing entities from Rights, but requires change detection to happen. See Change Detection. It's possible that improving local detect changes when Find is called could make manual DetectChanges unnecessary here, although that would make it "not work" all the time from your perspective. We will inevstigate.

@luizfbicalho
Copy link
Author

This issue I managed to use the change detection to reproduce another problem that I have

I have an extension on the Newtonsoft.Json to read from the json properties and load the correct entities from the database, this way when I finish loading the entity, it's clear for EF what I have to change.

The problem with the many to many relationship is that I can't refresh them, and it's weird, because I could imagine two scenarios.

  1. the change tracker would have 2 entities for the many to many relation, one in removed state and one in added state
  2. the change tracker would merge the changes onthis relation and end up with the relation or unchanged or modified

But my problem is that It end up as deleted.
Is there something that I can do to bypass this problem?

@luizfbicalho
Copy link
Author

Is there anything that I can do to spped things up in this issue?

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

3 participants