如何在实体框架 6.1 中仅加载子对象的某些字段
本文关键字:对象 加载 字段 实体 框架 | 更新日期: 2024-10-25 12:38:45
我正在研究一个有两个类的模型,Product
和 Transaction
。
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);
但它抛出了一个异常:
包含路径表达式必须引用导航属性 在类型上定义。使用虚线路径进行参考导航 属性和用于集合导航的 Select 运算符 性能
编辑澄清:我想要实现的实际上是不加载TransactionId
,并且在加载Transaction
时Customer
。
要实现您需要的内容,除了将查询投影到匿名类型或 DTO 之外,您别无选择。如您所见,在 Include
扩展方法中,您可以只指定要加载的相关实体,这些实体在带有表的内部联接中转换(或多个联接,请参阅引用链接中的 T备注 部分),但这并不意味着您将从相关实体加载所有属性。如果调用 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
列。此时,数据不会仍然加载,您只是定义一个稍后转换为 sql 的 linq 查询。何时发生?,何时调用ToList
扩展方法。
作为最后一个建议,我建议您查看Automapper。将实体与其各自的 DTO 映射后,查询可以如下所示:
var product = db.Products.Where(p => p.ProductId == productId)
.ProjectTo<ProductDTO>()
.ToList();
有关此链接ProjectTo
扩展方法的详细信息
你也可以尝试匿名投影
var product = db.Products.Where(p => p.ProductId = productId)
.Select(pr=> new
{
product = pr,
transactionDates = pr.Transactions.Select(tr=>tr.Date),
}.ToList();
我认为使用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
除了其他答案之外,如果您不想重新实现 DTO 类,您可以这样做:
public class ProductDTO : Product
{
}
因此,DTO 类将具有必填字段,EF 不会引发任何异常。