由C#EF核心调用的挂起的选择查询,MSSQL Management Studio可以很好地执行查询。为什么?

时间:2019-01-09 13:01:47

标签: c# sql-server entity-framework-core ef-core-2.2

我不能为您提供足够的数据来重现错误,所以我将为您提供尽可能多的数据。

我有一个从EF核心执行的select语句。

var bookings = context.Booking
                      .Where(booking => booking.ConsigneeNumber == customer.GetCustomerTarget().Code 
                             && booking.CreatedAt >= from 
                             && booking.CreatedAt < to 
                             && booking.BookingLine.Any(b => b.BookingLineSpecification
                                                        .Any(c => c.CurrencyCode == code))
                            )

                     .Include(booking => booking.BookingLine)
                         .ThenInclude(bl => bl.BookingLineSpecification)
                         .ThenInclude(bls => bls.UnitType)
                     .Include(booking => booking.BookingLine)
                         .ThenInclude(bl => bl.BookingLineAddress)
                         .ThenInclude(bla => bla.Country)
                     .Include(booking => booking.BookingLine)
                         .ThenInclude(bl => bl.BookingLineAddress)
                         .ThenInclude(bla => bla.PostalCode)
                     .Include(booking => booking.BookingLine)
                         .ThenInclude(bl => bl.BookingLineSpecification)
                         .ThenInclude(bls => bls.RelBookingLineSpecificationSalesInvoiceDetail)
                         .ThenInclude(Rel => Rel.SalesInvoiceDetail);

在MSSQL服务器上挂起的SQL查询本身变为:

     (@__GetCustomerTarget_Code_0 bigint,@__from_1 datetime2(7),@__to_2 datetime2(7),@__code_3 varchar(255))
        SELECT [booking].[Id], 
    [booking].[booking_provider_id], 
    [booking].[booking_status_id], 
    [booking].[consignee_name], 
    [booking].[consignee_number], 
    [booking].[created_at], 
    [booking].[created_by], 
    [booking].[currency_code], 
    [booking].[deliveryNumber], 
    [booking].[description], 
    [booking].[destroyed_at], 
    [booking].[destroyed_by], 
    [booking].[inter_company_number], 
    [booking].[invoicee_name], 
    [booking].[invoicee_number], 
    [booking].[is_create], 
    [booking].[location_id], 
    [booking].[location_name], 
    [booking].[maturity_level_id], 
    [booking].[number], 
    [booking].[order_number], 
    [booking].[provider_key], 
    [booking].[shipment_id], 
    [booking].[system_responsible_id], 
    [booking].[updated_at], 
    [booking].[updated_by]  
    FROM [Integration].[booking] AS [booking]  
WHERE ((([booking].[consignee_number] = @__GetCustomerTarget_Code_0) 
AND ([booking].[created_at] >= @__from_1)) 
AND ([booking].[created_at] < @__to_2)) 
AND EXISTS (      
SELECT 1      
FROM [Integration].[booking_line] AS [b]      
WHERE EXISTS (          
SELECT 1          
FROM [Integration].[booking_line_specification] AS [c]          
WHERE ([c].[currency_code] = @__code_3) AND ([b].[Id] = [c].[booking_line_id])) AND ([booking].[Id] = [b].[booking_id]))

在MSSQL Management Studio中执行时,此语句在零秒(但有些毫秒)内执行。 但是,C#应用程序遇到超时。

当我在MSSQL上使用内部工具时,可以看到该spid已挂起,并永久等待。 但是原因似乎正在改变。一开始是由于IO_COMPLETION。然后是它的SOS_YIELD_〜某物,最后是PAGEIOLATCH_SH 它保持在最终状态

我一生都无法弄清为什么MSSQL可以毫无问题地执行查询。 但是EF似乎无法利用索引。还是我完全想不到的其他东西。

我简直没有主意。谁能指出我可能会有所帮助的方向?

我尝试过:

在Visual stuido 2017中运行它。在发布模式下运行。我尝试了启用懒惰模式,并且不使用包含。我已经尝试删除了懒惰的负载,包括在内,只是为了看看是否可以收回预订。

不。 MSSQL似乎拒绝EF Core使用索引。

问题是,仅当我提供某些参数时查询才会挂起。 其他参数也可以。具体来说,如果我提供了不同的货币代码,那么无论查询是否暂停,这似乎都对MSSQL有所不同。

我已经根据MSSQL Management Studio中的执行计划完全重建了有效执行此查询所需的索引。

任何可能需要的进一步信息,请让我知道,我会尽力而为。

更新实际执行计划:

enter image description here

更新2: 我想指出的是,该数据库当前正在用于开发,因此该数据库,我的软件以及介于两者之间的任何内容都在我的“控制之下”。

据我看来没有经验的头脑可以控制任何事情:)

因此,关于如何更好地调试问题或提出更多数据要求的任何建议都将充满活力和赞赏。并且有可能实现,尤其是在提示如何向您提供信息时! (还有我)

SQL事件探查器:打开与数据库的连接:

set quoted_identifier on
set arithabort on
set numeric_roundabort off
set ansi_warnings on
set ansi_padding on
set ansi_nulls on
set concat_null_yields_null on
set cursor_close_on_commit off
set implicit_transactions off
set language us_english
set dateformat mdy
set datefirst 7
set transaction isolation level read committed

2 个答案:

答案 0 :(得分:3)

这是我们对任何希望知道我们为解决此问题所做的工作的人的答案。 要解决这一事实,这是一个“ hack”,即实体框架核心实际上不允许您按设计执行此操作,或者不允许您按设计自行执行。

在实际执行要求特定数据的C#代码之前,我们先执行此存储过程。

这将查找可用于查询的缓存执行计划,该查询看起来很像EF生成的查询。然后将它们一一杀死。

这不是一件好事。 1,它要求用户具有执行此操作的权限。任何应用程序都不应具有此权限级别。您可以争论自己想要的一切“可能拥有”,但实际上,所有人都知道它不应该拥有。 2,
在代码中,我要在执行select语句之前执行存储过程以使应用程序执行。真。真? 嗯,是。我是。干净吗?没有。干净吗?没有。我什至不建议这样做,因为这样做有很多陷阱,如果您更改参数或以任何方式更改查询,则必须重新构建“ like”字符串。我首先使用SQL Profiler嗅探查询来构建它,然后将尽可能唯一的位复制到其他可能的查询执行中。可能有不正确的身份证明吗?是。 如果您每秒运行一次百万次选择,则这可能不是可行的解决方案。等等

但这对我有用。

EF核心在存储过程中执行其查询。 但是,如果使用存储过程,则不适用于包含。 试图把头缠住,我的大脑流血了。所以这个痛苦的解决方案就是我正在使用的解决方案。

/****** Object:  StoredProcedure [dbo].[WipePlanForDraftLoad]    Script Date: 11/01/2019 11:50:03 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

-- =============================================
-- Author:  Morten Bork, Henrik Davidsen    
-- Create date: 2019-01-11
-- Description: Procedure to Wipe a cached plan for a query that sort of matches a specific string
-- =============================================
CREATE PROCEDURE [dbo].[WipePlanForDraftLoad]
    -- Add the parameters for the stored procedure here

AS
BEGIN

declare plans cursor 
for 
select t1.plan_handle from sys.dm_exec_cached_plans t1
left outer join sys.dm_exec_query_stats t2 on t1.plan_handle = t2.plan_handle
outer apply sys.dm_exec_sql_text(sql_Handle) 
where text like '%(@__customerCode_0 bigint,@__from_1 datetime2(7),@__to_2 datetime2(7),@__currencyCode_3 varchar(255))%WHERE (\[c\].\[currency_code\] = @__currencyCode_3) AND (\[b\].\[Id\] = \[c\].\[booking_line_id\])) AND (\[booking\].\[Id\] = \[b\].\[booking_id\])%' escape '\'

declare @plan_handle varbinary(64)
open plans
fetch next from plans into @plan_handle
while @@FETCH_STATUS = 0
begin
    dbcc freeproccache(@plan_handle)
    fetch next from plans into @plan_handle
end
close plans
deallocate plans
END
GO

答案 1 :(得分:0)

前段时间我遇到了类似的问题,这与在EF中不会自动设置的SSMS中设置的某些默认设置有关。如果可以,请尝试通过代码为EF会话设置ARITHABORT ON,然后查看查询执行的方式是否与SSMS中的执行方式相同。