【问题标题】:When exactly does Entity Framework Fire a SQL Command?Entity Framework 究竟何时触发 SQL 命令?
【发布时间】:2013-04-09 04:14:12
【问题描述】:

假设我有这样的代码:

public void TestWhenSqlFires()
{
    var db = new Db(); // EF DbContext
    var items = db.SimpleObjects; // SimpleObject implements interface IId
    var byId = WhereId(items, 1);
    var array = byId.ToArray();
}

protected IEnumerable<IId> WhereId(IEnumerable<IId> items, int id)
{
    return items.Where(i => i.Id == id);
}

在 TestWhenSqlFires() 中的哪一行,SQL 将实际针对数据库运行?

(这是一个从 this answer 上的 cmets 衍生出来的问题)

【问题讨论】:

    标签: sql ef-code-first entity-framework-5


    【解决方案1】:

    一种发现和测试自己的方法:

    打开 SQL Server Management Studio,打开一个新查询,选择 EF 将运行的数据库并运行此查询:

    SELECT top 10 deqs.last_execution_time AS [Time], dest.TEXT AS [Query]
    FROM sys.dm_exec_query_stats AS deqs
    CROSS APPLY sys.dm_exec_sql_text(deqs.sql_handle) AS dest
    ORDER BY deqs.last_execution_time DESC
    

    这会告诉您过去对数据库运行的 10 个查询。

    在 TestWhenSqlFires() 的第一行设置断点,运行您的代码,然后在遍历每一行后运行上述查询。你会发现:

    // C# Line 1
    var db = new Db();
    
    --SQL Line 1
    SELECT TABLE_SCHEMA SchemaName, TABLE_NAME Name FROM INFORMATION_SCHEMA.TABLES
        WHERE TABLE_TYPE = 'BASE TABLE'
    
    // C# Line 2
    var items = db.SimpleObjects;
    
    --SQL Line 2
    SELECT COUNT(*) FROM [sys].[databases] WHERE [name]=@1
    
    SELECT [GroupBy1].[A1] AS [C1] FROM (
        SELECT COUNT(1) AS [A1] FROM [dbo].[__MigrationHistory] AS [Extent1]
    ) AS [GroupBy1]
    
    (@1 nvarchar(4000))SELECT TOP (1) [Project1].[C1] AS [C1],
        [Project1].[MigrationId] AS [MigrationId],
        [Project1].[Model] AS [Model]  FROM (
            SELECT [Extent1].[MigrationId] AS [MigrationId],
                [Extent1].[Model] AS [Model], 1 AS [C1]
                FROM [dbo].[__MigrationHistory] AS [Extent1]
         )  AS [Project1]  ORDER BY [Project1].[MigrationId] DESC
    
    // C# Line 3
    var byId = WhereId(items, 1);
    
    --SQL Line 3
    
    // C# Line 4
    var array = byId.ToArray();
    
    --SQL Line 4
    SELECT [Extent1].[Id] AS [Id], [Extent1].[Stuff] AS [Stuff]
        FROM [dbo].[SimpleObject] AS [Extent1]
    

    最终的 SQL 查询是 EF 实际获取数据。之前的查询只是在预热 - 验证数据库是否存在,它正在使用 EF5 迁移历史,并且它与当前的迁移历史哈希匹配。

    所以答案是 - 第 4 行,在调用 .ToArray() 之后(或任何枚举集合的调用,如 .ToList()、foreach 等)。值得注意的是,将其传递给接受 IEnumerable 的方法,即使涉及特定的 Generic,也不会枚举集合,因此不会在必要时提前触发 SQL。

    【讨论】:

    • 克里斯,检查最后一个查询:SELECT Id, Stuff FROM SimpleObject。您可能已经预料到了:SELECT Id, Stuff FROM SimpleObject WHERE Id=1。这意味着您的 Where 扩展方法适用于包含所有 SimpleObjects(由实际查询返回)的对象集合。所以它确实枚举了集合,因为没有其他方法可以完成 Where 方法。如果您传递的是 IQueryable 而不是 IEnumerable,它将不会枚举。
    • @Olexander 我现在明白了,我很抱歉 - 我误解了。我现在看到,如果我将其更改为 IQueryable,它实际上会更改 SQL - 你是对的,它在内存中枚举它 - 太糟糕了!
    • 没问题,我们终于达成协议了;)
    猜你喜欢
    • 2010-10-23
    • 1970-01-01
    • 1970-01-01
    • 2012-07-10
    • 1970-01-01
    • 2011-09-19
    • 1970-01-01
    • 2020-11-15
    • 1970-01-01
    相关资源
    最近更新 更多