如何加载多个相关实体,但不加载他的所有字段,包括?

时间:2019-07-02 16:12:17

标签: entity-framework database-performance

我有一个使用include子句加载许多相关实体的方法,但这对创建查询来说太大了。我需要加载许多相关的实体,但我只想加载对我很重要的字段。

public RECEIPT_REMITS GetByIDWithIncludes(string UUID) 
{ 
   return Context.RECEIPT_REMITS.Where(h => h.UUID == UUID) .
   Include(r => r.DEPOSITE) 
  .Include(r => r.PROVIDER) 
  .Include(r => r.RECEIPT_KINDS) 
  .Include(r => r.RECEIPT_REMITS_DETAIL.Select(d => 
    d.RECEIPT_REMITS_SERIES)).FirstOrDefault(); 
} 

现在,该语句正确地加载了数据,但速度很慢,而且还带来了我不需要的字段。我该怎么办?

1 个答案:

答案 0 :(得分:1)

实体代表数据记录。完整的数据记录。这使得它们是糟糕的选择,无法用于其他目的,例如视图模型。相反,您应该采用视图模型,然后通过.Select()或通过将Automapper及其.ProjectTo<T>()方法(与EF的IQueryable实现集成)一起使用来映射实体以查看模型。即使某些视图模型最终与EF模型相同,它们仍可用于不同的目的。实体应始终代表与其关联的行,因此您不能期望告诉EF返回部分填充的实体。

例如,如果我有一个表ReceiptRemit,该表包含我关心的10列,并且我还想包含相关的“存款”,但是我只关心“存款”表中的ID和金额:

实体:

[Table("RECEIPT_REMIT")]
public class ReceiptRemit
{
   [Key]
   public string UUID { get; set; }
   public string Field1 { get; set; }
   public string Field2 { get; set; }
   // etc. etc.
   public virtual ICollection<Demerit> Demerits { get; set; } = new List<Demerit>();
}
[Table("DEMERITE")]
public class Demerit
{
   [Key]
   [Column("DEMERITE_ID")]
   public int DemeritId { get; set; }
   public decimal Amount { get; set; }
   public string Field1 { get; set; }
   public string Field2 { get; set; }
   // etc. etc. to match the table, but stuff I don't care about...
}

查看模型:

[Serializable]
public class ReceiptRemitViewModel
{
   public string UUID { get; set; }
   public string Field1 { get; set; }
   public string Field2 { get ; set; }
   // etc. etc.
   public IEnumerable<DemeritSummaryViewModel> Demerits { get; set; } = new List<DemeritSummaryViewModel>();
}
[Serializable]
public class DemeritSummaryViewModel
{
   public int DemeritId { get; set;}
   public decimal Amount { get; set; }
}

然后阅读:(.Select()

public ReceiptRemitViewModel GetByID(string UUID) 
{ 
   return Context.ReceiptRemits.Where(h => h.UUID == UUID)
      .Select(x => new ReceiptRemitViewModel
      {
         UUID = x.UUID.
         Field1 = x.Field1,
         Field2 = x.Field2,
         Demerits = x.Demerits.Select(d => new DemeritSummaryViewModel
         {
            DemeritId = d.DemeritId,
            Amount = d.Amount
         }).ToList(),
       }.Single();
}

要加载几个相关的摘要详细信息可能会有些麻烦,但是可以使用Automapper简化此操作。 Automapper可以根据约定找出最常见的映射详细信息,或针对任何不起作用的特定配置进行配置。设置完成后,以上内容将变为:

return Context.ReceiptRemits.Where(h => h.UUID == UUID)
  .ProjectTo<ReceiptRemitViewModel>()
  .Single();

或者,对于诸如批量操作之类的事情,您可以为相关数据定义不同的实体定义,并将这些替代实体注册到新的DbContext定义中。它必须是单独的DbContext声明,因为DbContext不能将2个实体映射到同一张表。对于需要加载相对较大数量的记录以检查并可能仅更新相关实体和字段的子集的情况,这种方法非常有用。