实体框架核心:如何确保加载导航属性?

时间:2020-08-03 13:13:38

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

说我有以下模型:

public class Subject
{
    private List<SubjectResponse> responses;

    public int Id { get; private set; }

    public IEnumerable<SubjectResponse> Responses => responses.ToList();

    public void Foo()
    {
        // How do I check here if Responses fully has been loaded?

        foreach (var response in Responses)
        {
            // ...
        }
    }
}

public class SubjectResponse
{
    public int Id { get; private set; }
}

如何检查所有响应是否都已加载到Foo()中?我可能会检查if (Responses is null),但这在所有情况下都行不通。

这是可能做错事的最小示例。在真实的应用程序中,响应可以在完全不同的位置加载。但是h这显示了EF如何固定响应,因此它可以包含条目,但不是所有条目。

public async Task Bar()
{
    var response = await dbContext.SubjectResponses.SingleAsync(s => s.Id == 1);
    var subject = await dbContext.Subjects.SingleAsync(s => s.Id == 1);
    subject.Foo();
    // subject.Responses now has a count if 1, when there might actually be more responses.
}

由于性能影响(并且因为惰性加载不会异步加载相关的实体),所以我不想使用延迟加载。渴望和显式加载都可以。

编辑:我主要要寻找的是一种检查导航属性是否已完全加载的方法,以便我可以加载它。

1 个答案:

答案 0 :(得分:1)

您无法检测到所有相关实体是否恰好已通过实体框架传递。

您显示的内容之所以有效,是因为dbContext.SubjectResponses.SingleAsync(s => s.Id == 1)中的实体的SubjectId为1,将被缓存,并随后附加到dbContext.Subjects.SingleAsync(s => s.Id == 1)的结果上。

EF和您的代码都无法知道已经从数据库中加载了SubjectResponses为1的 all SubjectId,所以您必须explicitly load them

var subject = await dbContext.Subjects.SingleAsync(s => s.Id == 1);
await dbContext.Entity(subject)
               .Reference(s => s.responses)
               .LoadAsync();

但是您不能这样做,因为Subject.responses是私有的,因此您必须在实体的Foo()方法中执行此操作,并且必须将DbContext注入到您的实体,那将变成一团糟。

为什么不仅要务实地做到这一点,还应事先将Responses设为公共自动财产,并将Include()设为相关实体:

var subject = await dbContext.Subjects.Include(s => s.Responses).SingleAsync(s => s.Id == 1);