C# 如何改进LInQ性能存储库模式

C# 如何改进LInQ性能存储库模式,c#,asp.net-mvc,postgresql,performance,linq,C#,Asp.net Mvc,Postgresql,Performance,Linq,我在WebAPI中使用了通用存储库模式,数据库为postgreSQL。事务表有300000到1000000个数据。出于报告目的,我必须将事务数据的计数连接到其他两个表中。LinQ查询加载大约1.5分钟。提供数据。如何优化或改进性能 var data = (from emp in (await new Repository<emp>().GetAll()).ToList() join trans1 in (await new Repository<trans1>()

我在WebAPI中使用了通用存储库模式,数据库为postgreSQL。事务表有300000到1000000个数据。出于报告目的,我必须将事务数据的计数连接到其他两个表中。LinQ查询加载大约1.5分钟。提供数据。如何优化或改进性能

var data = (from emp in (await new Repository<emp>().GetAll()).ToList()
    join trans1 in (await new Repository<trans1>().GetAll()).ToList()
    on emp.staffid equals trans1?.leadstaffid
    join trans2 in (await new Repository<trans2>().GetAll())
    on trans1.statusid equals trans2.statusid
    into tassta
    from ts in tassta.DefaultIfEmpty()
    group new { emp, trans1, ts }
    by new { emp.staffid, emp.fullname } into grp
    select new ReportTs
    {
        particulars = grp.FirstOrDefault().emp.fullname.Trim(),
        id = grp.FirstOrDefault().trans1.id,
        staffid = grp.FirstOrDefault().trans1.staffid,
        PByDep = grp.Where(ys => ys.trans1.statusid == 2).Select(ys1 => ys1.trans1.statusid).Count(),
        PFT = grp.Where(ys => ys.trans1.statusid == 3).Select(ys1 => ys1.trans1.statusid).Count(),
        PByC = grp.Where(ys => ys.trans1.statusid == 4).Select(ys1 => ys1.trans1.statusid).Count(),
        PFR = grp.Where(ys => ys.trans1.statusid == 5).Select(ys1 => ys1.trans1.statusid).Count(),
        inid = grp.FirstOrDefault().trans1.inid,
        rowtotal = grp.Count(ys => ys.trans1.statusid == null) +
                  grp.Count(ys => ys.trans1.statusid == 2) +
                  grp.Count(ys => ys.trans1.statusid == 3) +
                  grp.Count(ys => ys.trans1.statusid == 4) +
                  grp.Count(ys => ys.trans1.statusid == 5) ,
        PApp = true,
        CDate = false
    }).Distinct().ToList();
var data=(来自emp in(wait new Repository().GetAll()).ToList())
将trans1加入(等待新存储库().GetAll()).ToList()中
关于emp.staffid等于trans1?.leadstaffid
将trans2加入(等待新存储库().GetAll())
在trans1.statusid上等于trans2.statusid
进入塔斯塔
来自tasta.DefaultIfEmpty()中的ts
新组{emp,trans1,ts}
由新的{emp.staffid,emp.fullname}进入grp
选择新报告
{
详情=grp.FirstOrDefault().emp.fullname.Trim(),
id=grp.FirstOrDefault().trans1.id,
staffid=grp.FirstOrDefault().trans1.staffid,
PByDep=grp.Where(ys=>ys.trans1.statusid==2)。选择(ys1=>ys1.trans1.statusid)。Count(),
PFT=grp.Where(ys=>ys.trans1.statusid==3)。选择(ys1=>ys1.trans1.statusid)。Count(),
PByC=grp.Where(ys=>ys.trans1.statusid==4)。选择(ys1=>ys1.trans1.statusid)。Count(),
PFR=grp.Where(ys=>ys.trans1.statusid==5)。选择(ys1=>ys1.trans1.statusid)。Count(),
inid=grp.FirstOrDefault().trans1.inid,
rowtotal=grp.Count(ys=>ys.trans1.statusid==null)+
grp.Count(ys=>ys.trans1.statusid==2)+
grp.Count(ys=>ys.trans1.statusid==3)+
grp.Count(ys=>ys.trans1.statusid==4)+
grp.Count(ys=>ys.trans1.statusid==5),
PApp=真,
CDate=false
}).Distinct().ToList();

这里的问题是,从数据库获取所有数据后,您正在内存中运行查询

我假设您的数据库中有一个名为“emp”的表。当你这样做的时候

 (await new Repository<emp>().GetAll()).ToList()
当然,如果有很多元组,这需要花费很多时间

获取所有数据后,使用Linq对这些数据运行内存内查询

为了提高性能,必须从第一行和第二行中删除“ToList()”,这将具体化数据

完成此操作后,必须重写查询,因为您编写的查询无法转换为SQL查询

您的目标应该是拥有一个可以对您的数据库运行的查询,以便只获取所需的数据

---编辑---

这里有两个例子。 在第一个示例中,所有数据都将从数据库中获取,然后查询将在内存中运行(正如您现在所做的)。在第二种方法中,查询将在数据库中运行,您将只获取所需的数据

 public class Repository<T>()
 {
      public Task<IQueryable<T>> GetAll(){...}
 }

 public class Examples 
 {

      public async static Task Example1()
      {
           var repository = new Repository<emp>();

           var emps =  await repository.GetAll().ToList();

           var reports = from emp in emps
           where emp.Id > 10
           select new ReportData(){
                ...
           }


      }

      public async static Task Example2()
      {
           var repository = new Repository<emp>();

           var emps =  await repository.GetAll();

           var reports = (from emp in emps
           where emp.Id > 10
           select new ReportData(){
                ...
           }).ToList();


      }
 }
公共类存储库()
{
公共任务GetAll(){…}
}
公开课示例
{
公共异步静态任务Example1()
{
var repository=新存储库();
var emps=await repository.GetAll().ToList();
var报告=来自emp中的emp
其中emp.Id>10
选择new ReportData(){
...
}
}
公共异步静态任务示例2()
{
var repository=新存储库();
var emps=await repository.GetAll();
var报告=(来自emp中的emp)
其中emp.Id>10
选择new ReportData(){
...
}).ToList();
}
}
请记住,尽管语法非常相似,但这两个示例所做的事情却截然不同


在第一种情况下,Linq查询将在foreach循环中编译,在第二种情况下,它将在postgres查询中结束。这意味着在这种情况下,您无法调用无法转换为postgres查询的方法(即detacts=grp.FirstOrDefault().emp.fullname.Trim())

其中有多少数据?@Stefan,对于“trans1”表,截至目前,它有多达30万个。它将在一个月内收集多达100万份数据。“emp”(employee)表目前有35000个数据,trans2是trans1的父表。它已被id列引用。首先,ToList()是可疑的,您必须依赖Querable而不是Enumerable。您可能希望用它生成一个存储过程。。。您正在使用实体框架吗?另外,Lakh不是一个国际单位,请使用数千或数百万(如果使用特定值很重要)。@JHBonarius,请您在postgreSQL中共享上述linQ查询到存储过程的示例代码片段,并在存储库模式中调用进程。这将帮助我解决问题。谢谢你的回复。我不清楚上面的说法。请提供基于上述linQ查询(我已经发布)的示例代码片段。这将帮助我更好地理解。提前谢谢。我在回答中添加了两个示例。第二个示例不一定有效,除非您在投影之前执行
ToEnumerable()
。(linq对sql的限制)。(对此不完全确定,但这是我的经验)。请注意,最后一个查询可能会实际工作。EF core现在很聪明。@JHBonarius做ToEnumerable()实现了IQueryable,就像ToList()一样,所以这回到了第一个场景是的,我知道。我想说的是,linq到sql不能转换所有内容,所以有时必须转换才能使查询正常工作。例如,
\u ctx.Entities.Where(x=>x.Id>10.ToEnumerable()。选择(x=>MyFunction(x)).ToList()
 public class Repository<T>()
 {
      public Task<IQueryable<T>> GetAll(){...}
 }

 public class Examples 
 {

      public async static Task Example1()
      {
           var repository = new Repository<emp>();

           var emps =  await repository.GetAll().ToList();

           var reports = from emp in emps
           where emp.Id > 10
           select new ReportData(){
                ...
           }


      }

      public async static Task Example2()
      {
           var repository = new Repository<emp>();

           var emps =  await repository.GetAll();

           var reports = (from emp in emps
           where emp.Id > 10
           select new ReportData(){
                ...
           }).ToList();


      }
 }