如何仅在Entity Framework 6.1中加载子对象的某些字段?
我正在研究一个有两个类的模型, Product
和Transaction
。
public class Product { [DataMember] public Guid ProductId {get; set;} [DataMember] public virtual ICollection 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
时实际上没有加载TransactionId
和Customer
。
为了实现您的需求,除了将查询投影到匿名类型或DTO之外,您没有其他选择。 正如您所看到的,在Include
扩展方法中,您只需指定要加载的相关实体,该实体将在带有表的内部联接中进行转换(或多个联接,请参阅引用链接中的“ 备注”部分),但这并不是t意味着你要从相关实体加载所有属性。 如果您调用Select
方法,您可以选择要投影的列,但不能使用实体类型投影Linq to Entities查询,您必须使用我在上面评论的两个选项之一。 所以,我的建议是在业务逻辑层创建一组类(DTO)来投影查询结果,例如:
public class ProductDTO { [DataMember] public Guid ProductId {get; set;} [DataMember] public virtual IEnumerable 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() .ToList();
有关ProjectTo
扩展方法的更多信息, ProjectTo
链接
你也可以尝试匿名投影
var product = db.Products.Where(p => p.ProductId = productId) .Select(pr=> new { product = pr, transactionDates = pr.Transactions.Select(tr=>tr.Date), }.ToList();