【问题标题】:How to execute SqlQuery with Entity Framework Core 2.1?如何使用 Entity Framework Core 2.1 执行 SqlQuery?
【发布时间】:2018-10-28 08:18:04
【问题描述】:

在 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&lt;&gt; 上,而不存在于 DbContext.Database 上。

如何在DbSet&lt;&gt; 之外运行FromSql?数据库对象DbContext.Database.FromSql&lt;&gt;上不存在方法FromSql

【问题讨论】:

标签: entity-framework entity-framework-6 entity-framework-core


【解决方案1】:

更新

这是 EF Core 2.1 的工作,但如果您使用的是 EF Core 3.0 或更高版本,请参阅此complete answer


可以看到扩展名SqlQuery改成了FromSql

但新的FromSql 方法比SqlQuery 更具限制性。该方法的documentation 解释说它存在一些限制,例如:

SQL 查询只能用于返回 属于 你的模型。我们对enable returning ad-hoc types from raw SQL queries 的积压工作有所增强。
SQL 查询必须返回实体或查询类型的所有属性的数据。

[...]
更新:最近的 GitHub dotnet/efcore 讨论 Support raw SQL queries without defining an entity type for the result #10753

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

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

正如文档所说,您只能将FromSql实体或查询类型一起使用。您的 SQL 查询不会返回模型中定义的实体的所有数据,而只会返回实体的一列。顺便说一句,自 2018 年 5 月 7 日起,EF Core 2.1 中引入了一项新功能。微软表示:

EF Core 2.1 RC1 是一个“上线”版本,这意味着一旦您测试了 您的应用程序与 RC1 一起正常工作,您可以在 生产并获得 Microsoft 的支持,但您仍然应该 更新到最终的稳定版本。

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

什么是query type

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

如果您想在您的 SQL 文本中使用查询类型功能,您首先定义一个类,我们将其命名为 MySuperClass

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

然后在您的DbContext 类中定义DbQuery&lt;MySuperClass&gt; 类型的属性,如下所示:

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&lt;T&gt;## 如果您不想使用DbQuery&lt;T&gt; 并且不想定义一个只包含一个属性的类,那么您可以使用ExecuteSqlCommandAsync 就像@vivek nuna 在他的回答中所做的那样(他的回答部分正确)。但是您必须知道该方法的返回值是受查询影响的行数。此外,您必须将您的标题作为输出参数,以便使您的查询成为存储过程。使用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,其中包含许多帮助方法,可以帮助轻松处理我们上面所做的RAW SQL,并与DbContext 共享smae 连接。

【讨论】:

  • 你在哪里设置属性public DbQuery&lt;MySuperClass&gt; MySuperQuery { get; set; }的值?我试图找出在哪里获得DbQuery 的实例,因为该类是抽象的。我必须定制一个吗?里面没有什么可以覆盖的,所以我不明白。
  • 您需要一个 EF 可以在进行物化时实例化的类。所以抽象类是行不通的。你需要一个自定义的@rory.ap
  • 如何传递参数?
  • 请注意,DbQuery 已被替换为在 EF Core 3 中使用 DbSet 的无键实体类型。
  • 为什么要添加这个答案而不是仅仅将问题标记为Raw SQL Query without DbSet - Entity Framework Core的重复?
【解决方案2】:

您可以通过以下方式使用在Microsoft.EntityFrameworkCore.Relational 程序集的RelationalDatabaseFacadeExtensions 类中定义的ExecuteSqlCommandAsync 方法。

_databaseContext.Database.ExecuteSqlCommandAsync(<Your parameters>)

【讨论】:

  • 这个只执行SQL,不返回结果集
猜你喜欢
  • 2019-01-11
  • 2018-09-29
  • 1970-01-01
  • 1970-01-01
  • 2019-04-13
  • 1970-01-01
  • 2018-12-31
  • 2023-03-14
  • 2019-05-06
相关资源
最近更新 更多