【问题标题】:The Include property lambda expression [...] is invalid. The expression should represent a property accessInclude 属性 lambda 表达式 [...] 无效。表达式应表示属性访问
【发布时间】:2019-08-13 14:16:52
【问题描述】:

我正在尝试获取所有修补程序并包括属性 Available 为 1 的所有详细信息(与其相关联)。这是我的代码:

public static IList<HotFix> GetAllHotFix()
{
    using (Context context = new Context())
    {
        return context.HotFix
            .Include(h => h.AssociatedPRs)
            .Include(h => h.Detail.Where(d => d.Available = 1))
            .ToList();
    }
}

我得到了那个错误。我尝试使用 .ThenInclude 但无法解决。

在 HotFix 中我有:

[Required]
public virtual List<HotFixDetail> Detail { get; set; }

【问题讨论】:

  • 请发布HotFixHotFixDetail的代码
  • 您不能在Include 中过滤(即使用Where
  • 不能分享太多,但我在帖子中添加了带有列表的属性。如果它值得的话,如果我删除 where 它可以正常工作(但会带来所有细节)
  • @DavidG 有什么解决方法吗?
  • 那么您可能需要从HotFixDetail 开始,然后返回HotFix。所以,像context.HotFixDetail.Where(d =&gt; d.Available == 1)....

标签: c# entity-framework linq


【解决方案1】:

虽然您忘记编写类定义,但您似乎有一个HotFix 类。每个HotFix 都有一个零个或多个AssociatedPRs 的序列和一个零个或多个Details 的序列。

曾经Detail 至少有一个数字属性Available

您想要所有HotFixes,每个都有其所有AssociatedPRs,以及所有Details 的属性Available 值等于1(您不是说available 是布尔值吗?)

在使用实体框架时,人们倾向于使用 include 来获取item with its sub-items。这并不总是最有效的方法,因为它会获取表格的完整行,包括您不打算使用的所有属性。

例如,如果您有一对多的关系,Schools with their Students,那么每个Student 都会有一个外键指向这个`学生就读的School

所以如果 School [10] 有 1000 个Students,那么每个Student 都会有一个指向School 的外键,值为10。如果你使用Include 来获取School [10] with its Students,那么这个外键键值也被选中,并发送了 1000 次。您已经知道它将等于 Schools 主键值,因此将这个值传输 10 到 1001 次会浪费处理能力。

查询数据时,始终使用 Select,并且仅选择您实际计划使用的属性。仅当您计划更新获取的数据时才使用 Include。

另一个好的建议是使用复数来描述序列和单数来描述序列中的一个项目

您的查询将是:

var result = context.HotFixes.Select(hotfix => new
{
    // Select only the hotfix properties you actually plan to use:
    Id = hotfix.Id,
    Date = hotfix.Date,
    ...

    AssociatedPRs = hotfix.AssociatedPRs.Select(accociatedPr => new
    {
        // again, select only the associatedPr properties that you plan to use
        Id = associatedPr.Id,
        Name = associatedPr.Name,
        ...

        // foreign key not needed, you already know the value
        // HotFixId = associatedPr.HotFixId
    })
    .ToList(),

    Details = hotfix.Details
        .Where(detail => detail.Available == 1)
        .Select(detail => new
        {
            Id = detail.Id,
            Description = detail.Description,
            ...

            // not needed, you know the value:
            // Available = detail.Available,

            // not needed, you know the value:
            // HotFixId = detail.HotFixId,
        })
        .ToList(),
});

我使用匿名类型。您只能在定义匿名类型的过程中使用它。如果需要返回获取的数据,则需要将选择的数据放入一个类中。

return context.HotFixes.Select(hotfix => new HotFix()
{
    Id = hotfix.Id,
    Date = hotfix.Date,
    ...

    AssociatedPRs = hotfix.AssociatedPRs.Select(accociatedPr => new AssociatedPr()
    {
       ... // etc

注意:您仍然不必填写所有字段,除非您的功能要求明确说明这一点。

您的函数的用户可能会感到困惑,不知道哪些字段将实际填充,哪些字段不会。另一方面:在向数据库添加项目时,他们已经习惯于不填写所有字段,例如主键和外键。

为了解决并非所有字段都填写的问题,一些开发人员设计了一个额外的层:存储库层(使用存储库模式)。为此,他们创建了代表人们想要放入存储并希望保存到存储中的数据的类。通常这些人对数据保存在关系数据库中以及外键之类的东西不感兴趣。所以存储库类不会有外键

存储库模式的优势在于,存储库层隐藏了存储系统的实际结构。它甚至隐藏它是一个关系数据库。它也可能在 JSON 文件中。如果数据库发生变化,repository 层的用户不必知道这一点,也可能不需要更改。

存储库模式还可以更轻松地模拟数据库以进行单元测试:由于用户不知道数据在关系数据库中,因此对于单元测试,您可以将日期保存在 JSON 文件或 CSV 中-file 或其他。

缺点是您需要编写额外的类来保存要放入存储库或从存储库获取的数据。

添加这个额外的层是否明智,取决于您希望数据库在未来多久更改一次布局,以及您的单元测试需要多好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-12
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多