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

DbContext.ChangeTracker.HasChanges() not working as expected #14808

Closed
midicine opened this issue Feb 25, 2019 · 4 comments · Fixed by #19477
Closed

DbContext.ChangeTracker.HasChanges() not working as expected #14808

midicine opened this issue Feb 25, 2019 · 4 comments · Fixed by #19477
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported punted-for-3.0 type-bug
Milestone

Comments

@midicine
Copy link

midicine commented Feb 25, 2019

We are not able to find any Added/Updated/Deleted entities from ChangeTracker.Entries(), but ChangeTracker.HasChanges() shows TRUE

Exception message: N/A
Stack trace: N/A

Steps to reproduce

using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace TestEntityFrameworkCore
{
    public class Booking
    {
        public int Id { get; set; }
        public Status Status { get; set; }
    }

    public class Status
    {
        public string Value { get; set; }
    }

    public class SqlDbContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            => optionsBuilder
                .UseSqlServer(@"Server=.;Database=dbname;Trusted_Connection=True;");

        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<Booking>(m =>
            {
                m.ToTable("Booking", "dbo");
                m.HasKey(x => x.Id);
                m.OwnsOne(x => x.Status, nav =>
                {
                    nav.Property(x => x.Value)
                        .HasColumnName("Status");
                });
            });
        }
    }

    public class Program
    {
        public static async Task Main()
        {
            using (var context = new SqlDbContext())
            {
                var booking = await context.Set<Booking>()
                    .FirstOrDefaultAsync(x => x.Id == 1897229);
                booking.Status = new Status() { Value = "Cancelled" };
                context.Entry(booking).State = EntityState.Unchanged;
                context.Entry(booking.Status).State = EntityState.Unchanged;
                // we expect there are no changes in context.ChangeTracker()
                if (context.ChangeTracker.HasChanges())
                {
                    // but context.ChangeTracker shows there are changes(), and we found ChangedCount of StateManager in ChangeTracker had value "1"
                    var test = context.ChangeTracker.Entries().Where(x => x.State != EntityState.Unchanged).ToList();
                    Console.WriteLine(test.Count); // output 0
                }
                await context.SaveChangesAsync(); // and will not execute any db command here
            }
        }
    }
}

Further technical details

EF Core version: 2.2.2
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10 Pro 1809
IDE: Visual Studio 2017 15.9.7

@ajcvickers
Copy link
Member

Note for triage: I was able to reproduce this.

@ajcvickers ajcvickers self-assigned this Feb 25, 2019
@ajcvickers ajcvickers added this to the 3.0.0 milestone Mar 1, 2019
@ajcvickers ajcvickers modified the milestones: 3.0.0, Backlog Jun 28, 2019
@ajcvickers ajcvickers modified the milestones: Backlog, 5.0.0 Nov 13, 2019
@ajcvickers
Copy link
Member

@AndriySvyryd The issue here is that we create a special additional entry to handle owned type replacement--this is the state manager contents when HasChanges is called:

Booking {Id: 1} Unchanged
  Id: 1 PK
  Status: {BookingId: 1}
Status (Shared) {BookingId: 1} Deleted
  BookingId: 1 PK FK
  Value: 'S'
Status (Shared) {BookingId: 1} Unchanged
  BookingId: 1 PK FK
  Value: 'Cancelled'

So it's clear why HasChanges returns true. But if HasChanges is intended to indicate whether or not changes need to be made to the database, then this is wrong.

On the other hand, if HasChanges is intended to only indicate that there might be changes made to the database, then this is okay. Although HasChanges is then less useful.

Thoughts?

@AndriySvyryd
Copy link
Member

The way it's described I think it should return false. If you want to detect this case you'd need to use different API.

@AndriySvyryd
Copy link
Member

AndriySvyryd commented Jan 3, 2020

But perhaps the way to fix this is that setting the state to unchanged should detach the deleted shared entry and copy over the original values.

@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 Jan 3, 2020
@ajcvickers ajcvickers modified the milestones: 5.0.0, 5.0.0-preview1 Mar 13, 2020
@ajcvickers ajcvickers modified the milestones: 5.0.0-preview1, 5.0.0 Nov 7, 2020
@ajcvickers ajcvickers removed their assignment Sep 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported punted-for-3.0 type-bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants