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

Entity already tracked exception for entity with multiple references to same navigation property #20116

Closed
vflame opened this issue Mar 1, 2020 · 8 comments
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@vflame
Copy link

vflame commented Mar 1, 2020

When trying to attach/update an entity with multiple references to the same entity (and included with Include/ThenInclude), EF core throws an exception indicating the entity is already tracked.

For example:

  • A rental have a navigation to a User.
  • A rental has multiple reservations.
  • Each reservation has a navigation to the same User as the parent rental.

I can work around this by using a new context to materialize the entity and then making the changes on that entity; or using context.Entry(rental).Reload(); however, is it possible to context.Attach or context.Update disconnected entities with multiple references to a same entity? Why or why not?

Steps to reproduce

    class Program
    {
        static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();
            var ctx = host.Services.GetRequiredService<MyContext>();
            ctx.Database.Migrate();

            //seed data
            var seedUser = new User()
            {
                Id = "10",
                UserName = "SeedUser"
            };
            var seedReservation = new ReservationSlot()
            {
                Id = "15",
                User = seedUser
            };
            var seedRental = new Rental()
            {
                User = seedUser,
                Id = "5",
                Modified = DateTime.Now,
                Reservations = new List<ReservationSlot>() { seedReservation }
            };
            ctx.Rentals.Add(seedRental);
            ctx.SaveChanges();
            ctx.Dispose();

            var newCtx1 = host.Services.GetService<IServiceScopeFactory>().CreateScope().ServiceProvider.GetRequiredService<MyContext>();

            //materialize disconnected entity
            var rentals = newCtx1.Rentals
                .Include(p => p.User)
                .Include(p => p.Reservations)
                    .ThenInclude(p => p.User).AsNoTracking().ToList();

            newCtx1.Dispose();



            var rental = rentals.First();
            //attach disconnected entity
            var newCtx2 = host.Services.GetService<IServiceScopeFactory>().CreateScope().ServiceProvider.GetRequiredService<MyContext>();

            //exception thrown for entity "User" being tracked more than once.
            newCtx2.Attach(rental);

            rental.Modified = DateTime.Now;
            newCtx2.SaveChanges();

            Console.ReadKey();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                services.AddDbContext<MyContext>(o => o.UseSqlite("Data Source=blogging.db"));
            });

    }

    public class MyContext : DbContext
    {
        public MyContext(DbContextOptions<MyContext> options) : base(options)
        {
        }
        public DbSet<Rental> Rentals { get; set; }
    }
    public class Rental
    {
        public string Id { get; set; }
        public List<ReservationSlot> Reservations { get; set; }

        public User User { get; set; }
        public DateTime Modified { get; set; }
    }

    public class ReservationSlot
    {
        public string Id { get; set; }
        public User User { get; set; }
    }

    public class User
    {
        public string Id { get; set; }
        public string UserName { get; set; }
    }

Alternatively, trying to seed the data with multiple references to the same User also raises the exception:

           //seed data
            var seedUser = new User()
            {
                Id = "10",
                UserName = "SeedUser"
            };            

            var seedReservation1 = new ReservationSlot()
            {
                Id = "15",
                User = seedUser
            };
            var seedReservation2 = new ReservationSlot()
            {
                Id = "16",
                User = seedUser
            };
            var seedRental = new Rental()
            {
                User = seedUser,
                Id = "5",
                Modified = DateTime.Now,
                Reservations = new List<ReservationSlot>() { seedReservation1, seedReservation2 }
            };
            ctx.Rentals.Add(seedRental); //exception thrown here for user already being tracked
            ctx.SaveChanges();
            ctx.Dispose();

Can EF core detect the same primary key and treat the entity as the same for tracking purposes?

Further technical details

EF Core version: 3.1.2
Database provider: npgsql
Target framework: .net core 3.1
Operating system: Win 10
IDE: vs 2019

@vflame vflame added the type-bug label Mar 1, 2020
@jprsilva
Copy link

jprsilva commented Mar 2, 2020

Don't know if somehow this is also related to this issue:
#19984
When we have two entities with the same Id in an object graph that we want to attach do EFCore.
In your case the User entity (in my case Author entity).

@vflame
Copy link
Author

vflame commented Mar 2, 2020

Don't know if somehow this is also related to this issue:
#19984
When we have two entities with the same Id in an object graph that we want to attach do EFCore.
In your case the User entity (in my case Author entity).

Sounds like the same issue. I can provide both projects, one .net core 2.0 and one .net core 3.0 that produces this issue if needed. I think EF Core should be able to detect that the same primary key should map to the same entity.

@ajcvickers
Copy link
Member

@vflame

is it possible to context.Attach or context.Update disconnected entities with multiple references to a same entity? Why or why not?

This is not supported because in general resolution of conflicts between two or more different instances in the same graph is very ambiguous. Property values may be different, but more importantly, for anything other than a single entity with no relationships, the connections in the graph are also different.

@ajcvickers
Copy link
Member

See also #20124

@yodasad
Copy link

yodasad commented Apr 7, 2022

Is there a workaround for this case

            var seedRental = new Rental()
            {
                User = seedUser,
                Id = "5",
                Modified = DateTime.Now,
                Reservations = new List<ReservationSlot>() { seedReservation1, seedReservation2 }
            };
            ctx.Rentals.Add(seedRental); //exception thrown here for user already being tracked

?

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
@buttch
Copy link

buttch commented Dec 9, 2022

@vflame

is it possible to context.Attach or context.Update disconnected entities with multiple references to a same entity? Why or why not?

This is not supported because in general resolution of conflicts between two or more different instances in the same graph is very ambiguous. Property values may be different, but more importantly, for anything other than a single entity with no relationships, the connections in the graph are also different.

What in the case that in two different places in graph we are using exactly the same instance of entity. There is no way to have other properties and relation in such case, right ? I think I should not throw exception in such case.

@ajcvickers
Copy link
Member

@buttch The exception is only thrown when more than one instance is found.

@buttch
Copy link

buttch commented Dec 9, 2022

@buttch The exception is only thrown when more than one instance is found.

@ajcvickers you are right. I'm sorry for confusion. I had to double check.
I was checking this yesterday and probably did something wrongly that's why I though that I have same instance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

5 participants