.NET Entity Framework Core 仅返回父子关系图的部分对象图

问题描述 投票:0回答:1

我在 ASP.NET Core Web API 项目中有一个使用 Entity Framework Core 的启动项目。

我有一个

Category
对象,它与
Category
具有父子关系(即:类别及其子类别,子类别可以有子类别等)。

当我尝试仅返回顶级类别(希望它们的子类别会自动添加到图表中)时,我发现它确实返回顶级类别及其子类别,但不返回其子类别的子类别,即:

top level -> child -> [should return child's child but its not]

我正在尝试在我的

CategoryRepository
类上使用此方法:

  public async Task<IEnumerable<Category>> getAllTopLevelCategories()
  {
      return await _context.Categories
                           .Include(p => p.Children)
                           .Where(category => category.ParentId == null)   
                           .ToListAsync();
  }

但是我返回的 JSON 缺少“Steer Compost”,它是“Compost”类别的子项

[
  {
    "name": "Garden",
    "parentId": null,
    "parent": null,
    "children": [
      {
        "name": "Compost",
        "parentId": "e9a9e63b-e71f-4aab-b1bb-97db5687048b",
        "parent": null,
        "children": [],
        "products": [],
        "id": "464b75f0-f5db-4fac-8023-ec39ae21ad79",
        "dateAdded": "2024-10-14T20:51:57.167129Z"
      }
    ],
    "products": [],
    "id": "e9a9e63b-e71f-4aab-b1bb-97db5687048b",
    "dateAdded": "2024-08-07T13:59:27.182185Z"
  },
  {
    "name": "Pets",
    "parentId": null,
    "parent": null,
    "children": [
      {
        "name": "Pet Food",
        "parentId": "f1dfe34c-adb2-4e19-81ad-d69d4c3364b9",
        "parent": null,
        "children": [],
        "products": [],
        "id": "b1000daf-d101-4ee3-b5b6-fb49e4aa5598",
        "dateAdded": "2024-10-14T17:36:29.502663Z"
      }
    ],
    "products": [],
    "id": "f1dfe34c-adb2-4e19-81ad-d69d4c3364b9",
    "dateAdded": "2024-08-07T14:17:44.973453Z"
  }
]

此方法返回所有

Categories
,但每个的对象图都已完全填写!我基本上想要这个,但只返回顶层
Categories
(以及它们所属的对象图)。我可以使用这个,但我必须对返回的
Category
数组进行过滤(使用 Linq),因此我只会使用顶级类别,但这似乎是 Entity Core 应该能够做到的事情,我只是还不知道该怎么做。

  public async Task<IEnumerable<Category>> getAllCategories()
  {
      return await _context.Set<Category>()
                           .ToListAsync();
  } 

结果:

[
  {
    "name": "Garden",
    "parentId": null,
    "parent": null,
    "children": [
      {
        "name": "Compost",
        "parentId": "e9a9e63b-e71f-4aab-b1bb-97db5687048b",
        "parent": null,
        "children": [
          {
            "name": "Steer Compost",
            "parentId": "464b75f0-f5db-4fac-8023-ec39ae21ad79",
            "parent": null,
            "children": [],
            "products": [],
            "id": "5d567504-62be-4af7-9cab-dcdd66b670ec",
            "dateAdded": "2024-10-18T15:09:45.58668Z"
          }
        ],
        "products": [],
        "id": "464b75f0-f5db-4fac-8023-ec39ae21ad79",
        "dateAdded": "2024-10-14T20:51:57.167129Z"
      }
    ],
    "products": [],
    "id": "e9a9e63b-e71f-4aab-b1bb-97db5687048b",
    "dateAdded": "2024-08-07T13:59:27.182185Z"
  },
  {
    "name": "Pets",
    "parentId": null,
    "parent": null,
    "children": [
      {
        "name": "Pet Food",
        "parentId": "f1dfe34c-adb2-4e19-81ad-d69d4c3364b9",
        "parent": null,
        "children": [],
        "products": [],
        "id": "b1000daf-d101-4ee3-b5b6-fb49e4aa5598",
        "dateAdded": "2024-10-14T17:36:29.502663Z"
      }
    ],
    "products": [],
    "id": "f1dfe34c-adb2-4e19-81ad-d69d4c3364b9",
    "dateAdded": "2024-08-07T14:17:44.973453Z"
  },
  {
    "name": "Pet Food",
    "parentId": "f1dfe34c-adb2-4e19-81ad-d69d4c3364b9",
    "parent": {
      "name": "Pets",
      "parentId": null,
      "parent": null,
      "children": [
        null
      ],
      "products": [],
      "id": "f1dfe34c-adb2-4e19-81ad-d69d4c3364b9",
      "dateAdded": "2024-08-07T14:17:44.973453Z"
    },
    "children": [],
    "products": [],
    "id": "b1000daf-d101-4ee3-b5b6-fb49e4aa5598",
    "dateAdded": "2024-10-14T17:36:29.502663Z"
  },
  {
    "name": "Compost",
    "parentId": "e9a9e63b-e71f-4aab-b1bb-97db5687048b",
    "parent": {
      "name": "Garden",
      "parentId": null,
      "parent": null,
      "children": [
        null
      ],
      "products": [],
      "id": "e9a9e63b-e71f-4aab-b1bb-97db5687048b",
      "dateAdded": "2024-08-07T13:59:27.182185Z"
    },
    "children": [
      {
        "name": "Steer Compost",
        "parentId": "464b75f0-f5db-4fac-8023-ec39ae21ad79",
        "parent": null,
        "children": [],
        "products": [],
        "id": "5d567504-62be-4af7-9cab-dcdd66b670ec",
        "dateAdded": "2024-10-18T15:09:45.58668Z"
      }
    ],
    "products": [],
    "id": "464b75f0-f5db-4fac-8023-ec39ae21ad79",
    "dateAdded": "2024-10-14T20:51:57.167129Z"
  },
  {
    "name": "Steer Compost",
    "parentId": "464b75f0-f5db-4fac-8023-ec39ae21ad79",
    "parent": {
      "name": "Compost",
      "parentId": "e9a9e63b-e71f-4aab-b1bb-97db5687048b",
      "parent": {
        "name": "Garden",
        "parentId": null,
        "parent": null,
        "children": [
          null
        ],
        "products": [],
        "id": "e9a9e63b-e71f-4aab-b1bb-97db5687048b",
        "dateAdded": "2024-08-07T13:59:27.182185Z"
      },
      "children": [
        null
      ],
      "products": [],
      "id": "464b75f0-f5db-4fac-8023-ec39ae21ad79",
      "dateAdded": "2024-10-14T20:51:57.167129Z"
    },
    "children": [],
    "products": [],
    "id": "5d567504-62be-4af7-9cab-dcdd66b670ec",
    "dateAdded": "2024-10-18T15:09:45.58668Z"
  }
]

这是我的

Category
对象:

using Newtonsoft.Json;

using ShopBack.Database.Models;

/// <summary>
/// The Category model
/// </summary>
public class Category : ModelBase
{
    /// <summary>
    /// Name of the category
    /// </summary>
    public required string Name { get; set; }

    /// <summary>
    /// Parent CategoryId
    /// </summary>
    public required Guid? ParentId { get; set; }

    /// <summary>
    /// Parent CategoryId
    /// </summary>
    public virtual Category? Parent { get; set; }
   
    /// <summary>
    /// Parent CategoryId
    /// </summary>
    public virtual ICollection<Category> Children { get; set; } = new List<Category>();

    /// <summary>
    /// List of products for the category
    /// </summary>
    public virtual ICollection<Product> Products { get; set; } = new List<Product>();
}
json .net-core entity-framework-core asp.net-core-webapi
1个回答
0
投票

你没有告诉它加载 2 个子级别,只加载 1 个。如果你想要 top -> child -> grandchild 那么:

return await _context.Categories
    .Include(p => p.Children)
    .ThenInclude(c => c.Children)
    .Where(category => category.ParentId == null)   
    .ToListAsync();

请记住,它会停在那里。它不会包括曾孙。

即使您没有指定包含某些孙子项,您也可能会看到它填充了它们,这与 EF 的缓存有关。如果在您读取此查询时 DbContext 碰巧加载并跟踪了孙子实体或任何其他相关实体,那么当您执行启用跟踪的查询时,该实体将自动关联。这可能会误导结果,因为如果您有 2 个孙子需要“堆肥”,但其中一个碰巧已被较早读取和跟踪,而另一个没有,那么堆肥将只填满一个孙子。

为了避免跟踪未明确请求的缓存填充实体,请执行无跟踪查询:

return await _context.Categories
    .Include(p => p.Children)
    .THenInclude(c => c.Children)
    .Where(category => category.ParentId == null)   
    .AsNoTracking()
    .ToListAsync();

这有两个效果。 #1 - 此查询获取的实体不会添加到跟踪缓存中。 #2 - 在构建此查询的结果时,不使用跟踪缓存中的实体。 (仅限数据库中当前持久的数据)我建议在读取数据时应使用

AsNoTracking
并仅将跟踪查询保留用于更新/插入读取。

根据返回的行数和大小(列),您还可能受益于使用

.AsSplitQuery()
来消除连接多个表时产生的笛卡尔积。

© www.soinside.com 2019 - 2024. All rights reserved.