dotnet / dotnet/efcore

A delete/insert converted to a update allows changing property values that should not changed after insert

Abierto
#33,653 0 comentarios 1 reacción 1 asignado Reclamado por @AndriySvyryd Ver en GitHub
area-model-building area-save-changes
Lenguaje dominante
C#
Estrellas
14.8k
Forks
3.4k
Métricas de merge de PR
Métricas de PR pendientes

Descripción

Originally reported here: https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues/1908
See #30705

The main issue here is that the application is marking an entity as Deleted, and then adding a new instance with the same key as Added. Because of #30705, this gets converted to an update containing this:

```sql
UPDATE [Item] SET [CreatedAt] = @p0, [UpdatedAt] = @p1
```

Before 7.0.3, the behavior was this:

```sql
UPDATE [Item] SET [UpdatedAt] = @p1
```

`UpdatedAt` is marked as `BeforeSaveBehavior` ignore.
`CreatedAt` is marked as `AfterSaveBehavior` ignore.

So, if we treat this as an update, then `UpdatedAt` should be sent, but `CreatedAt` should not.
On the other hand, if this remains a `delete` `insert`, then `CreatedAt` should be sent in the insert, and `UpdatedAt` should not.

Repro:

```C#
using var context = new StoreContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

var item = new Item { Id = "abc", CreatedAt = DateTime.Now.ToString() };
var store = new Store { CreatedAt = DateTime.Now.ToString(), Items = { item }};
context.Add(store);
await context.SaveChangesAsync();
context.ChangeTracker.Clear();

store = await context.Stores.Include(e => e.Items).SingleAsync();

store.CreatedAt = "X";
store.UpdatedAt = "Y";

store.Items = new List()
{ new() { Id = item.Id, CreatedAt = "A", UpdatedAt = "B" } };

// Doing the store update like this instead does not result in the issue:
// store.Items.First().CreatedAt += "+";
// store.Items.First().UpdatedAt += "+";

context.SaveChanges();

public class Item
{
public string Id { get; set; } = null!;
public int StoreId { get; set; }

public string? CreatedAt { get; set; }
public string? UpdatedAt { get; set; }
}

public class Store
{
public int StoreId { get; set; }
public List Items { get; set; } = new();

public string? CreatedAt { get; set; }
public string? UpdatedAt { get; set; }
}

public class StoreContext : DbContext
{
public DbSet Stores => Set();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) =>
optionsBuilder
.LogTo(Console.WriteLine, LogLevel.Information)
.UseSqlServer(
"Data Source=localhost;Database=BuildBlogs;Integrated Security=True;Trust Server Certificate=True;ConnectRetryCount=0");

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(b =>
{
b.Property(x => x.CreatedAt).Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
b.Property(x => x.UpdatedAt).Metadata.SetBeforeSaveBehavior(PropertySaveBehavior.Ignore);
});

modelBuilder.Entity(b =>
{
b.Property(x => x.CreatedAt).Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
b.Property(x => x.UpdatedAt).Metadata.SetBeforeSaveBehavior(PropertySaveBehavior.Ignore);
});
}
}
```

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.