【问题标题】:Entity Framework 6 and collections实体框架 6 和集合
【发布时间】:2013-12-17 04:35:04
【问题描述】:

我正在开发我的第一个实体框架应用程序。我正在使用 EF vesion 6(来自 Nuget)和 .net 4.0。但是,在我看来,这似乎应该非常简单。我在互联网上发现了很多相互矛盾的建议和解决方案,但是在花了几天时间试图解决问题之后,我真的很困惑,并且到了质疑我对实体框架的一些基本理解的地步。我想做的是:创建一个简单的相关实体集合,并在它们从父级中删除时自动删除它们。

以下是我将如何在 vanilla C# 中对此进行建模。为了与 Microsoft 示例保持一致,假设我们有两个类,Post 和 Tag,如下所示:

public class Post
{
    public string Name { get; set; }
    public string Author { get; set; }

    public ICollection<Tag> Tags { get; set; }
}

public class Tag
{
    public string Text { get; set; }
    // Possibly other properties here
}

那么,添加标签就像myPost.Tags.Add(myTag)一样简单,删除标签就像myPost.Tags.Remove(myTag)一样简单。

现在到实体框架方面:我看了看,想“当然是外键!”但是我在添加 FK 时遇到了很多问题:标签从帖子中删除时不会从数据库中删除,myPost.Tags 从数据库加载时将有 0 个元素,尽管 SQL 资源管理器显示 PostId 值为正确,等等。我迷惑了一堆技巧,比如将Tag.PostId标记为键,手动删除标签,实际上将标签作为DbSet添加到上下文中,手动设置myTag.Post = null;(我尝试同时启用延迟加载和禁用,因为它的价值 - 虽然如果可能的话我想保持关闭)

现在(很大程度上归功于看似矛盾和过于复杂的示例),我很困惑和迷茫。有人可以确切地告诉我应该如何在 EF 中建立这种关系吗? (顺便说一下,我使用的是 Code First)

解决方案:

感谢 Moho,我想出了这个结构,它完全符合我的要求:

public class Post
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Author { get; set; }

    public virtual ICollection<Tag> Tags { get; set; }

    public Post()
    {
        Tags = new HashSet<Tag>();
    }
}

public class Tag
{
    [Key, Column(Order=1), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Text { get; set; }
    // Possibly other properties here

    public virtual Post Post { get; set; }
    [Key, Column(Order=2)]
    public virtual int PostId { get; set; }
}

public class TestContext : DbContext
{
    public DbSet<Post> Posts { get; set; }
}

当从 Post 的标签集合中删除 Tag 时,实体框架将为标签发出 DELETE,如下所述 (#2):http://www.kianryan.co.uk/2013/03/orphaned-child/

同样,在帖子中添加标签会自动发出 INSERT 并设置 FK 关系。

需要注意的一点:确保使用virtual!我想这也是我很多挫败感的根源。

【问题讨论】:

  • 你没有把Id属性和EF做关系?您能否举例说明您是如何进行查询的?
  • @Tico 我遗漏了 EF 实现的东西,这是故意的 - 我想避免我的实际实现中的任何负面影响/先入之见(更不用说它在我摆弄它时发生了很大变化! ) 这样我就可以从一开始就获得有关如何设置它的建议。与我的示例查询相同的故事,尽管我设想我的用例是这样的:“1)使用一些初始标签创建的帖子 2)帖子的标签被编辑,一些被删除,一些被添加。3)帖子被保存。”
  • 所以我猜你对上下文和其他东西很熟悉。因为您在 SQL 中确实有一些数据。使用 LINQ 查询其中之一。如果它仍然返回null,则您的查询可能有问题。
  • 你我的朋友绝对是个天才!我一直把头撞在墙上,试图完成这项工作,你的解决方案完全符合我的要求。

标签: c# entity-framework


【解决方案1】:

也尝试从Tag 端定义关系,指定每个Tag 与单个Post 相关并且是必需的。

Tag中为Post添加所需的导航属性:

public class Tag
{
    // you need an ID
    public int Id { get; set; }

    public string Text { get; set; }

    [Required]
    public virtual Post Post { get; set; }
}

或者,如果你真的不想添加导航属性,你可以使用 Fluent API:

modelBuilder.Entity<Post>().HasMany( p => p.Tags ).WithRequired();

【讨论】:

  • 我通常为导航属性添加一个Id
  • 我的意思是您的实体需要一个主键 ID 字段;假设您为了简洁而忽略了它们
  • [Required] DataAnnotation 用于创建所需的关系。我的意思是public virtual Post Post { get; set; } 以及类似的属性:public int PostId { get; set; }。当我搜索对象时,我只有Id。如果我搜索Post,它将返回null
  • [Required] 注释是否也会阻止孤儿(标签)?类似于本文中的内容:kianryan.co.uk/2013/03/orphaned-child(我在各种努力中尝试了 #2)
  • 是的 - 使Tag->Post 关系成为必需意味着没有相应的Post 就没有Tag。这就是为什么从Tags 集合中删除Tag 将删除数据库中的Tag 记录
【解决方案2】:

这可能是线程死灵术,但我没有代表发表评论。以下不是您需要的吗?

public class Tag
{
    [Key, Column(Order=1), DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Text { get; set; }
    // Possibly other properties here

    public int PostId { get; set; }

    [ForeignKey("PostId")]
    public virtual Post Post { get; set; }

}

PostId 从数据库中加载,然后用作外键(通过注释)到 Post 类中。帖子是虚拟的,其他什么都不是。您也可以只使用基本的 [Key] 注释。

【讨论】:

  • EF 自动将名为 ID 的属性标识为类的唯一标识符,因此您是多余的。此外,Fluent API 在描述关系方面似乎比对属性的修饰更好(更可靠)。此外,在 DbContext 中定义您的关系(以及在其他数据访问层文件中的其余持久性描述)允许您在以后离开 EF,只需进行一些更改。
猜你喜欢
  • 2014-11-08
  • 2014-07-28
  • 1970-01-01
  • 1970-01-01
  • 2021-08-22
  • 2013-10-30
  • 2015-05-20
  • 2018-12-06
  • 1970-01-01
相关资源
最近更新 更多