C# 优化LINQ查询

C# 优化LINQ查询,c#,sql-server,linq,linq-to-sql,query-optimization,C#,Sql Server,Linq,Linq To Sql,Query Optimization,我有一份报告,显示了向某个确定的商户发出的订单,在我需要为支付状态添加过滤器之前,该报告工作正常 以下是我构建查询的方式,按筛选器筛选: var queryOrder = context.Orders.Select(m=>m); if (viewModel.InitialDate.HasValue) queryOrder = queryOrder.Where(m => m.CreatedDate.Date >= viewModel.InitialDate.Value)

我有一份报告,显示了向某个确定的商户发出的订单,在我需要为支付状态添加过滤器之前,该报告工作正常

以下是我构建查询的方式,按筛选器筛选:

var queryOrder = context.Orders.Select(m=>m);

if (viewModel.InitialDate.HasValue)
    queryOrder = queryOrder.Where(m => m.CreatedDate.Date >= viewModel.InitialDate.Value);

(...) /* continues building the query, filter by filter */


if (viewModel.SelectedPaymentStatus != null)
    queryOrder = queryOrder.Where(m => viewModel.SelectedPaymentStatus.Contains(m.Payments.Select(p => p.PaymentStatusId).Single().ToString()));

queryOrder = queryOrder.Where(m => m.MerchantId == merchantId);
当我运行
queryOrder
时,即使它只是
queryOrder.Count()
,执行它也需要1分钟以上的时间。使用SQL Server的分析工具,我将生成的查询提取为:

SELECT [t0].[Id], [t0].[CustomerId], [t0].[MerchantId], [t0].[OrderNumber], [t0].[Amount], [t0].[SoftDescriptor], [t0].[ShippingMethod], [t0].[ShippingPrice], [t0].[IpAddress], [t0].[SellerComment], [t0].[CreatedDate]
FROM [dbo].[Order] AS [t0]
WHERE ([t0].[MerchantId] = @p0) 
AND ((CONVERT(NVarChar,(
       SELECT [t1].[PaymentStatusId]
       FROM [dbo].[Payment] AS [t1]
       WHERE [t1].[OrderId] = [t0].[Id]
    ))) IN (@p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8))
@p0参数是merchantId的Guid,@p1到@p8是数字字符串
“1”
“8”
,表示paymentStatusId

如果我跳过这一行:

if (viewModel.SelectedPaymentStatus != null)
    queryOrder = queryOrder.Where(m => viewModel.SelectedPaymentStatus.Contains(m.Payments.Select(p => p.PaymentStatusId).Single().ToString()));

查询在1秒内运行。但是当我使用它的时候,它的表现就触动了我的心。关于如何解决这个问题有什么建议吗?

所有的查询都会延迟,这是linq的优点和缺点。尝试拆分查询并使用一些内存中的结果。 尝试删除第一个查询(实际上没有多大意义,您返回的是同一个集合),然后将第二个查询修改为这样,看看是否有什么不同

var clause = context.Orders.Payments.Select(p => p.PaymentStatusId).Single().ToString();
if (viewModel.SelectedPaymentStatus != null)
    var queryOrder = context.Orders.queryOrder.Where(m => viewModel.SelectedPaymentStatus.Contains(clause));

我已经对问题片段进行了注释:

if (viewModel.SelectedPaymentStatus != null) {

    // Give me only orders from queryOrder where...
    queryOrder = queryOrder.Where(

        // ...my viewModel's SelectedPaymentStatus collection...
        m => viewModel.SelectedPaymentStatus.Contains(

                 // ...contains the order's payment's PaymentStatusId...
                 m.Payments.Select(p => p.PaymentStatusId).Single()

                                          // ... represented as a string?!
                                         .ToString()
                                          // Why are database IDs strings?
             )
        );
}
viewModel.SelectedPaymentStatus
似乎是字符串的集合;因此,您要求数据库将
PaymentStatusId
转换为nvarchar,并与
SelectedPaymentStatus
的元素进行字符串比较。恶心

由于
viewModel.SelectedPaymentStatus
较小,因此最好创建一个临时
列表
并在查询中使用:

if (viewModel.SelectedPaymentStatus != null) {

    // Let's do the conversion once, in C#
    List<int> statusIds = viewModel.SelectedPaymentStatus.Select( i => Convert.ToInt32(i) ).ToList();

    // Now select the matching orders
    queryOrder = queryOrder.Where(
                     m => statusIds.Contains(
                              m.Payments.Select(p => p.PaymentStatusId).Single())
                          )
                 );
}
if(viewModel.SelectedPaymentStatus!=null){
//让我们用C进行一次转换#
List statusIds=viewModel.SelectedPaymentStatus.Select(i=>Convert.ToInt32(i)).ToList();
//现在选择匹配的订单
queryOrder=queryOrder.Where(
m=>statusIds.Contains(
m、 Payments.Select(p=>p.PaymentStatusId).Single())
)
);
}

您是说生成的sql运行得更快吗?这是实体框架还是linq to sql?不,生成的sql即使在sql management studio上也运行得很慢。它是linq到sqlqueryOrder.Where(m=>viewModel.SelectedPaymentStatus.Contains(m.Payments.Select(p=>p.PaymentStatusId.Single().ToString());这行代码试图做什么?ToString()可能是瓶颈的一大部分,因为它对查询中的所有值进行转换,您能用另一种方式进行转换吗?也许跳过Single并使用First()?第一个查询并不像您想象的那么糟糕。身份选择不会真正引起任何问题(特别是在iqueryable上,查询提供者基本上会意识到这是不可操作的。他真正做的是确保将
queryOrder
键入为
iqueryable
,而不是底层集合类型。只需调用
AsQueryable
,就可以更好地实现这一点,但更好地传达含义。另一个选项是不要使用
var
,而是手动将变量键入
IQueryable
。没错,只是因为DbSet已经实现了IQueryable,所以它看起来是多余的。顺便说一句,我认为区别在于去掉Contains子句并使其成为内存中的结果。