3

我致力于我的应用程序的速度优化,我发现 LINQ(或 EF)正在为我创建一些运行缓慢的奇怪 SQL。

这是一些代码:

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));

从理论上讲,它应该创建了需要 25 条记录的 SQL 语句,但分析器显示了以下 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 是 IQueryable 对象,我的所有排序和过滤逻辑都在该对象上完成。

这是我想出来的:如果我不做任何过滤,SQL 是正常的,上面有 SELECT TOP (25)。但是每当我过滤某些东西时,就会变得一团糟。这是我的过滤器之一的代码:

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();

它没有任何约束。我如何使这个 LINQ-EF 东西产生一些好的 SQL ?

提前谢谢!

4

5 回答 5

3

你没有说是什么_DatabaseView,但我根据你的结果疯狂猜测它不是ObjectQuery<T>. 这可以解释你的问题。ObjectQuery将转换为 SQL;IEnumerable<T>.Skip()惯于。调用AsQueryable()enumerable不足以实现这一点。

例如,这个:

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

...不会将其TOP放入 SQL 中。

但是这个:

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

... 将要。

再说一遍:你还没有说什么_DatabaseView . 直接在您的设备上尝试此操作ObjectContext,您会发现它有效。错误出现在您用来分配的代码中_DatabaseView,您没有向我们展示。

于 2010-10-25T15:43:30.913 回答
2

您可以实际更改正在使用的 SQL 的唯一方法是编写您自己的并使用它,而不是使用生成的 SQL。

您的意思是 LINQ 的 Skip 和 Take 部分未转换为 SQL。我认为这是因为您执行 LINQ 的方式。

尝试类似的东西

(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)

相反,看看它是否会对生成的代码产生影响。

编辑不知何故,我错过了你说它应该是需要 25 条记录的 SQL 的部分。

于 2010-10-25T14:07:21.153 回答
2

LINQ Parser 肯定会考虑 LINQ to Entities 查询中的SkipTake方法并生成正确的表达式树,然后对象服务将表达式树转换为命令树,该命令树将传递给数据库提供程序以生成特定的 SQL 查询。
在这种情况下,这两种方法分别使用 aWHERE [Extent1].[row_number] > 50SELECT TOP (25)for影响生成的 SQLSkipTake

现在,您确定您正在Profiler中查看正确的跟踪吗?我建议通过在进入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();
于 2010-10-25T14:44:57.313 回答
0

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

于 2010-10-25T14:14:49.057 回答
0

尝试在 Select 之前移动 Skip 和 Take。

于 2010-10-25T15:05:48.493 回答