c# - 涉及 Max() 的 SQL 到 LINQ 转换
问题描述
我想检索具有最新订单和总金额的前 5 位商家。但是,我似乎无法获得总金额。我认为问题出在LastOrderGrandTotal= x.FirstOrDefault().GrandTotal
. 语法下面的代码采用 LINQ 方法语法
var list = _ApplicationDbContext.OrderTransaction
.Where(x => x.Status != 0)
.GroupBy(x => x.MerchantUserId)
.Select(x => new DashboardActiveMerchantStore { MerchantUserId = x.Key, CreatedDate = x.Max(c => c.CreatedDate), LastOrderGrandTotal= x.FirstOrDefault().GrandTotal })
.OrderByDescending(x => x.CreatedDate)
.Take(5)
.ToList();
为进一步说明,上述查询的 SQL 语法为
select TOP(5) MerchantUserId,MAX(CreatedDate),GrandTotal from OrderTransaction
group by MerchantUserId
order by CreatedDate desc
我有一个类来存储从 LINQ 语法中检索的数据
public class DashboardActiveMerchantStore
{
public string MerchantName { get; set; }
public double LastOrderGrandTotal { get; set; }
public Guid MerchantUserId { get; set; }
public DateTime CreatedDate { get; set; }
}
解决方案
当您使用实体框架时,一种完全不同的、更自然的方法是可能的。您可以使用virtual ICollection<...>
.
我想检索具有最新订单和总金额的前 5 位商家。
实体框架中的类
Merchants
和之间似乎存在一对多的关系OrderTransactions
:每个 Merchant 都有零个或多个 OrderTransaction,并且每个 OrderTransaction 都是恰好发生在 Merchant 时的交易,即外键 MechantId 所指的 Merchant。
如果您遵循实体框架约定,您将拥有类似于以下内容的类:
public class Merchant
{
public Guid Id {get; set;} // primary key
public string Name {get; set;}
...
// Every Merchant has zero or more TransactionOrders (one-to-many)
public virtual ICollection<TransactionOrder> TransactionOrders {get; set;}
}
public class TransactionOrder
{
public Guid Id {get; set;} // primary key
public DateTime CreatedDate {get; set;}
public int Status {get; set;}
public double GrandTotal {get; set;}
...
// every TransactionOrder is an order of a Merchant, using foreign key
public Guid MerchantId {get; set;}
public virtual Merchant Merchant {get; set;}
}
当然还有 DbContext:
public class OrderDbContext : DbContext
{
public DbSet<Merchant> Merchants {get; set;}
public DbSet<TransactionOrder> TransactionOrders {get; set;}
}
这就是实体框架检测表、表的列以及表之间的一对多关系所需的全部内容。只有当你偏离约定时,比如你想使用不同的属性名,或者不同的表名,你才需要属性或流畅的 API。
在实体框架中,表的列由非虚拟属性表示。虚拟属性表示表之间的关系(一对多、多对多)
获得前 5 名商家
如果你想要一些关于“商家......以及一些关于他们的 OrderTransactions 的信息”的信息,你可以从dbContext.Merchants
;
如果您想要一些关于“OrderTransactions that ...,每个都有一些关于他们的商家的信息”的信息,你从dbContext.OrderTransactions
.
我要查询最近订单和总金额前5位的商户
所以你想要Merchants
. 从这些商家中,您至少想了解他们的Id
和他们的Name
,以及关于他们的一些信息OrderTransactions
。
不是所有的 OrderTransaction,只有关于它们最后一个 OrderTransaction 的信息,其状态为非零。从最后一个 OrderTransaction 中,您需要 CreatedDate 和 GrandTotal。
既然您已经获得了具有最后一个非零状态订单的商家,您不需要所有这些商家,您只需要具有最新 CreatedDate 的五个商家。
我希望以上是您的要求。这不是您的 SQL 所说的,但您的 SQL 没有获取 Merchants,它获取了 TransactionOrders 组。
var result = dbContext.Merchants
.Select(merchant => new
{
Id = merchant.Id,
Name = merchant.Name,
// from Every Merchant, get the Date and Total of their last
// non-zero status Order
// or null if there is no such order at all
LastOrder = merchant.OrderTransactions
.Where(orderTransaction => orderTransaction.Status != 0)
.OrderByDescending(orderTransaction => oderTransaction.CreatedDate)
.Select(orderTransaction => new
{
CreatedDate = orderTransaction.CreatedDate,
GrandTotal = orderTransaction.GrandTotal,
})
.FirstOrDefault(),
})
实体框架知道您的一对多关系。因为您使用它,
virtual ICollection<...>
它会自动为您创建 (Group-)Join。
现在您不想要所有商家,您不想要没有 LastOrder 的商家。他们没有任何非零状态的订单。从剩余的商户中,您只需要具有最新 LastOrder.CreatedDate 的五个商户。
因此,删除所有没有 LastOrders 的商家,按 LastOrder.CreatedDate 降序排列,然后取前 5 名。
继续 LINQ:
.Where(merchant => merchant.LastOrder != null) // at leas one Order with non-zero Status
.OrderbyDescending(merchant => merchant.LastOrder.CreatedDate)
.Take(5).
您将拥有“Merchants (Id and Name) with their LastOrder (CreatedData and GrandTotal)”,如果需要,添加一个额外的 Select 以将其转换为五个DashboardActiveMerchantStores
推荐阅读
- javascript - 在fabricJS中以相同的左原点缩放时保持相同的对象大小
- unix - Unix 树按模式忽略
- python - 将套接字从电子 UI 连接到 python 服务器时出现错误 400(错误请求)
- docker - 如何在 Azure ML 管道中正确指定私有 ACR Docker 映像?
- elasticsearch - 自动化 Kibana 仪表板创建
- java - Java中的静态方法和线程安全
- php - 如何不丢失 PHP 表单上的数组字段文本?
- flutter - Flutter - 当兄弟姐妹灵活时,扩展不会占用所有可用空间
- python - 使用递归反转字符串
- angular - 如何检查父组件中是否为 @Output() 参数提供了值