C# 如何使用EF优化此查询

C# 如何使用EF优化此查询,c#,entity-framework-core,C#,Entity Framework Core,您好,我是实体框架的新手。我只是想知道是否有办法改进我的实现。这是密码 public async Task<List<Record>> GetRecordsByBatchId(string batchId, string source) { List<string> idList = new List<string>(); //[1] Get all parent ID from table 1 wit

您好,我是实体框架的新手。我只是想知道是否有办法改进我的实现。这是密码

 public async Task<List<Record>> GetRecordsByBatchId(string batchId, string source)
    {
        List<string> idList = new List<string>();


        //[1] Get all parent ID from table 1 with a filter of source and batchId
        var parentIds= await _context.Set<FirstTable>()
            .Where(a => a.IsActive
                && a.BatchId.Equals(batchId)
                && a.Source.Equals(source)).Select(b => b.ParentId).ToListAsync();

        if (parentIds.Count() == 0)
        {
            return new List<Record>();
        }


        //[2] Query idNumber of each parentId from [1] to SecondTable
        List<long> idNumber = await _context.Set<SecondTable>()
            .Where(a => parentIds.Contains(a.Id))
            .Select(b => b.IdNumber).ToListAsync();


        //[3] Query Record/s that contains idNumber from previous query [2]. it is possible that 1 or 
        //more records has same idNumber
        List<Risk> recordByIdNumber = await _context.Set<SecondTable>()
            .Where(a => idNumber.Contains(a.IdNumber)).ToListAsync();


       //[4] In this part I just want to group the records in [3] by Id number and sort each group 
       //by its endorsementNumber in descending order and return the record with highest endorsement 
       //number for each group 
        return (from record in recordByIdNumber 
                group record by record.IdNumber into g
                orderby g.Key
                select g.OrderByDescending(risk =>risk.EndorsementNumber).FirstOrDefault()).ToList();
    }
}
第二个表的模型

    public class FirstTable
{
    public Guid? ParentId{ get; set; }
    public string BatchId { get; set; }
    public string Source { get; set; }
    public bool IsActive { get; set; }
}
 public class SecondTable
{
    public Guid Id{ get; set; }
    public int EndorsementNumber { get; set; }
    public long IdNumber { get; set; }
}
注意:我只是在模型中包含了必要的属性

这种方法正如预期的那样发挥作用。我只是想知道这些查询是否有可能被优化,因为对于SecondTable表只有一个查询

任何帮助都将不胜感激,提前感谢。

var parentIds=\u context.Set()
var parentIds =  _context.Set<FirstTable>()
        .Where(a => a.IsActive
            && a.BatchId.Equals(batchId)
            && a.Source.Equals(source)).Select(b => new { b.parentId });


var risks = await (from s in  _context.Set<SecondTable>()
             join p in parentIds on s.Id equals p.parentId
             join r in _context.Set<SecondTable>() on s.IdNumber equals r.IdNumber
             select r).GroupBy(r=>r.IdNumber)
                       .Select(r=> r.OrderByDescending(risk =>risk.EndorsementNumber).FirstOrDefault())
            .ToArrayAsync();
   return risks;
.其中(a=>a.IsActive &&a.BatchId.Equals(BatchId) &&a.Source.Equals(Source)).Select(b=>new{b.parentId}); var risks=await(来自_context.Set()中的s) 在s上的parentId中加入p。Id等于p.parentId 在s.IdNumber上的_context.Set()中加入r等于r.IdNumber 选择r).GroupBy(r=>r.IdNumber) .Select(r=>r.OrderByDescending(risk=>risk.背书编号).FirstOrDefault()) .ToArrayAsync(); 退货风险;
您可以有1个查询,而不是3个查询。随着第一个查询的行数增加,它的性能会更好

编辑:正如评论中提到的@SvyatoslavDanyliv,根据EF的版本和您使用的提供者,组获取操作可能无法工作。您可能需要按如下操作分离查询和组:

var result = await (from s in  _context.Set<SecondTable>()
                 join p in parentIds on s.Id equals p.parentId
                 join r in _context.Set<SecondTable>() on s.IdNumber equals r.IdNumber
                 select r).ToArrayAsync();

var risks = result.GroupBy(r=>r.IdNumber)
                  .Select(r=> r.OrderByDescending(
                           risk =>risk.EndorsementNumber).FirstOrDefault())
            .ToArray();
                
return risks;
var result=await(来自_context.Set()中的s)
在s上的parentId中加入p。Id等于p.parentId
在s.IdNumber上的_context.Set()中加入r等于r.IdNumber
选择r).ToArrayAsync();
var风险=结果.GroupBy(r=>r.IdNumber)
.选择(r=>r.OrderByDescending(
risk=>risk.背书编号).FirstOrDefault()
.ToArray();
退货风险;
var parentIds=\u context.Set()
.其中(a=>a.IsActive
&&a.BatchId.Equals(BatchId)
&&a.Source.Equals(Source)).Select(b=>new{b.parentId});
var risks=await(来自_context.Set()中的s)
在s上的parentId中加入p。Id等于p.parentId
在s.IdNumber上的_context.Set()中加入r等于r.IdNumber
选择r).GroupBy(r=>r.IdNumber)
.Select(r=>r.OrderByDescending(risk=>risk.背书编号).FirstOrDefault())
.ToArrayAsync();
退货风险;
您可以有1个查询,而不是3个查询。随着第一个查询的行数增加,它的性能会更好

编辑:正如评论中提到的@SvyatoslavDanyliv,根据EF的版本和您使用的提供者,组获取操作可能无法工作。您可能需要按如下操作分离查询和组:

var result = await (from s in  _context.Set<SecondTable>()
                 join p in parentIds on s.Id equals p.parentId
                 join r in _context.Set<SecondTable>() on s.IdNumber equals r.IdNumber
                 select r).ToArrayAsync();

var risks = result.GroupBy(r=>r.IdNumber)
                  .Select(r=> r.OrderByDescending(
                           risk =>risk.EndorsementNumber).FirstOrDefault())
            .ToArray();
                
return risks;
var result=await(来自_context.Set()中的s)
在s上的parentId中加入p。Id等于p.parentId
在s.IdNumber上的_context.Set()中加入r等于r.IdNumber
选择r).ToArrayAsync();
var风险=结果.GroupBy(r=>r.IdNumber)
.选择(r=>r.OrderByDescending(
risk=>risk.背书编号).FirstOrDefault()
.ToArray();
退货风险;

是的,查询1-3可以并且应该合并。为此,您需要在模型中具有导航属性。第一个表和第二个表之间似乎存在一对多关系。让我们使用客户和订单代替

class Customer {
    int CustomerId
    string BatchId
    ICollection<Order> Orders
}

class Order {
    int OrderId
    int CustomerId
    Customer Customer
    Risk Risk
}
class客户{
int客户ID
字符串批处理ID
I收款单
}
阶级秩序{
整型医嘱ID
int客户ID
顾客顾客
风险
}
在这种情况下,只需将第三个查询编写为

List<Risk> = await _context.Orders.Where(o => o.Customer.BatchId == batchId)
    .Select(o => o.Risk).ToListAsync();
List=wait\u context.Orders.Where(o=>o.Customer.BatchId==BatchId)
.选择(o=>o.Risk).toListSync();

显然,我只是在猜测结构和关系。但希望这能让你开始。对我来说,
Contains()
是“代码气味”。第一次查询中很有可能会有一个很大的列表,并且contains()会在数据库中生成一个巨大的
IN
子句,这很容易使系统崩溃

是的,查询1-3可以而且应该组合在一起。为此,您需要在模型中具有导航属性。第一个表和第二个表之间似乎存在一对多关系。让我们使用客户和订单代替

class Customer {
    int CustomerId
    string BatchId
    ICollection<Order> Orders
}

class Order {
    int OrderId
    int CustomerId
    Customer Customer
    Risk Risk
}
class客户{
int客户ID
字符串批处理ID
I收款单
}
阶级秩序{
整型医嘱ID
int客户ID
顾客顾客
风险
}
在这种情况下,只需将第三个查询编写为

List<Risk> = await _context.Orders.Where(o => o.Customer.BatchId == batchId)
    .Select(o => o.Risk).ToListAsync();
List=wait\u context.Orders.Where(o=>o.Customer.BatchId==BatchId)
.选择(o=>o.Risk).toListSync();

显然,我只是在猜测结构和关系。但希望这能让你开始。对我来说,
Contains()
是“代码气味”。您的第一个查询很可能会有一个很大的列表,contains()会在数据库中生成一个巨大的
IN
子句,这很容易使系统崩溃,我认为需要对您的模型进行描述才能理解您的代码。Hello@RoyCohen我刚刚更新了我的问题,包括模型,thanksHi@Felix我刚刚再次编辑了模型,很抱歉造成混淆,因为我刚刚从实际的属性重命名了这些属性。@Edward-不仅仅是Id(尽管命名一致性会有所帮助)。应该有导航属性来实现关系(请参见答案中的示例!)!在关系数据库中,不仅仅是列的名称,还有强制一对多关系的外键约束。EF还需要实现对象之间的关系,而不是
xyz.Count()==0
y