dotnet / dotnet/EntityFramework.Docs

ChangeTracker's StateChanged event's changes not being updated

Open
#3,888 8 comments 3 reactions 0 assignees View on GitHub
area-change-tracking
Dominant language
Mermaid
Stars
1.7k
Forks
2k
Avg merge
7d 23h
Merged PRs (30d)
16

Description

## File a bug
Base on this [documentation](https://docs.microsoft.com/en-us/ef/core/logging-events-diagnostics/events) I expect that when saving changes any property manipulation inside the `StatusChanged` event would also be included in the update command generated by EF, done some tests and I can't get it to work, and I'm not sure if that is the expected behavior.

### Testbed
Below is a test snipped that can be run in a toplevel statement.
At the set up value on the database part the ChangedAt property is properly set using the Tracked event.

Simulating normal update entity use case, I retrieve the value from a new context and change its name property, after calling SaveChanges the StatusChanged event is callend and the entity ChangedAt property is updated, but this update is not persisted on the database (or not present in the sql generated when using a SQLServer provider), but still the entity's ChangedAt property is _de facto_ changed.

After that for control I retrieve the same entity again to be sure that the ChangeAt property is not updated in the previous step.

```cs
using Microsoft.EntityFrameworkCore;
CancellationTokenSource tks = new CancellationTokenSource();
DateTime firstRetrieval, valueAfterSaveChanges, secondRetrieval; //control variables

//set up a value on the database
using (Db db = new Db())
{
db.Entities.Add(new Entity()
{
Name = Guid.Empty.ToString(),
});
await db.SaveChangesAsync(tks.Token);
}

//retrieve its value and change another variable triggering state changed
using (Db db = new Db())
{
var entity = await db.Entities.FirstOrDefaultAsync(tks.Token);
firstRetrieval = entity.ChangedAt;

Thread.Sleep(1000); // wait to have a visible changed date, can be skipped/removed

entity.Name = Guid.NewGuid().ToString();
await db.SaveChangesAsync(tks.Token);
valueAfterSaveChanges = entity.ChangedAt;
}

//retrieve value again
using (Db db = new Db())
{
var entity = await db.Entities.FirstOrDefaultAsync(tks.Token);
secondRetrieval = entity.ChangedAt;
}

Console.WriteLine($"{firstRetrieval} value in first retrieval");
Console.WriteLine($"{valueAfterSaveChanges} value after savechanges, updated by state changed event");
Console.WriteLine($"{secondRetrieval} value in second retrieval, should be equals to {valueAfterSaveChanges} {(secondRetrieval != valueAfterSaveChanges ? "but its not" : "")}");

Thread.Sleep(Timeout.Infinite);

public class Db : DbContext
{
public DbSet Entities { get; set; }
public Db()
{
ChangeTracker.StateChanged += UpdateTimes;
ChangeTracker.Tracked += UpdateTimes;
}

private void UpdateTimes(object sender, Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntryEventArgs e)
{
switch (e.Entry.State)
{
case EntityState.Modified:
case EntityState.Added:
(e.Entry.Entity as Entity).ChangedAt = DateTime.Now;
break;
}
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseInMemoryDatabase("teste");
base.OnConfiguring(optionsBuilder);
}

}
public class Entity
{
public Guid Id { get; set; }
public String Name { get; set; }
public DateTime ChangedAt { get; set; }
}
```

OUTPUT
```
info: 15/05/2022 00:03:08.909 CoreEventId.ContextInitialized[10403] (Microsoft.EntityFrameworkCore.Infrastructure)
Entity Framework Core 6.0.5 initialized 'Db' using provider 'Microsoft.EntityFrameworkCore.InMemory:6.0.5' with options: StoreName=nomezinho
info: 15/05/2022 00:03:08.979 InMemoryEventId.ChangesSaved[30100] (Microsoft.EntityFrameworkCore.Update)
Saved 1 entities to in-memory store.
info: 15/05/2022 00:03:08.994 CoreEventId.ContextInitialized[10403] (Microsoft.EntityFrameworkCore.Infrastructure)
Entity Framework Core 6.0.5 initialized 'Db' using provider 'Microsoft.EntityFrameworkCore.InMemory:6.0.5' with options: StoreName=nomezinho
info: 15/05/2022 00:03:10.125 InMemoryEventId.ChangesSaved[30100] (Microsoft.EntityFrameworkCore.Update)
Saved 1 entities to in-memory store.
info: 15/05/2022 00:03:10.141 CoreEventId.ContextInitialized[10403] (Microsoft.EntityFrameworkCore.Infrastructure)
Entity Framework Core 6.0.5 initialized 'Db' using provider 'Microsoft.EntityFrameworkCore.InMemory:6.0.5' with options: StoreName=nomezinho
15/05/2022 00:03:08 value in first retrieval
15/05/2022 00:03:10 value after savechanges, updated by state changed event
15/05/2022 00:03:08 value in second retrieval, should be equals to 15/05/2022 00:03:10 but its not
```

### Include provider and version information

EF Core version: 6.0.5, and 6.0.4
Database provider: `Microsoft.EntityFrameworkCore.SqlServer` and `Microsoft.EntityFrameworkCore.InMemory`
Target framework: .NET 6.0
Operating system: win10-x64 build 19044.1645

IDE:
Microsoft Visual Studio Community 2022 (64-bit) 17.3.0 Preview 1.0

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.