简体   繁体   English

LINQ或Entity Framework创建无界的SQL语句

[英]LINQ or Entity Framework creates unbounded SQL statement

I work on speed optimization of my application, and what I have found is that LINQ (or EF) is creating some strange SQL for me that works slow. 我正在研究我的应用程序的速度优化,我发现LINQ(或EF)正在为我创建一些工作缓慢的奇怪SQL。

Here is some code : 这是一些代码:

SomeList.AddRange(_databaseView
                .Select(l=> new SomeViewModel
                                {
                                    Date = l.Date,
                                    Details = l.Details,
                                    Level = l.LevelName,
                                    Id = l.ViewID,
                                    Message = l.Message,
                                    ProjectName = l.projectName,
                                    StatusId = l.StatusID,
                                    StatusName = l.StatusName
                                })
                .Skip(50)
                .Take(25));

And in theory it should've created SQL statement that takes 25 record but profiler showes following SQL for it : 从理论上讲,它应该创建一个SQL语句,它需要25条记录但是分析器会在SQL之后显示它:

    SELECT [Extent1].[Date]  AS [Date],
       [Extent1].[ID]            AS [ID],
       [Extent1].[LevelID]       AS [LevelID],
       [Extent1].[StatusID]      AS [StatusID],
       [Extent1].[projectName]   AS [projectName],
       [Extent1].[LevelName]     AS [LevelName],
       [Extent1].[StatusName]    AS [StatusName],
       [Extent1].[Message]       AS [Message],
       [Extent1].[Details]       AS [Details],
       [Extent1].[LogViewID]     AS [LogViewID]
FROM   (SELECT [v_MyView].[Date]       AS [Date],
               [v_MyView].[ProjectID]     AS [ProjectID],
               [v_MyView].[LevelID]       AS [LevelID],
               [v_MyView].[StatusID]      AS [StatusID],
               [v_MyView].[projectName]   AS [projectName],
               [v_MyView].[LevelName]     AS [LevelName],
               [v_MyView].[StatusName]    AS [StatusName],
               [v_MyView].[Message]       AS [Message],
               [v_MyView].[Details]       AS [Details],
               [v_MyView].[ViewID]        AS [ID]
        FROM   [dbo].[v_MyView] AS [v_MyView]) AS [Extent1]

_databaseView is IQueryable object on which all my sorting and filtering logic is done. _databaseView是IQueryable对象,我的所有排序和过滤逻辑都在其上完成。

Here is something I figured : if I don't do any filtering SQL is normal with SELECT TOP (25) on it. 这是我想到的:如果我不做任何过滤SQL是正常的SELECT TOP(25)就可以了。 But whenever I do filtering something gets messed up. 但每当我做过滤时,一些东西都搞砸了。 Here is the code to one of my filters: 这是我的一个过滤器的代码:

if (Filters.ProjectName != null && Filters.ProjectName[0] != 0)    // check if "all" is not checked
    _databaseView = Filters.ProjectName
        .Join(_databaseView,  f => f, l => l.ProjectID,  (f,l) => new SomeViewModel
                                                                           {
                                                                               Date = l.Date,
                                                                               Details = l.Details,
                                                                               LevelName = l.LevelName,
                                                                               ViewID = l.ViewID,
                                                                               Message = l.Message,
                                                                               projectName = l.projectName,
                                                                               StatusID = l.StatusID,
                                                                               StatusName = l.StatusName
                                                                           })
    .AsQueryable();

And it is without any constraint. 它没有任何限制。 How do I make this LINQ-EF thing to produce some good SQL ? 如何使这个LINQ-EF产生一些好的SQL呢?

Thx in advance! Thx提前!

You don't say what _DatabaseView is, but my wild guess based on your results is that it's not an ObjectQuery<T> . 你没有说_DatabaseView是什么,但根据你的结果我的猜测是它不是一个ObjectQuery<T> Which would explain your problem. 这可以解释你的问题。 ObjectQuery will convert to SQL; ObjectQuery将转换为SQL; IEnumerable<T>.Skip() won't. IEnumerable<T>.Skip()不会。 Calling AsQueryable() on an enumerable is not enough to make this happen. 调用AsQueryable()上的枚举不足以实现这一目标。

For example, this: 例如,这个:

var foo = MyObjectContext.SomeEntitySet.AsEnumerable().AsQueryable().Take(10);

...won't put the TOP in the SQL. ...不会把TOP放在SQL中。

But this: 但是这个:

var bar = MyObjectContext.SomeEntitySet.Take(10);

... will. ......会的。

Once again: You haven't said what _DatabaseView is . 再一次:你还没有说_DatabaseView 什么。 Try this operation directly on your ObjectContext and you will see that it works. 直接在ObjectContext上尝试此操作,您将看到它的工作原理。 The bug is in the code you use to assign _DatabaseView , which you haven't shown us. 该错误位于用于分配_DatabaseView的代码中,您尚未向我们展示。

The only way you can actually change the SQL that is being used would be to write your own and use that instead of using the generated SQL. 实际更改正在使用的SQL的唯一方法是编写自己的SQL并使用它而不是使用生成的SQL。

You mean the Skip and Take portions of the LINQ not being converted to SQL. 你的意思是LINQ的Skip和Take部分没有被转换为SQL。 I think it is because of the way that you are doing the LINQ. 我认为这是因为你在做LINQ的方式。

Try something like 尝试类似的东西

(From l In DataBaseView Select new SomeViewModel
                                {
                                    Date = l.Date,
                                    Details = l.Details,
                                    Level = l.LevelName,
                                    Id = l.ViewID,
                                    Message = l.Message,
                                    ProjectName = l.projectName,
                                    StatusId = l.StatusID,
                                    StatusName = l.StatusName
                                }).Skip(50).Take(25)

Instead and see if it makes a difference in the generated code. 相反,看看它是否对生成的代码产生影响。

Edit Somehow I missed the portion where you said it should be SQL that takes 25 records. 编辑不知怎的,我错过了你说它应该是SQL需要25条记录的部分。

LINQ Parser definitely consider the Skip and Take methods in your LINQ to Entities query and produces the correct expression tree and then Object Services converts the expression tree to a command tree which will be passed to the database provider for the specific SQL query generation. LINQ Parser肯定会考虑LINQ to Entities查询中的SkipTake方法并生成正确的表达式树,然后对象服务将表达式树转换为命令树,该命令树将传递给数据库提供程序以生成特定的SQL查询。
In this case this 2 methods affecting the generated SQL with a WHERE [Extent1].[row_number] > 50 and SELECT TOP (25) for Skip and Take respectively. 在这种情况下,这2个方法分别影响生成的SQL,其中WHERE [Extent1].[row_number] > 50SELECT TOP (25)分别为SkipTake

Now, are you sure that you are looking at the right trace in the Profiler ? 现在,您确定在Profiler中查看正确的跟踪吗? I suggest take a look at ObjectQuery.ToTraceString method by writing the below code before going to Profiler and then debug through your code and examine the value of sql variable: 我建议在转到Profiler之前通过编写下面的代码来查看ObjectQuery.ToTraceString方法,然后通过代码调试并检查sql变量的值:

var query = _DatabaseView.Select(l=> new SomeViewModel {
                                                     Date = l.Date,
                                                     Details = l.Details,
                                                     Level = l.LevelName,
                                                     Id = l.ViewID,
                                                     Message = l.Message,
                                                     ProjectName = l.projectName,
                                                     StatusId = l.StatusID,
                                                     StatusName = l.StatusName})
                         .Skip(50)
                         .Take(25));
string sql = (query as ObjectQuery).ToTraceString();

如果你无法使用适当的索引使SQL运行得足够好,那么你总是可以尝试编写一个存储过程并从LINQ调用它。

尝试在选择之前移动Skip and Take。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM