【问题标题】:EF Core one-way self referencing entity bindingEF Core 单向自引用实体绑定
【发布时间】:2019-03-01 07:33:17
【问题描述】:

我使用 EF Core 设置了一个自引用实体,如下所示:

实体

public class DetailType
{
    public int DetailTypeId { get; set; }
    public string Name { get; set; }

    public int? ParentTypeId { get; set; }
    public DetailType ParentType { get; set; }
    public IEnumerable<DetailType> ChildTypes { get; set; }
}

绑定

modelBuilder.Entity<DetailType>()
    .ToTable("detailtype")
    .HasOne(x => x.ParentType)
    .WithMany(x => x.ChildTypes)
    .HasForeignKey(x => x.ParentTypeId);

我正在通过 API 检索这些实体,当前结果如下所示:

[
    {
        "detailTypeId": 20,
        "name": "Money",
        "parentTypeId": null,
        "parentType": null,
        "childTypes": null
    },
    {
        "detailTypeId": 22,
        "name": "Optional Extra",
        "parentTypeId": null,
        "parentType": null,
        "childTypes": [
            {
                "detailTypeId": 42,
                "name": "Extra Nights",
                "parentTypeId": 22,
                "childTypes": null
            }
        ]
    },
    {
        "detailTypeId": 42,
        "name": "Extra Nights",
        "parentTypeId": 22,
        "parentType": {
            "detailTypeId": 22,
            "name": "Optional Extra",
            "parentTypeId": null,
            "parentType": null,
            "childTypes": []
        },
        "childTypes": null
    }
]

我遇到的问题是数组中的第三项与第二项正好相反。有没有办法避免这种情况,所以我只有父母 -> 孩子关系,而不是父母 -> 孩子以及孩子 -> 父母。上面的示例是我的 API 实际返回的内容的大幅缩减版本,因此我想尽可能减少不必要的膨胀,因为会发生很多关系。

理想情况下,我想要摆脱 ParentType 属性,但仍然拥有 ChildTypes 集合,但我不确定如何在模型构建器中定义它。

编辑:

我已经删除了流畅的关系,因为它不需要。我还尝试了以下方法:

var roots = this.Items.Where(x => x.ParentTypeId == null);
foreach (var root in roots)
{
    root.ChildTypes = this.Items.Where(x => x.ParentTypeId == root.DetailTypeId);
}

return roots.ToList();

this.Items 顺便说一下 DbSet)

但是,这需要将 ChildTypes 更改为 IQueryable,当我这样做时,我得到以下异常:

实体类型上的导航属性“ChildTypes”的类型 'DetailType' 是 'EntityQueryable' 未实现 收藏。集合导航属性必须 实现目标类型的ICollection。

【问题讨论】:

  • 如何为您的 api 使用不包含 ParentType 属性的单独模型。然后使用例如简单地将内部(db)模型映射到控制器中的外部(api)模型。自动映射器automapper.org
  • 如前所述,有一些设计级别的正确方法可以做到这一点。但作为一种解决方法 - 您可以申请 .Where(x =&gt; x.parentTypeId == null) 以摆脱除顶级之外的所有项目。这应该提供所需的输出。
  • 是的,您需要使用视图模型/DTO 来处理此问题,而不是直接使用实体类。
  • @YeldarKurmangaliyev 这确实只返回根项目。但是,它也会删除 ChildTypes 中的所有项目,所以我得到的只是根项目,这不是我想要的。
  • @AndyFurniss 它确实返回了包含ChildItems 中列出的所有非根项目的根项目,不是吗?为什么会删除ChildTypes中的所有项目?

标签: c# entity-framework asp.net-core entity-framework-core self-referencing-table


【解决方案1】:

首先,您无需在模型构建器中指定此关系。它自己算出来的。

然后关于您的问题 - 我想到的第一件事(我不知道您的整个数据集)是获取所有具有 ParentTypeId == nullDetailType 对象。

通过这个你会得到根,然后,递归地构建子元素的tree

这样做会清理你的结果,你会看到你想看到的结构。

【讨论】:

  • 这不需要多次数据库调用吗?
  • 好吧,取决于 - 您是否启用了延迟加载,或者您可以在对 DB 的调用中明确包含 .ChildTypes。另外-您可以最初从数据库中查询所有项目,然后从接收到的列表中,通过仅获取根元素并继续递归来构建您的结构。您将已经查询了所有项目,并且所有操作都将在内存中,无需进一步调用数据库。
  • 此外 - 如果您使用 DetailType 的 IQuerable 集合并构建您的结构,最后您调用集合的 .ToList(),您将只执行一个查询(当 @ 987654327@ 被调用)。您始终可以使用 SQL Server Profiler 来检查您的查询并确保执行的内容就是您想要执行的内容。
  • 嗯,我已经尝试过了,但进展不大。有关详细信息,请参阅我的问题的编辑。如果我做错了,请告诉我。
  • 什么是this.Items?你不是在查询 dbcontext 吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-02
  • 1970-01-01
  • 1970-01-01
  • 2022-11-30
  • 1970-01-01
  • 2021-09-27
相关资源
最近更新 更多