WithOne 實體關係引發 EF Core 自動刪除數據

最近遇到了一個 EF Core 的恐怖問題,在添加數據時居然會自動刪除數據庫中已存在的數據,通過追查發現是一個多餘的實體關係配置引發的。git

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

罪魁禍首就是上面的 WithOne()github

今天寫了個很是簡單的控制檯程序重現了這個問題。sql

實體類 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 的定義async

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

實體關係配置ui

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

觸發問題的實體查詢與添加代碼code

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 語句server

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 實體。blog

解決方法

去掉多條的 WithOne()get

示例代碼

重現這個問題的完整示例代碼:https://github.com/cnblogs-dudu/efcore-unexpected-deletion

相關文章
相關標籤/搜索