【问题标题】:EF Core, FromSqlRaw with USE HINTEF Core,FromSqlRaw 与 USE HINT
【发布时间】:2021-02-23 23:21:43
【问题描述】:

我很困惑!
我有一个 ASP.Net Core 3 WebApi 应用程序,它运行良好:

var results = _context.Users.ToList();

但是,如果我尝试在 SQL 中添加“HINT”...

var results = _context.Users.FromSqlRaw("SELECT * FROM t_user OPTION(USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'))").ToList();

...然后它抛出这个异常...

Incorrect syntax near the keyword 'OPTION'.
Database 'HINT' does not exist. Make sure that the name is entered correctly.

为什么这个 SQL 会在 SQL Server Management Studio 中成功运行,却被 WebApi 误解了?


更新 #1

该死的。当您告诉 EF Core 使用 Raw SQL 时,它实际上将该 SQL 放入子句中,这就是我看到错误的原因。

所以,这是我正在尝试运行的(完全有效的)SQL:

SELECT * FROM t_user 
OPTION(USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'))

..但是,查看 SQL Server Profiler,EF Core 实际上正在尝试此 SQL,这是无效的...

SELECT [u].[user_id], [u].[user_name]
FROM (
    SELECT * FROM t_user 
    OPTION(USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'))
) AS [u]

该死的……

那么我们如何使用来自 EF Core 的提示?


更新 #2

因为我使用的是 EF Core,所以我按照this Microsoft article 中的说明将DbCommandInterceptor 添加到我的查询中。

在他们的示例中,它拦截 SQL 并将 " OPTION (ROBUST PLAN)" 附加到字符串。它表明它正在尝试运行此 SQL:

SELECT [u].[user_id], [u].[user_name]
FROM t_user 
OPTION (ROBUST PLAN)

如果我以 Microsoft 为例,并将其更改为使用 my HINT,那么我仍然会收到相同的错误,即“Database 'HINT' 不存在。"

private static void ManipulateCommand(DbCommand command)
{
    if (command.CommandText.StartsWith("-- Use hint: robust plan", StringComparison.Ordinal))
    {
        command.CommandText += " OPTION(USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'))";
    }
}

啊啊啊!为什么我不能使用提示?!

【问题讨论】:

  • 您确定它与您在 SSMS 中使用的 SQL Server 版本和兼容性级别相同吗?这不是 EF 错误,这是直接来自 SQL Server
  • 感谢@Charlieface,这是一个很好的提示...并指出了我正确的方向!
  • 什么版本的 EF?我在 EF Core 5 或 3.1 中看不到这种行为,除非我在 FromSqlRaw 之后编写其他查询运算符。
  • 我使用的是 EF Core v3.1.4。我的项目是.Net Core 3.1。而且,我试图去掉任何额外的操作符,但仍然有这个问题。

标签: sql sql-server asp.net-core-webapi


【解决方案1】:

似乎出于某种原因USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE') 被视为USE <db_name>

USE

将数据库上下文更改为 SQL Server 中的指定数据库或数据库快照。

那么 errr: "Database 'HINT' does not exist." 就很合理了。


现在ENABLE_PARALLEL_PLAN_PREFERENCE 是未记录的查询提示:

https://github.com/MicrosoftDocs/sql-docs/issues/2442

很遗憾,我们不得不拒绝添加有关 ENABLE_PARALLEL_PLAN_PREFERENCE 的信息。这是一个未记录的查询提示,用于故障排除,在 Microsoft 支持的指导下使用。我们通常不会故意记录某些查询提示/跟踪标志,因为它可能会导致性能问题或其他意外后果。

所以在将其设置为默认值或在生产代码中使用时请谨慎。

您可以尝试使用OPTION(QUERYTRACEON 8649),其行为与提到的ENABLE_PARALLEL_PLAN_REFERENCE 相同,但需要管理员权限:

private static void ManipulateCommand(DbCommand command)
{
 if (command.CommandText.StartsWith("-- Use hint: robust plan", StringComparison.Ordinal))
 {
   command.CommandText += " OPTION(QUERYTRACEON 8649)";
 }
}

总结:在应用程序代码中设置此提示之前,我建议解决真正的潜在问题(maxdop/Cost Threshold for Parallelism/...)


编辑:

Raw SQL Queries

使用 LINQ 组合需要您的原始 SQL 查询是可组合的,因为 EF Core 会将提供的 SQL 视为子查询。可以组合的 SQL 查询以 SELECT 关键字开头。 此外,传递的 SQL 不应包含任何对子查询无效的字符或选项,例如:

尾随分号

在 SQL Server 上,尾随查询级提示(例如,OPTION (HASH JOIN))

在 SQL Server 上,不与 SELECT 子句中的 OFFSET 0 OR TOP 100 PERCENT 一起使用的 ORDER BY 子句

【讨论】:

  • 谢谢...我尝试了这两个建议,但是...没有。如果您自己运行 SELECT,那么是的,“QUERYTRACEON 8649”工作正常。问题是,EF Core 将此放入子句中......并且(甚至从 SQL Server Management Studio 运行这样的命令)它会引发错误。
  • @MikeGledhill 使用子查询是默认机制。为什么拦截器和QUERYTRACEON 8649 生成子查询,而OPTION (ROBUST PLAN) 工作正常?
【解决方案2】:

当您告诉 Ef core 查询的输出是一个实体时,EF 会将您的查询包装到该实体中,但是如果您为查询定义了一个 Keyless Entity Types,EF 首先执行查询,然后将结果映射到您的模型中。

首先像你的用户模型一样创建模型:

public class KeyLessUser
        {
            public string UserId { get; set; }

            public string UserName { get; set; }

        }

并将其添加到 DbContext 和 HasNoKey:

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<KeyLessUser>().HasNoKey();
        }

最后像这样执行你的查询:

var results = _context.Set<KeyLessUser>().FromSqlRaw("SELECT * FROM t_user OPTION(USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE'))").ToList();

经过测试,工作正常。

【讨论】:

    猜你喜欢
    • 2021-03-06
    • 2020-11-23
    • 2022-01-14
    • 2021-04-07
    • 2021-10-13
    • 1970-01-01
    • 2021-01-30
    • 2020-04-10
    • 1970-01-01
    相关资源
    最近更新 更多