【问题标题】:Why EF core tries to add navigational property into DB and not only the Id of foreign model?为什么 EF 核心尝试将导航属性添加到数据库中,而不仅仅是外国模型的 Id?
【发布时间】:2019-05-14 16:10:54
【问题描述】:

我想知道为什么 EF 还要尝试添加外国模型。 示例:

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

public class Content
{
    public int Id { get; set; }
    public string Name{ get; set; }
    public Category Category{ get; set; }
}

使用迁移创建“内容”后,我有一个包含类别 ID 的表。那就是创造。所以我有三列:Id、name 和 categoryId。似乎 EF“知道”这应该只是 Category 的主键,需要存储。

比我尝试用 EF 添加一些东西。

var cat = new Category {Id = 2, Name = "awesomeCat"})
var addContent = new Content({Name = "test", Category = cat})

现在我想使用 _context.Add(addContent) 添加内容。我期待使用名称“test”和 categoryId 2 的单个插入到 db。ID 将由 DB 生成。

但 EF 也尝试将新类别添加到类别表中。

所以我进行了更深入的研究,似乎 EF“不”知道它已经存在并且没有维护任何关于类别模型的交易。 我又试了一次,没有使用新的类别,而是在之前加载它: var cat = _context.findById("2"); 并分配了这个。现在 EF 应该知道这个已经存在并且不必将其添加到类别表中。

有没有可能,我的模型是错误的。 我是否需要更多地使用它:

public class Content
{
    public int Id { get; set; }
    public string Name{ get; set; }
    public int? CategoryId{ get; set; }
    [ForeignKey("CategoryId")]
    public Category Category{ get; set; }
}

那我不会得到两个类别引用吗?

【问题讨论】:

  • 当您尝试加载现有类别 (var cat = _context.findById("2");) 时是否按预期工作,还是仍插入重复行?您的问题并不清楚,因为您说“EF 应该知道”,但这可能意味着“应该并且应该”或“应该但不应该”。
  • 对不起,你是对的。不,EF 仍然尝试添加类别,当然,它失败了,因为 ID 是 DB 生成的,并且不允许分配除 0(或 null)以外的值

标签: asp.net-core entity-framework-core


【解决方案1】:

您需要告诉 EF Core 它是一个主键并生成该键

public class Category
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Content
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
    public Category Category { get; set; }
}

那你就不用标记[ForeignKey("CategoryId")]了,EF Core会把对象引用变成数据库中的ID

如果我误解了你的问题,请再问:)

【讨论】:

    【解决方案2】:

    EF Core 具有对实体的内部跟踪。当您只是新建一个类别时,它不会被跟踪。当您添加内容时,EF 也会跟踪任何相关实体,其中包括您的类别,默认情况下将被跟踪为“已添加”。你有几个选择。

    1. 不要“新建”现有类别,而是从数据库中检索它。如果EF从数据库中拉取,那么会被跟踪,不会再添加。

    2. 您可以显式跟踪您新建的类别实例并将其状态设置为“未更改”。

      _context.Attach(category);
      _context.Entry(category).State = EntityState.Unchanged;
      _context.Add(content);
      
    3. 最好的方法是根本不处理引用属性,而使用显式外键属性。向您的内容类添加一个属性:

      public int CategoryId { get; set; }
      

      然后,您可以简单地设置这个 id,而不是 Category 属性:

      var addContent = new Content { Name = "test", CategoryId = 2 };
      

      EF 将在保存后回填引用属性。

    【讨论】:

    • 我会试试 ID 的属性。不过,您会建议从内容中删除 Category 的导航属性吗?那么我应该添加 CategoryId 还是用 CategoryId 替换 Category?不确定什么是最好的和推荐的。使用包含从数据库中提取类别时,仅拥有 CategoryId 不会创建类别,对吧?
    猜你喜欢
    • 2021-08-13
    • 2018-09-21
    • 1970-01-01
    • 2020-06-17
    • 2020-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-09
    相关资源
    最近更新 更多