如何使用Entity Framework Core 2.1执行SqlQuery?

24
在Entity Framework 6中,我可以使用以下命令在数据库上执行原始的SQL查询:
IEnumerable<string> Contact.Database.SqlQuery<string>("SELECT a.title FROM a JOIN b ON b.Id = a.aId WHERE b.Status = 10");
在一个新项目中,我正在尝试使用 Entity Framework Core 2.1。我需要执行原始 SQL 查询。在搜索时,我发现扩展方法 SqlQuery 已更改为 FromSql。不过,FromSql 只存在于 DbSet<> 上,而不是 DbContext.Database 上。
我该如何在 DbSet<> 之外运行 FromSql 呢?方法 FromSql 在数据库对象 DbContext.Database.FromSql<> 上不存在。

我更新了我的回答。随着EF Core 2.1 RC 1的推出,微软引入了一个新功能DbQuery<T>,您可以在其上调用FromSql。 - CodeNotFound
这个回答解决了你的问题吗?不使用DbSet - Entity Framework Core 的原始SQL查询 - Michael Freidgeim
2个回答

34

更新

这个方法适用于 EF Core 2.1,但如果你使用的是 EF Core 3.0 或更高版本,请参考这个完整答案


我看到扩展方法 SqlQuery 被改成了 FromSql。

但新的 FromSql 方法比 SqlQuery 更加限制。该方法的文档解释了它存在一些限制,例如:

SQL查询只能用于返回模型实体类型。我们有一个计划去增强支持从原始 SQL 查询中返回匿名类型
SQL 查询必须为实体或查询类型的所有属性返回数据。

[...]
更新:更近期的 GitHub dotnet/efcore 讨论#10753 支持定义结果类型以外的原始 SQL 查询

因此,在您的情况下,您要使用的 SQL 查询如下:

SELECT a.title FROM a JOIN b ON b.Id = a.aId WHERE b.Status = 10

正如文档所述,您只能将FromSql实体或查询类型一起使用。您的 SQL 查询不会返回模型中定义的实体的所有数据,而是仅返回实体的一个列。顺便提一下,EF Core 2.1 中引入了一个新功能,自 2018 年 5 月 7 日发布候选版以来就一直存在。微软说:

EF Core 2.1 RC1 是一个“上线”版本,这意味着一旦您测试了应用程序在 RC1 上正确工作,您就可以在生产环境中使用它,并从 Microsoft 获得支持,但是您仍然应该在最终稳定版发布后更新。

##在查询类型上使用 FromSql##

什么是查询类型

现在,EF Core 模型可以包括查询类型。与实体类型不同,查询类型没有定义键,并且不能插入、删除或更新(即它们是只读的),但是它们可以直接由查询返回。查询类型的一些使用场景包括:映射到没有主键的视图、映射到没有主键的表、映射到模型中定义的查询、作为 FromSql() 查询的返回类型。

如果您想使用查询类型特性来处理 SQL 文本,您首先要定义一个类,比如命名为MySuperClass

public class MySuperClass
{
    public string Title { get; set; }
}

然后在你的 DbContext 类中定义一个类型为 DbQuery<MySuperClass> 的属性,如下所示:

public DbQuery<MySuperClass> MySuperQuery { get; set; }

最后,您可以像以下代码一样在其上使用FromSql

var result = context.MySuperQuery.FromSql("SELECT a.title FROM a JOIN b ON b.Id = a.aId WHERE b.Status = 10").ToList().First();
var title = result.Title;

##不想使用 DbQuery<T> ## 如果您不想使用 DbQuery<T>,也不想定义一个只包含一个属性的类,则可以像 @vivek nuna 在他的答案中所做的那样使用 ExecuteSqlCommandAsync(他的答案部分正确)。但您必须知道该方法返回的值是查询影响的行数。此外,您必须将标题作为输出参数放置,以便使查询成为存储过程。使用 ExecuteSqlCommandAsyncExecuteSqlCommand,然后在调用方法时读取您传递的输出参数。

不创建存储过程,因此不使用 ExecuteSqlCommandAsyncExecuteSqlCommand 的更简单的方法是使用以下代码:

using (var context = new MyDbContext())
{
    var conn = context.Database.GetDbConnection();
    await conn.OpenAsync();
    var command = conn.CreateCommand();
    const string query = "SELECT a.title FROM a JOIN b ON b.Id = a.aId WHERE b.Status = 10";
    command.CommandText = query;
    var reader = await command.ExecuteReaderAsync();
    while (await reader.ReadAsync())
    {
        var title = reader.GetString(0);
        // Do whatever you want with title 
    }
}  
你可以将这个逻辑封装为一个助手方法,该方法将接收您的SQL查询并返回所需的数据。但我建议您使用Dapper.Net,它包含许多助手方法,可以轻松处理原始SQL(就像我们上面所做的那样),并与DbContext共享相同的连接。

你在哪里设置属性public DbQuery<MySuperClass> MySuperQuery { get; set; }的值?我试图弄清楚从哪里获取DbQuery的实例,因为该类是抽象的。我需要自定义一个吗?它里面没有需要覆盖的内容,所以我不懂。 - rory.ap
你需要一个EF可以在进行材料化时实例化的类。因此,抽象类将不起作用。你需要一个自定义的类 @rory.ap - CodeNotFound
如何传递参数? - Toolkit
请注意,EF Core 3中使用DbSet替换了DbQuery,以使用无键实体类型。 - NetMage
为什么要添加这个答案,而不是将问题标记为 Raw SQL Query without DbSet - Entity Framework Core 的重复? - user692942

2
您可以通过以下方式使用Microsoft.EntityFrameworkCore.Relational程序集中RelationalDatabaseFacadeExtensions类中定义的ExecuteSqlCommandAsync方法来执行操作。请注意保留HTML标签。
_databaseContext.Database.ExecuteSqlCommandAsync(<Your parameters>)

11
这只执行SQL语句,不返回结果集。 - MonkeyDreamzzz

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