EF Core 3.1的ExecuteSqlRaw / ExecuteSqlRawAsync是否可以完全替代ExecuteSqlCommand / ExecuteSqlCommandAsync?

33

升级到EFCore 3.1后,出现了已弃用的警告:

警告 CS0618 'RelationalDatabaseFacadeExtensions.ExecuteSqlCommandAsync(DatabaseFacade, RawSqlString, params object[])'已过时:'对于使用纯字符串异步执行SQL查询,请改用ExecuteSqlRawAsync。对于使用插值字符串语法创建参数异步执行SQL查询,请改用ExecuteSqlInterpolatedAsync。'

警告 CS0618 'RelationalDatabaseFacadeExtensions.ExecuteSqlCommand(DatabaseFacade, RawSqlString, params object[])'已过时:'对于使用纯字符串执行SQL查询,请改用ExecuteSqlRaw。对于使用插值字符串语法创建参数执行SQL查询,请改用ExecuteSqlInterpolated。'

与这些警告相关的旧代码如下:

context.Database.ExecuteSqlCommand("DELETE FROM Table WHERE ID = @p0", id);
await context.Database.ExecuteSqlCommandAsync("DELETE FROM Table2 WHERE ID = @p0", id);

参考精细手册,通常SQL Server参数@somename并未详述。实际上,我甚至不明白文档中展示在表名后打开括号的示例代码是如何有效的SQL语法:

context.Database.ExecuteSqlRaw("SELECT * FROM [dbo].[SearchBlogs]({0})", userSuppliedSearchTerm)
我没有看到文档中有定义userSuppliedSearchTerm变量的内容或类型,我很难理解它怎么可能是一个SQL语句的一部分(string userSuppliedSearchTerm = "WHERE id = 123"; 带有硬编码的值?SQL注入?)或者是一个单一的原始值(int userSuppliedSearchTerm = 123),所以这是否意味着它是某种自定义类型,指定了数据库列名和值?EFCore如何知道要查询表中的哪个列?EFCore是否解决圆括号导致的语法错误?圆括号对于EFCore理解查询是否必须?
我的问题是:鉴于我的ExecuteSqlCommand代码是有意义的,并且ExecuteSqlRaw的文档很不清楚/似乎解释得很差,我们如何从这个工作代码转换过来:
var id = 123;
context.Database.ExecuteSqlCommand("DELETE FROM Table WHERE ID = @p0", id);

如何使用ExecuteSqlRaw?

SomeType x = ???;
context.Database.ExecuteSqlRaw("???", x);
2个回答

54

规则很简单。

EF Core 2.x有3个 ExecuteSqlCommand 重载:

public static int ExecuteSqlCommand(this DatabaseFacade databaseFacade,
    RawSqlString sql, params object[] parameters); // 1
public static int ExecuteSqlCommand(this DatabaseFacade databaseFacade,
   RawSqlString sql, IEnumerable<object> parameters); // 2
public static int ExecuteSqlCommand(this DatabaseFacade databaseFacade,
    FormattableString sql); // 3

在 EF Core 3.x 中被映射到{{某些内容}}。

public static int ExecuteSqlRaw(this DatabaseFacade databaseFacade,
    string sql, params object[] parameters); // 1
public static int ExecuteSqlRaw(this DatabaseFacade databaseFacade,
    string sql, IEnumerable<object> parameters); // 2
public static int ExecuteSqlInterpolated(this DatabaseFacade databaseFacade,
    FormattableString sql); // 3

从功能上讲,它们是完全等效的。 Raw 重载支持与 v2.x 重载 #1 和 #2 相同的占位符和参数值(命名和未命名)。而 Interpolated 的行为与 v2.x 重载 #3 完全相同。

将方法重命名并对插值和非插值的 sql 参数使用不同的名称的原因是 C# 编译时 v2.x 重载 #1 和 #3 的重载决策。有时候选择插值,而意图是使用另一个,反之亦然。拥有单独的名称使意图清晰。

您可以在 EF Core 3.0 Breaking Changes - FromSql、ExecuteSql 和 ExecuteSqlAsync 已重命名 中了解更多关于这种做法的原因。

关于支持的参数占位符、名称和值的信息可以在 Raw SQL queries - Passing parameters 中找到。

但是为了回答您具体的问题,如果现有的 v2.x 代码是

context.Database.ExecuteSqlCommand("DELETE FROM Table WHERE ID = @p0", id);

那么将其更改为ExecuteSqlRaw
如果是
context.Database.ExecuteSqlCommand($"DELETE FROM Table WHERE ID = {id}");

然后将其更改为ExecuteSqlInterpolated


4
我没有看到文档中有定义userSuppliedSearchTerm变量的内容或类型,因此我很难理解它是如何被合理地用作SQL语句的一部分(例如:string userSuppliedSearchTerm = "WHERE id = 123"; 内置值? SQL注入?)或者一个单一的原始值(例如:int userSuppliedSearchTerm = 123)。这是否意味着它是某种自定义类型,指定了数据库列名和值?EFCore如何知道要查询表中的哪个列?EFCore是否解决了由括号引起的语法错误?括号对于EFCore理解查询是否是必需的?
这一切与EF Core无关。
文档选择使用表值函数dbo.SearchBlogs来演示原始SQL查询的使用,因此SELECT * FROM [dbo].[SearchBlogs]({0})是合法的SQL,因为SearchBlogs是一个函数,而不是表/视图 - 只是我链接的文档没有提到。

12
至少我不是唯一一个觉得文档非常令人困惑的人。 - mbx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接