dotnet / dotnet/efcore

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

未关闭
#33,653 0 条评论 1 个 reaction 已指派 1 人 已被 @AndriySvyryd 认领 在 GitHub 查看
area-model-building area-save-changes
主要语言
C#
星标
14.8k
派生
3.4k
PR 合并指标
PR 指标待抓取

描述

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);
});
}
}
```

贡献指南

打开贡献指南

评估

这个 Issue 还没有评估数据。

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。