LINQ Query在服务器上超时,但立即在本地执行

时间:2017-01-02 09:50:20

标签: c# sql-server linq

之前我已经问过这个问题,并且因为我没有提供代码而被投票。但我坚信我的问题与代码无关,我非常渴望解决这个问题。

基本上,我需要从LINQ查询中获取2个不同的东西,查询正在构建并且非常长。

奇怪的是:当在数据库服务器上执行select LINQ查询时,它会在第二次计数时超时(无论我将CommandTimeout设置为多长时间),但是当我从数据库中获取相同的数据库实例时服务器并将其放在我的本地然后执行LINQ查询它立即得到两个计数。

I SQL Profiler在数据库服务器和本地服务器上的已翻译SQL查询,它们是相同的。当我在本地生成的翻译SQL查询手动在数据库服务器的SQL Management Studio上运行时,它也会立即返回计数。

这就是为什么我高度怀疑它不是代码问题或许多人提出的N + 1问题。我怀疑它是一个环境问题,但我不知道为什么会发生这种情况。如果有人之前遇到此问题,有人可以提出意见吗?如果您需要LINQ代码,请告诉我我可以缩短它并上传。

        IQueryable<StudentDm> studentQuery = GetListQuery();
        IQueryable<StudentTestDm> studentTestQuery = studentQuery.SelectMany(a => a.StudentTests);

        if (studentAcademicTestProficiencyGrowthParameter.TestTypeId.HasValue)
        {
            studentTestQuery = studentTestQuery.Where(a => a.TestTypeId == studentAcademicTestProficiencyGrowthParameter.TestTypeId);
        }

        DateTime serviceDateFrom = (studentAcademicTestProficiencyGrowthParameter.ServiceDateFrom.HasValue) ? studentAcademicTestProficiencyGrowthParameter.ServiceDateFrom.Value : DateTime.MinValue;
        DateTime serviceDateTo = (studentAcademicTestProficiencyGrowthParameter.ServiceDateTo.HasValue) ? studentAcademicTestProficiencyGrowthParameter.ServiceDateTo.Value : DateTime.MaxValue;

        // test 1 query
        IQueryable<StudentTestDm> studentTest1Query = studentTestQuery;

        if (studentAcademicTestProficiencyGrowthParameter.Test1SchoolYear.HasValue)
        {
            studentTest1Query = studentTest1Query.Where(a => a.Year == studentAcademicTestProficiencyGrowthParameter.Test1SchoolYear);
        }

        if (studentAcademicTestProficiencyGrowthParameter.Test1TestDate.HasValue)
        {
            studentTest1Query = studentTest1Query.Where(a => a.TestDate == studentAcademicTestProficiencyGrowthParameter.Test1TestDate);
        }

        // we grab the grouped students who has non-proficient test, and who does not have proficient test
        IQueryable<IGrouping<StudentDm, StudentTestDm>> groupedStudentWithTest1 = studentTest1Query.GroupBy(a => a.Student).Where(a => a.Select(b => b.TestLevel.ProficiencyId).Contains((int)LookUpEnum.LookUpEnum.TestLevelProficiency.NotProficient)
                                                                                                                                    && !a.Select(b => b.TestLevel.ProficiencyId).Contains((int)LookUpEnum.LookUpEnum.TestLevelProficiency.Proficient));

        // get the count of students whos attended less than 30 days of services
        count1 = groupedStudentWithTest1.Where(a => a.Key.StudentServices.Where(b => b.Date >= serviceDateFrom && b.Date <= serviceDateTo).GroupBy(b => b.Date).Count() < 30).Count();

        // test 2 query
        IQueryable<StudentTestDm> studentTest2Query = studentTestQuery;

        if (studentAcademicTestProficiencyGrowthParameter.Test2SchoolYear.HasValue)
        {
            studentTest2Query = studentTest2Query.Where(a => a.Year == studentAcademicTestProficiencyGrowthParameter.Test2SchoolYear);
        }

        if (studentAcademicTestProficiencyGrowthParameter.Test2TestDate.HasValue)
        {
            studentTest2Query = studentTest2Query.Where(a => a.TestDate == studentAcademicTestProficiencyGrowthParameter.Test2TestDate);
        }

        // we are grabbing the students who are not-proficient in test 1 but are proficient in test 2
        IQueryable<IGrouping<StudentDm, StudentTestDm>> groupedStudentWithImprovedTest2 = studentTest2Query.GroupBy(a => a.Student).Where(a => a.Select(b => b.TestLevel.ProficiencyId).Contains((int)LookUpEnum.LookUpEnum.TestLevelProficiency.Proficient))
                                                                                                                                   .Where(a => groupedStudentWithTest1.Select(b => b.Key.Id).Contains(a.Key.Id)); 

        // THIS QUERY TIMES OUT (ON Database Server): get the count students who attended less than 30 days of services
        count2 = groupedStudentWithImprovedTest2.Where(a => a.Key.StudentServices.Where(b => b.Date >= serviceDateFrom && b.Date <= serviceDateTo).GroupBy(b => b.Date).Count() < 30).Count();

1 个答案:

答案 0 :(得分:1)

请参阅聊天讨论。该查询遵循“可选过滤”模式,问题是参数嗅探。

查询是从SQL事件探查器捕获并在SSMS中运行但无法重现该问题。

但是,当从SQL事件探查器添加了所有前面的SET选项时,问题也在SSMS中重现

在查询末尾添加OPTION (RECOMPILE)似乎可以解决问题。

(具体来说,这是在sp_executesql

的结束报价之前添加的

此链接EF 6 Parameter Sniffing显示了通过LINQ添加OPTION (RECOMPILE)的方法。我将等待原始海报的回应,以确定这是否真的有效。

链接显示“可以使用EF6的拦截功能”

并显示一些像这样的代码(删节):

public class OptionRecompileHintDbCommandInterceptor : IDbCommandInterceptor
{


    public void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        addQueryHint(command);
    }

    public void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        addQueryHint(command);
    }

    private static void addQueryHint(IDbCommand command)
    {
        if (command.CommandType != CommandType.Text || !(command is SqlCommand))
            return;

        if (command.CommandText.StartsWith("select", StringComparison.OrdinalIgnoreCase) && !command.CommandText.Contains("option(recompile)"))
        {
            command.CommandText = command.CommandText + " option(recompile)";
        }
    }
}