WithOne 实体关系引起 EF Core 自动删除数据

最近遇到了一个 EF Core 的恐怖问题,在添加数据时竟然会自动删除数据库中已存在的数据,经过追查发现是一个多余的实体关系配置引起的。

modelBuilder.Entity<Question>()
    .HasOne(q => q.Owner)
    .WithOne();

罪魁祸首就是上面的 WithOne()

今天写了个非常简单的控制台程序重现了这个问题。

实体类 Question 的定义

public class Question
{
    public int Id { get; set; }
    public string Title { get; set; }
    public int UserId { get; set; }
    public User Owner { get; set; }
}

实体类 User 的定义

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

实体关系配置

modelBuilder.Entity<Question>()
    .HasOne(q => q.Owner)
    .WithOne();

触发问题的实体查询与添加代码

class Program
{
    static async Task Main(string[] args)
    {
        var conn = "server=.;database=question;integrated security=true";

        var host = new HostBuilder()
            .ConfigureServices(services =>
            {
                services.AddDbContext<MyDbContext>(options => options.UseSqlServer(conn));
            }).Build();

        using (var db = host.Services.GetRequiredService<MyDbContext>())
        {
            var newQuestion = new Question
            {
                Title = "test " + DateTime.Now.ToLongDateString(),
                Owner = await db.Set<User>().FirstAsync(u => u.Id == 1)
            };

            var latestQuestion = await db.Set<Question>()
                .Where(q => q.UserId == 1).OrderByDescending(q => q.Id).FirstOrDefaultAsync();

            db.Set<Question>().Add(newQuestion);
            await db.SaveChangesAsync();
        }
    }
}

EF Core 生成的在 INSERT 之前的 DELETE SQL 语句

exec sp_executesql N'SET NOCOUNT ON;
DELETE FROM [Question]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

问题分析(只是个人猜想)

上面的代码中,创建一个新的 Question 实例时,与一个从数据库查询出来的 Id 为 1 的 User 实例进行了关联,此时这个 User 实例进入 EF Core 的跟踪范围,但这个新建的 Question 实例还没被 EF Core 跟踪。后来使用同样的 UserId 从数据库查询 Question ,查询出来的 Question 实例由于 WithOne 实体关系从而与已经被跟踪的 User 实例(因为 UserId 一样)进行了关联。此时被 EF Core 跟踪到的实体状态是:Id 为 1 的 User 实体与从数据库查询得到的 Id 为 x 的 Question 实体进行了一对一关联。而在 db.Set<Question>().Add(newQuestion) 时,EF Core 跟踪到了实体状态的变化 —— User 实体与一个没有 Id 的新 Question 实体关联了,对于这样的状态变化,EF Core 理所当然地做出了“正确的决定” —— 删除之前关联的 Question 实体,添加新的 Question 实体。

解决方法

去掉多条的 WithOne()

示例代码

重现这个问题的完整示例代码:https://github.com/cnblogs-dudu/efcore-unexpected-deletion

原文地址:https://www.cnblogs.com/dudu/p/10616031.html