【问题标题】:When do you need to .Include related entities in Entity Framework?什么时候需要在实体框架中包含相关实体?
【发布时间】:2019-04-01 12:12:37
【问题描述】:

当我实际上必须.Include() 相关实体而我不需要时,这对我来说似乎是任意的。在某些情况下,EF 会在没有它的情况下为我提供相关实体的信息,而在其他情况下,它无法对相关实体执行任何操作,因为我没有包含它们:

无需 .Include() 即可工作;

这是一个我在没有 .Include(); 的情况下加载数据的示例

public class InvoiceService
{
    private ApplicationDbContext db { get; set; }
    public InvoiceService(ApplicationDbContext context)
    {
        db = context;
    }

    public Invoice Get(int id)
    {
        return db.Invoices.SingleOrDefault(x => x.Id == id);
    }
}

public partial class ShowInvoice : System.Web.UI.Page
{
    private InvoiceService invoiceService;

    private readonly ApplicationDbContext context = new ApplicationDbContext();
    protected void Page_Load(object sender, EventArgs e)
    {
        invoiceService = new InvoiceService(context);
        if (!IsPostBack)
        {
            int.TryParse(Request.QueryString["invoiceId"].ToString(), out int invoiceId);
            LoadInvoice(invoiceId);
        }
    }

    private void LoadInvoice(int invoiceId)
    {
        var invoice = invoiceService.Get(invoiceId);
        // Other code irrelevant to the question goes here.
    }
}

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

如您所见,该公司的信息肯定是通过的,但并未明确包含在内。

没有 .Include() 就无法工作;

相反,我在同一个项目中对发票进行了一些映射,并且在获取相关实体属性值时出现 NullReferenceExceptions,因为我没有.Include()

此方法获取指定公司的所有已批准时间表条目。此视图模型专门用于为发票处理时间表条目的关联(因此您将根据所选的时间表条目开具发票)。

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);
}

NullReferenceExceptions 发生在将时间表实体映射到视图模型的工厂中:

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();

为什么 Entity Framework 有时很乐意在没有我明确请求数据的情况下给我数据,有时它需要我明确请求数据,否则会引发错误?

我如何知道何时需要明确包含我正在查找的数据以及何时不需要?

【问题讨论】:

    标签: c# entity-framework


    【解决方案1】:

    实体框架使用延迟加载来加载子关系。 要使模型中的延迟加载起作用,应使用 virtual 关键字标记。 Ef 会覆盖它并添加延迟加载支持。

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

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

    【讨论】:

    • 非常感谢您的解释。那么,您是否会说最佳实践是在构建模型时始终将相关实体设为virtual?还是在您的查询中明确地.Include() 他们会更好?
    • 延迟加载通常会导致很多性能问题。每次延迟加载发生时,查询都会访问您的数据库。在您的示例中,您正在执行 foreach 并访问延迟加载的属性:这将导致 Select N+1 问题。在这种情况下,包含会更好。 OTOH 使用延迟加载使我们编写的代码更少,并且可以在您不循环使用它或性能不重要的地方使用。
    • 在我最近的项目中,出于性能原因,我们决定禁用延迟加载。在启用延迟加载的项目上,最好的方法是标记虚拟导航属性和集合(由于虚拟表方法查找对性能的影响几乎不明显),但在适当的情况下使用它(避免 Select N+1 问题)。
    【解决方案2】:

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

    【讨论】:

    • 不要厚脸皮,但你确定吗?我在时间表上的实体(StaffMember 和 Task)没有标记为虚拟,我必须 .Include 他们。看起来是相反的方式?
    • 没有包含哪些属性不会加载?都是收藏吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多