C# 涉及Max()的SQL到LINQ转换

C# 涉及Max()的SQL到LINQ转换,c#,mysql,entity-framework,linq,C#,Mysql,Entity Framework,Linq,我想检索具有最新订单和总金额的前5名商户。然而,我似乎无法得到总金额。我认为问题在于LastOrderGrandTotal=x.FirstOrDefault().GrandTotal。下面的代码语法是LINQ方法语法 var list = _ApplicationDbContext.OrderTransaction .Where(x => x.Status != 0) .GroupBy(x => x.MerchantUserId)

我想检索具有最新订单和总金额的前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; }

    }

我会像这样重写查询。您甚至可能不需要
first或default
,因为使用
GroupBy
我相信至少总会有一条记录

var result = data
    .Where(x => x.Status != 0)
    .GroupBy(x => x.MerchantUserId)
    .Select(q => new
    {
        q.Key,
        Orders = q.OrderByDescending(q => q.CreatedDate),
    })
    .Select(x => new DashboardActiveMerchantStore { 
        MerchantUserId = x.Key, 
        CreatedDate = x.Orders.FirstOrDefault().CreatedDate,
        LastOrderGrandTotal = x.Orders.FirstOrDefault().GrandTotal,
    })
    .Take(5)
    .ToList();

当您使用实体框架时,一种完全不同、更自然的方法是可能的。您可以使用
虚拟ICollection

我想检索具有最新订单和总金额的前5名商户

实体框架中的类 似乎
商户
订单交易
之间存在一对多的关系:每个商户都有零个或多个订单交易,并且每个订单交易都是一个确切的何时商户的交易,即外键机制所指的商户

如果您遵循了,您将拥有类似于以下内容的类:

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;}
}
实体框架知道您的一对多关系。由于您使用的是
虚拟ICollection
,因此它将自动为您创建(组)加入

现在你不需要所有的商人,你不需要没有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).

您将有“商家(Id和名称)及其最近的订单(CreatedData和GrandTotal)”,如果需要,请添加一个额外的选择,将其转换为五个
仪表板ActiveMerchantstores

我认为
x.FirstOrDefault()
不是同一个,因为您在调用它之后正在对列表进行排序。你最安全的选择是保留整个列表,进行排序,然后在排序后选择。但我不认为我们可以在保留整个列表之后进行排序。因为列表包含重复条目,我希望它按商户分组。我最初的计划是根据创建的数据按商户订单分组并获取总计,但我似乎无法根据您的SQL查询访问总计,它应该是
LastOrderGrandTotal=x.GrandTotal
。但我不确定您的SQL查询如何正常工作。因为您有groupby on
MerchantUserId
。因此,您只能在select中使用
MerchantUserId
和聚合函数,而不能使用其他列。请检查SQL查询是否成功运行@Tanyijing认为SQL查询不可能工作;它将抛出
…在选择列表中无效,因为它既不包含在聚合函数中,也不包含在groupby子句中
“With groupby始终存在记录”-即使我们将空列表分组?@CaiusJard则没有
x
可供选择,因此您无需担心子集合中没有项目。因此,是的,空列表=无记录。
public class OrderDbContext : DbContext
{
    public DbSet<Merchant> Merchants {get; set;}
    public DbSet<TransactionOrder> TransactionOrders {get; set;}
}
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(),
    })
.Where(merchant => merchant.LastOrder != null) // at leas one Order with non-zero Status
    .OrderbyDescending(merchant => merchant.LastOrder.CreatedDate)
    .Take(5).