使用 LINQ 查询和创建具有一对多关系的对象

本文关键字:一对多 关系 对象 LINQ 查询 创建 使用 | 更新日期: 2024-10-30 04:56:13

在数据库中,我有两个具有一对多关系的表:

orders          suborders
-----------     -----------
id              id
name            order_id
                name

我想查询这些表并最终得到一个订单对象列表,每个对象都包含一个子订单对象列表(或空列表)。我还想在单个数据库查询中执行此操作,以便它性能良好。

在传统的SQL查询领域,我会做类似的事情(原谅伪代码):

rs = "select o.id, o.name, so.id, so.name from orders o left join suborders so on o.id = so.order_id order by o.id"
orders = new List<Order>
order = null
foreach (row in rs) {
    if (order == null || row.get(o.id) != order.id) {
        order = new Order(row.get(o.id), row.get(o.name), new List<Suborders>)   
        orders.add(order)
    }    
    if (row.get(so.id) != null) {
        order.suborders.add(new Suborder(row.get(so.id) row.get(so.name))
    }
}

有没有办法使用 LINQ 到实体来获取相同的结果对象结构?请注意,我想从查询中获取新对象,而不是实体框架生成的对象。

以下内容让我接近,但抛出异常:"LINQ to Entities 无法识别该方法..."

var orders = 
    (from o in Context.orders
     join so in Context.suborders on o.id equals so.order_id into gj
     select new Order 
     {
         id = o.id,
         name = o.name,
         suborders = (from so in gj select new Suborder
         {
             so.id,
             so.name
         }).ToList()
     }).ToList();

使用 LINQ 查询和创建具有一对多关系的对象

解决方案最终非常简单。关键是使用组联接来获取 SQL 对子订单进行左联接,并添加第二个 ToList() 调用以强制运行查询,这样您就不会尝试在 SQL 服务器上创建对象。

orders = Context.orders
    .GroupJoin(
        Context.suborders,
        o => o.id,
        so => so.order_id,
        (o, so) => new { order = o, suborders = so })
    .ToList()
    .Select(r => new Order
    {
        id = r.order.id,
        name = r.order.name,
        suborders = r.suborders.Select(so => new Suborder
        {
            id = so.id,
            name = so.name
        }.ToList()
    }).ToList();

此代码仅对所有对象及其子对象进行一次 SQL 查询。它还允许您将 EF 对象转换为所需的任何对象。

我总是为关系创建一个虚拟化属性

因此,只需将(添加属性)扩展到您的订单类:

  public class Order{
  ...
  List<Suborder> _suborders;
  public List<Suborder> Suborders{
  get {
        return _suborders ?? (_suborders = MyContext.Suborders.Where(X=>X.order_id==this.id).ToList());
       }
  ...
  }

因此,只有在您调用getters时,才会获取(提取)数据

这段代码怎么样?

您可以获取本地缓存。

List<Orders> orders = new List<Orders>();
private void UpdateCache(List<int> idList)
{
    using (var db = new Test(Settings.Default.testConnectionString))
    {
        DataLoadOptions opt = new DataLoadOptions();
        opt.LoadWith<Orders>(x => x.Suborders);
        db.LoadOptions = opt;
        orders = db.Orders.Where(x => idList.Contains(x.Id)).ToList();
    }
}
private void DumpOrders()
{
    foreach (var order in orders)
    {
        Console.WriteLine("*** order");
        Console.WriteLine("id:{0},name:{1}", order.Id, order.Name);
        if (order.Suborders.Any())
        {
            Console.WriteLine("****** sub order");
            foreach (var suborder in order.Suborders)
            {
                Console.WriteLine("'torder id:{0},id{1},name:{2}", suborder.Order_id, suborder.Id, suborder.Name);
            }
        }
    }
}
private void button1_Click(object sender, EventArgs e)
{
    UpdateCache(new List<int> { 0, 1, 2 });
    DumpOrders();
}

输出示例如下

*** order
id:0,name:A
****** sub order
    order id:0,id0,name:Item001
    order id:0,id1,name:Item002
    order id:0,id2,name:Item003
*** order
id:1,name:B
****** sub order
    order id:1,id0,name:Item003
*** order
    id:2,name:C
****** sub order
    order id:2,id0,name:Item004
    order id:2,id1,name:Item005