C# 何时需要在实体框架中包含相关实体?

C# 何时需要在实体框架中包含相关实体?,c#,entity-framework,C#,Entity Framework,当我必须实际.Include()相关实体时,以及当我不这样做时,这对我来说似乎是任意的。在某些情况下,EF向我提供了相关实体的信息,而在其他情况下,EF无法处理相关实体,因为我没有包括它们: 不带.Include()的作品; 这是一个示例,我加载数据时没有.Include() 以下是结果,其中包括与我请求的发票相关的公司数据: 如你所见,该公司的信息肯定是通过,但没有明确包括在内 没有.Include()无法工作; 相反,我在同一个项目中对发票进行了一些映射,在获取相关实体属性值时,我得到了N

当我必须实际
.Include()
相关实体时,以及当我不这样做时,这对我来说似乎是任意的。在某些情况下,EF向我提供了相关实体的信息,而在其他情况下,EF无法处理相关实体,因为我没有包括它们:

不带.Include()的作品; 这是一个示例,我加载数据时没有.Include()

以下是结果,其中包括与我请求的发票相关的公司数据:

如你所见,该公司的信息肯定是通过,但没有明确包括在内

没有.Include()无法工作; 相反,我在同一个项目中对发票进行了一些映射,在获取相关实体属性值时,我得到了NullReferenceExceptions,因为我没有
.Include()

此方法获取指定公司的所有已批准时间表条目。此viewmodel仅在处理发票的时间表条目关联时使用(因此您是根据选定的时间表条目开票)

为什么Entity Framework有时乐于提供数据而不需要我显式请求该数据,有时它要求我显式请求该数据,或者抛出错误


我如何知道何时需要显式包含正在查找的数据,何时不需要?

实体框架使用延迟加载来加载子关系对于模型中的延迟加载到工作属性,应使用
virtual
关键字标记。
Ef将覆盖它并添加延迟加载支持

当您没有虚拟财产时,EF无法在以后加载您的子关系数据,因此唯一可能的时间是-在初始数据加载期间,使用
Include

public class Timesheet
{
    ...
    public virtual StaffMember StaffMember { get; set; }
    public virtual Task Task { get; set; }
    ...
}

这取决于你的型号。如果您已将关系属性标记为
virtual
,则需要使用
。Include
,以便EF知道您需要它。这是延迟加载。保留机器的内存和数据库请求

别厚颜无耻,但你确定吗?时间表上的我的实体(员工和任务)未标记为虚拟,我必须
。包括它们。看起来情况正好相反?哪些属性在没有包含的情况下不会加载?都是收藏品吗?太棒了,谢谢你的解释。那么,您认为最佳实践是在构建模型时始终使相关实体
虚拟化吗?或者在查询中显式地
.Include()
它们会更好吗?延迟加载通常会导致很多性能问题。每次发生延迟加载时,查询都会命中数据库。在您的示例中,您正在执行foreach并访问延迟加载的属性:这将导致SELECTN+1问题。那样的话,包括在内会更好。OTOH使用延迟加载会导致我们编写更少的代码,并且可以在您不在循环中使用它或者性能不重要的地方使用。在我最近的项目中,出于性能原因,我们决定禁用延迟加载。在启用延迟加载的项目上,最好是标记虚拟导航属性和集合(由于虚拟表方法查找,几乎没有明显的性能影响),但在适当的情况下使用它(避免选择N+1问题)。
public List<InvoiceTimesheetViewModel> GetInvoiceTimesheetsByCompanyId(int companyId)
{
    var factory = new TimesheetViewModelsFactory();

    var timesheets = db.Timesheets.Where(x => x.Approved && x.Company.Id == companyId && !x.Deleted).ToList();
    return factory.GetInvoiceTimesheetsViewModel(timesheets);
}
public List<InvoiceTimesheetViewModel> GetInvoiceTimesheetsViewModel(List<Timesheet> timesheets)
{
    var model = new List<InvoiceTimesheetViewModel>();
    foreach (var timesheet in timesheets)
    {
        var start = DateTime.Parse((timesheet.DateAdded + timesheet.StartTime).ToString());
        var finished = DateTime.Parse((timesheet.DateCompleted + timesheet.EndTime).ToString());
        DateTime.TryParse(timesheet.RelevantDate.ToString(), out DateTime relevant);

        model.Add(new InvoiceTimesheetViewModel
        {
            RelevantDate = relevant,
            BillableHours = timesheet.BillableHours,
            Finished = finished,
            Id = timesheet.Id,
            StaffMember = timesheet.StaffMember.UserName, // NRE here.
            Start = start,
            Task = timesheet.Task.Name // NRE here.
        });
    }
    return model;
}
var timesheets = db.Timesheets.Include(i => i.StaffMember).Include(i => i.Task)
            .Where(x => x.Approved && x.Company.Id == companyId && !x.Deleted).ToList();
public class Timesheet
{
    ...
    public virtual StaffMember StaffMember { get; set; }
    public virtual Task Task { get; set; }
    ...
}