【问题标题】:EF Core reference nulled after SaveChangesSaveChanges 后 EF Core 引用为空
【发布时间】:2020-11-03 13:32:17
【问题描述】:

我使用以下版本的 EF Core:

Microsoft.EntityFrameworkCore.Sqlite 5.0.0-rc.2.20475.6

我有以下课程:

public class Garage
{
    public int Id { get; set; }

    public List<Car> Cars { get; set; } = new List<Car>();
}

public class Car
{
    public int Id { get; set; }
}

public class Ferrari : Car
{
    public Special Special { get; set; }
}

public class Special
{
    public int Id { get; set; }
}

public class MyContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite($"Filename=my.db");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Garage>().ToTable("Garage");

        modelBuilder.Entity<Car>().ToTable("Car");

        modelBuilder.Entity<Ferrari>().ToTable("Ferrari");

        modelBuilder.Entity<Special>().ToTable("Special");
    }
}

我有以下代码:

await using var context = new MyContext();

await context.Database.EnsureCreatedAsync();

var garage = new Garage();

context.Add(garage);

await context.SaveChangesAsync();

var ferrari = new Ferrari();

garage.Cars.Add(ferrari);

ferrari.Special = new Special();

Console.WriteLine($"Special reference is null: {(ferrari.Special == null ? "YES" : "NO")}");

await context.SaveChangesAsync();

Console.WriteLine($"Special reference is null: {(ferrari.Special == null ? "YES" : "NO")}");

对 Special 的引用被取消。输出如下:

Special is null: NO
Special is null: YES

你可以自己试试:https://github.com/dagid4/EfCoreBug

在数据库中一切正常。你知道如何解决这个问题吗?还是只是一个错误?

它可以与派生类相关。如果您将 Special property 从 Ferrari 移到 Car,它就可以工作。

【问题讨论】:

    标签: c# entity-framework .net-core entity-framework-core


    【解决方案1】:

    这是因为当您使用 context.Add 方法时,您正在添加的对象被标记为已添加,并且在您调用 save 时它被保存。当您将对象添加到另一个对象时,它不会被标记为已添加,并且在您调用 save 时会被上下文忽略。而且您必须向 Car 类添加一个 GarageId 属性。否则它将永远无法正常工作。如果只有你没有一个车库。但在这种情况下,车库课程根本没有任何意义。并将 Ferrary 与 Car 类合并。否则,您将需要一个特殊的表和很多问题才能使其正常工作。

    试试这个:

    var garage = new Garage () ;
    var ferrari = new Ferrari();
    ferrari.Special = new Special();
    garage.Cars.Add(ferrari);
    
    Console.WriteLine($"Special reference is null: {(ferrari.Special == null ? "YES" : "NO")}")
    
    context.Add(garage);
    await context.SaveChangesAsync();
    
    Console.WriteLine($"Special reference is null: {(ferrari.Special == null ? "YES" : "NO")}");
    

    var garage = new Garage();
    
    context.Add(garage);
    
    await context.SaveChangesAsync();
    
    var ferrari = new Ferrari( GarageId=garage.Id);
    ferrari.Special = new Special();
    
    context.Add(ferrari);
    
    Console.WriteLine($"Special reference is null: {(ferrari.Special == null ? "YES" : "NO")}");
    
    await context.SaveChangesAsync();
    
    Console.WriteLine($"Special reference is null: {(ferrari.Special == null ? "YES" : "NO")}");
    

    【讨论】:

    • 好的,看起来它还取决于调用 SaveChanges 的时间。这种行为是有意的吗?我应该只在某些情况下调用 SaveChanges 吗?有哪些情况?
    • 在第二个示例中,您在 Ferrari 上设置 GarageId,但此属性不存在。此外,您在两个示例中都有错误,无法编译。没关系,感谢您的帮助。
    • 请出示您的 Garage 和 Car 类,我会修正外键的名称。我不得不猜你的数据结构。
    • 好的,将法拉利添加到上下文中会有所帮助。谢谢!我将对其进行更多测试并返回。我认为跟踪更改应该注意它。这就是我没有添加其他实体的原因。
    • 抱歉,我刚刚检查了您的链接,如果您没有车库,请阅读我的最新更新并将 GarageId 添加到汽车。但在这种情况下,车库课程根本没有任何意义。
    【解决方案2】:

    这是一个已确认的错误https://github.com/dotnet/efcore/issues/23180。修复应该从版本 5.0.1 https://github.com/dotnet/efcore/pull/23186 开始提供。

    由于#22603,在将存储生成的值传播到 FK 时,我们将 FK 存储生成的值设置为 null。当接受这些值时,我们用“商店生成的”空值覆盖传播的值。在 TPH 中,此时 Ferrari 条目中没有存储生成的值,因此我们不必将它们重置为 null,但是在 TPT 中,首先插入 Car 部分并触发存储生成的 Sidecar 的创建,导致到上述情况。 — AndriySvyryd

    【讨论】:

      猜你喜欢
      • 2019-12-10
      • 2023-03-13
      • 2021-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-26
      • 1970-01-01
      • 2021-06-04
      相关资源
      最近更新 更多