如何仅在Entity Framework 6.1中加载子对象的某些字段?

时间:2016-04-21 09:39:05

标签: c# entity-framework linq-to-entities entity-framework-6

我正在开发一个包含两个类ProductTransaction的模型。

public class Product
{
    [DataMember]
    public Guid ProductId {get; set;}

    [DataMember]
    public virtual ICollection<Transaction> Transactions { get; set; }
}

public class Transaction
{
    [DataMember]
    public Guid TransactionId {get; set;}

    [DataMember]
    public DateTimeOffset Date { get; set; }

    [DataMember]
    public String Customer { get; set; }
}

如何进行检索产品及其交易日期的查询?我试过像

这样的东西
var product = db.Products.Include(p => p.Transactions.Select(t => new { t.Date })).Where(p => p.ProductId = productId);

但它引发了一个例外:

  

Include路径表达式必须引用导航属性   在类型上定义。使用虚线路径进行参考导航   属性和集合导航的Select运算符   特性

编辑以澄清: 我想要实现的是在加载TransactionId时实际上加载CustomerTransaction

4 个答案:

答案 0 :(得分:5)

要实现您的需求,除了将您的查询投射到匿名类型或DTO之外别无选择。正如您所看到的,在Include扩展方法中,您只需指定要加载的相关实体,该实体将在带有表的内连接中进行转换(或多个连接,请参阅备注引用链接中的部分),但这并不意味着您将从相关实体加载所有属性。如果您调用Select方法,您可以选择要投影的列,但无法使用实体类型投影Linq to Entities查询,则必须使用我在上面评论的两个选项之一。所以,我的建议是在业务逻辑层创建一组类(DTO)来投影查询结果,例如:

 public class ProductDTO
 {
    [DataMember]
    public Guid ProductId {get; set;}
    [DataMember]
    public virtual IEnumerable<DateTime> TransactionDates { get; set; }
 }

稍后你可以这样做:

var product = db.Products.Where(p => p.ProductId = productId)
                         .Select(pr=> new ProductDTO
                         {
                           ProductId = pr.ProductId,
                           TransactionDates = pr.Transactions.Select(tr=>tr.Date),
                         }.ToList();

在这种情况下,请参阅我不需要调用Include扩展方法,因为在Select我正在从Transactions表格中投射一列。在这一点上,数据仍未加载,您只是定义了一个linq查询,后来将其转换为sql。当发生这种情况时?,当您拨打ToList分机方法时。

作为最后一项建议,我建议你看看Automapper。将实体与各自的DTO映射后,您的查询可能就是这样:

var product = db.Products.Where(p => p.ProductId == productId)
                         .ProjectTo<ProductDTO>()    
                         .ToList();

有关此link

ProjectTo扩展方法的详情

答案 1 :(得分:2)

您也可以尝试Anonymous projection

var product = db.Products.Where(p => p.ProductId = productId)
                         .Select(pr=> new 
                         {
                           product = pr,
                           transactionDates = pr.Transactions.Select(tr=>tr.Date),
                         }.ToList();

答案 2 :(得分:-1)

我认为使用 DTO 是最好的模式。如果您不返回值,匿名投影效果很好。

另一种选择是映射到匿名类型,然后创建实体

public MyEntity Products => db.Products.Where(p => p.ProductId = productId)
     .Select(pr=> new {
         product = pr,
         transactionDates = pr.Transactions.Select(tr=>tr.Date),
     }
     .AsEnumerable()
     .Select(e => new MyEntity { ... }); // Initialize a Linq entity here

答案 3 :(得分:-1)

除了其他答案,如果你不想重新实现 DTO 类,你可以这样做:

public class ProductDTO : Product
{
}

因此 DTO 类将具有必填字段并且 EF 不会抛出任何异常。