更新
这个方法适用于 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
(他的答案部分正确)。但您必须知道该方法返回的值是查询影响的行数。此外,您必须将标题作为输出参数放置,以便使查询成为存储过程。使用 ExecuteSqlCommandAsync
或 ExecuteSqlCommand
,然后在调用方法时读取您传递的输出参数。
不创建存储过程,因此不使用 ExecuteSqlCommandAsync
或 ExecuteSqlCommand
的更简单的方法是使用以下代码:
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);
}
}
你可以将这个逻辑封装为一个助手方法,该方法将接收您的SQL查询并返回所需的数据。但我建议您使用
Dapper.Net,它包含许多助手方法,可以轻松处理原始SQL(就像我们上面所做的那样),并与
DbContext
共享相同的连接。