选择联接查询中日期最少的行

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

我正在使用实体Framework和LINQ来执行这两个表之间的连接:

var query =
        orders.Join(
        orderdetails,
        order => order.Code,
        orderdt => orderdt.Order.Code,            
        (order, orderdt)            
        => new
        {
            Code = order.Code,
            WarehouseDivision = orderdt.WarehouseDivision,
            Type = order.Type,
            SupplierCode = order.SupplierCode,
            SupplierDescription = order.SupplierDescription,
            ExpectedDeliveryDate = orderdt.ExpectedDeliveryDate
        });

联接工作正常。现在,对于每个联接行,我需要选择具有最小ExpectedDelivery Date的行。关于如何实现这一目标的任何提示?

c# linq ef-code-first
1个回答
0
投票

我在您的加入中看到了一些奇怪的东西。

每个订单都有一个代码。每个OrderDetail都有一个订单,也有一个代码。

如果Order与OrderDetails之间存在一对多关系,则每个Order都具有零个或多个OrderDetails。每个OrderDetail都完全属于一个Order,即外键指向的Order。那是与OrderDetail.Order相同的Order吗?

在这种情况下,对于此代码的每个OrderDetail,Order.Code等于OrderDetail.Order.Code。对这些值执行联接不是很有用。

我认为您的意思是同时使用主键和外键。

无论采用哪种方式,无论您是在键上还是在属性代码上进行联接,解决问题的方法都是执行GroupJoin而不是Join。

如果您遵循了entity framework code first conventions,您将获得类似以下内容:

class Order
{
    public int Id {get; set;}    // Primary key
    ...                          // other Order properties

    // every Order has zero or more OrderDetails (one-to-many)
    public virtual ICollection<OrderDetail> OrderDetails {get; set;}
}

class OrderDetail
{
    public int Id {get; set;}
    public DateTime ExpectedDeliveryDate {get; set;}
    ...                          // other OrderDetail properties

    // every OrderDetail belongs to exactly one Order, using foreign key
    public int OrderId {get; set;}
    public virtual Order Order {get; set;}
}

可能是您对属性使用了不同的标识符,但主要是虚拟属性。

在实体框架中,非虚拟属性表示表中的列,虚拟属性表示表之间的关系(一对多,多对多)。

要求

从每个订单中,给我一些属性,以及具有最小ExpectedDeliveryDate的OrderDetail的一些属性。

GroupJoin解决方案

GroupJoin在主/外键上:

var OrdersWithOldestOrderDetail = dbContext.Orders  // GroupJoin Orders 
    .GroupJoin(dbContext.OrderDetails,              // with OrderDetails
    order => order.Id,                              // from every Order take the Id
    orderDetail => orderDetail.OrderId,             // from every OrderDetail take the OrderId
    (order, orderDetailsOfThisOrder) => new         // take the Order with all its matchin OrderDetails
    {                                               // to make one new
        // Select the Order properties that you plan to use:
        ...

        // Select the OrderDetail with the minimum ExpectedDeliveryDate
        // To do this, order by ascending deliveryDate
        // then Select the properties you want to used
        // and take the FirstOrDefault
        EarliestDeliveredOrderDetail = orderDetailsOfThisOrder
           .OrderBy(orderDetail => orderDetail.ExpectedDeliveryDate)
           .Select(orderDetail => new
           {
               ...
           })
           .ToList(),
    });

如果您真的想加入您所说的属性:

var result = dbContext.Orders                  // GroupJoin Orders 
    .GroupJoin(dbContext.OrderDetails,         // with OrderDetails
    order => order.Code,                       // from every Order take the Code
    orderDetail => orderDetail.Order.Code,     // from every OrderDetail take Order.Code
    (order, orderDetailsWithThisCode) => new   // take the Order with all its matchin OrderDetails
    {   
        ... etc, same as above

使用ICollection的更简单解决方案

[如果您使用的是实体框架,则实际上需要选择“所有带有OrderDetails的订单”,“所有带有学生的学校”,“带有组件的所有产品”:所有项目以及所有相关子项目,通常,使用ICollections而不是自己执行连接会更简单:

var OrdersWithOrderDetails = dbContext.Orders.Select(order => new
{
    // Select the Order properties that you plan to use:
    ...

    EarliestDeliveredOrderDetail = order.OrderDetails
        .OrderBy(orderDetail => orderDetail.ExpectedDeliveryDate)
        .Select(orderDetail => new
        {
            // Select the OrderDetail properties that you plan to use:
            ...
        })
        .FirstOrDefault(),
   })

看看如果您使用ICollection而不是(Group-)join会感觉如何?实体框架知道您的关系,并使用正确的(外部)键为您执行正确的联接。

事实上,我很少创建自己的Join。大多数时候,我使用虚拟属性。

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