Linq to Entities 过滤器导航集合属性

问题描述 投票:0回答:3

我有一个订单类,它有两个集合类型的导航属性;订单详细信息和类别。 Order 与 OrderDetail 和 Category 之间存在一对多关系。订单可能有也可能没有与其关联的类别。 OrderDetail 记录有一个 CustomerID 字段。

我正在尝试检索具有与其关联的类别的订单列表以及特定客户的相应订单详细信息记录。如果可能的话,我想使用 linq toEntity 来实现这一点。

public class order
{
    public order()
    {
        OrderDetails = new list<OrderDetail>();
        Categories = new list<Category>();
    }
    public int OrderID { get; set; }
    public DateTime OrderDate { get; set; }
    public virtual List<OrderDetail> OrderDetails { get; set; }
    public virtual List<Category> Categories{ get; set; }
}

public class OrderDetail
{
    public int OrderDetailID { get; set; }
    public int CustomerID { get; set; }
    public virtual Order Order { get; set; }
}

public class Category
{
    public int CategoryID { get; set; }
    public string CategoryName { get; set; }
    public virtual Order Order { get; set; }
}

如果我首先从 OrderDetail 实体开始,我可以让它工作,如下所示,但如果我想先从 Order 实体开始,我该如何编写它?

var query = from od in _dbCtx.OrderDetails
                .Include("Order")
                .Include("Order.Categories")
                where od.CustomerID == custID && od.Order.Categories.Count > 0
                select od;
c# linq entity-framework linq-to-entities
3个回答
3
投票

你可以试试这个:

var query =_dbCtx.Orders.Include("OrderDetails")
                        .Include("Categories")
                        .Where(o=>o.Categories.Count>0)
                        .SelectMany(o=>o.OrderDetails.Where(od=>od.CustomerID == custID));

此查询中的关键是

SelectMany
扩展方法,该方法用于将
Where
的结果展平为一个集合。

编辑1

由于您禁用了延迟加载,因此执行我的查询时获得的 OrderDetails 中的

Order
导航属性为
null
。一种选择是在使用结果时使用
Load
方法:

foreach(var od in query)
{
   // Load the order related to a given OrderDetail
   context.Entry(od).Reference(p => p.Order).Load();

   // Load the Categories related to the order
   context.Entry(blog).Collection(p => p.Order.Categories).Load();
}

另一种选择可能是返回匿名类型:

var query =_dbCtx.Orders.Include("OrderDetails")
                        .Include("Categories")
                        .Where(o=>o.Categories.Count>0)
                        .SelectMany(o=>o.OrderDetails.Where(od=>od.CustomerID == custID).Select(od=>new {Order=o,OrderDetail=od}));

但我不喜欢这些解决方案中的任何一个。最直接的方法是你从一开始就已经有的查询。


0
投票

实体框架的默认设置是允许延迟加载和动态代理。

在这种情况下,当您在关系属性上使用 virtual 关键字时,这些“应该”(如果您没有在 EF 中禁用它)使用延迟加载进行加载。

延迟加载 在需要时加载关系属性。示例:

var load = data.Orders.OrderDetails.Tolist() // Would load all OrderDetails to a list.

//Below would load all OrderDetails that has a OrderId smaller than 5
var loadSpecific = data.Orders.Where(x=> x.OrderId < 5).OrderDetails.ToList() 

您描述的情况是Eager Loading('Include'语句),没有任何问题。但如果您打算使用它,我会考虑使用下面的语法。如果您决定更改关系属性的名称,这将导致编译错误。

var load = data.Orders
.Include(x => x.OrderDetails)
.Include(x => x.Categories)

我建议您花 10-15 分钟时间阅读本文: https://msdn.microsoft.com/en-us/data/jj574232.aspx


0
投票

如果您使用 EF Core,这可能对您有帮助:

using (var context = new BloggingContext())
{
    var filteredBlogs = context.Blogs
        .Include(blog => blog.Posts.Where(post => post.BlogId == 1))
        .ThenInclude(post => post.Author)
        .Include(blog => blog.Posts)
        .ThenInclude(post => post.Tags.OrderBy(postTag => postTag.TagId).Skip(3))
        .ToList();
}

https://learn.microsoft.com/pt-br/ef/core/querying/lated-data/eager#filtered-include

© www.soinside.com 2019 - 2024. All rights reserved.