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

时间:2019-04-01 12:12:38

标签: c# entity-framework

当我必须.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.
    }
}

以下是结果,其中包括与我要求的发票相关的公司数据: enter image description here

如您所见,该公司的信息肯定来自但未明确包含。<​​/ p>

没有.include()不能工作;

相反,在同一个项目中,我已经完成了一些与发票的映射,由于我没有.Include(),因此在获取相关实体的属性值时得到了NullReferenceExceptions。

此方法获取指定公司的所有批准的时间表条目。此视图模型仅在处理发票的时间表条目的关联时使用(因此,您将根据所选的时间表条目进行开票)。

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

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

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

为什么实体框架有时很乐意在没有我明确请求数据的情况下向我提供数据,有时又需要我明确地请求数据或抛出错误?

我怎么知道何时需要显式包含要查找的数据以及什么时候不包含?

2 个答案:

答案 0 :(得分:1)

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

当您没有虚拟属性时,EF无法稍后再加载子关系数据,因此只有在使用Include进行初始数据加载期间才可以这样做。

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

答案 1 :(得分:1)

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