如何在 Entity Framework Core 2.0 中从 DbDataReader 转换为 Task<IEnumerable<TEntity>>?

How to convert from DbDataReader to Task<IEnumerable<TEntity>> in Entity Framework Core 2.0?

我通过以下方式调用 EF Core 2.0 中的存储过程。

private async Task<IEnumerable<TEntity>> InvokeStoredProcedureAsync(string entityName)
{
    var storedProcedureName = string.Format(CultureInfo.InvariantCulture, "sp_{0}BulkSelect", entityName);
    dynamic temp;
    using (MyDbContext MyDbContext = new MyDbContext(_options))
    {
        MyDbContext.Database.OpenConnection();
        DbCommand cmd = MyDbContext.Database.GetDbConnection().CreateCommand();
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandText = storedProcedureName;

        using (var reader = cmd.ExecuteReader())
        {
           temp = reader.Cast<Task<IEnumerable<TEntity>>>();
        }
    }

    return await temp;
}

我需要从 DbDataReader 转换为 Task<IEnumerable<TEntity>>

但我在尝试扩展临时变量以查看其值时遇到此错误。

Invalid attempt to call FieldCount when reader is closed.

请参考附件截图。

在您的 reader 被调用时它已经关闭,因为 using 块已完成。您还需要阅读 "row by row"。尝试以下操作:

private async Task<IEnumerable<TEntity>> InvokeStoredProcedureAsync(string entityName)
{
    var storedProcedureName = string.Format(CultureInfo.InvariantCulture, "sp_{0}BulkSelect", entityName);
    List<TEntity> temp = new List<TEntity>();
    using (MyDbContext MyDbContext = new MyDbContext(_options))
    {
        MyDbContext.Database.OpenConnection();
        DbCommand cmd = MyDbContext.Database.GetDbConnection().CreateCommand();
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandText = storedProcedureName;

        using (var reader = await cmd.ExecuteReaderAsync())
        {
           while (await reader.ReadAsync())
               temp.Add(reader.Cast<TEntity>()); // ATTENTION: this does not work! (see below)
        }
    }

    return temp;
}

但我 认为 上面使用的演员表并不符合您的想法。有关如何从 reader 创建实体的示例,请参见以下线程:Convert rows from a data reader into typed results

在异步方法中,无需转换为任务。那会自己发生。 reader 对象只有一行。应该一次读取每一行。

var temp = new List<TEntity>();

  using (var reader = await  cmd.ExecuteReaderAsync()) {
    while(await reader.ReadAsync()) {
        temp.add(CreateEntity(reader)) ;
     }
   }

return temp;

// this is tightly coupled to object. Generic methods can be made
//  to convert reader to object
public TEntity CreateEntity(SqlDataReader reader) => new TEntity()
    {
        Id = reader.GetInt32("id"),
        Col1 =  reader.GetString("col1"),
        Col2 =  reader.GetString("col2"),
        Col3 =  reader.GetString("col3")
    };

async - msdn

除了明显的异步代码问题外,您无法通过简单地调用 CastDbDataReader 实现为 class。如果可能的话,就不需要像 Dapper 和类似的微型 ORM。

EF Core 目前没有提供 public 方法来做到这一点。但是如果TEntity是一个模型实体class,你可以简单地使用FromSql方法:

private async Task<IEnumerable<TEntity>> InvokeStoredProcedureAsync(string entityName)
{
    var storedProcedureName = string.Format(CultureInfo.InvariantCulture, "sp_{0}BulkSelect", entityName);
    using (var db = new MyDbContext(_options))
    {
        var result = await db.Set<TEntity>().FromSql(storedProcedureName).ToListAsync();
        return result;
    }
}

通过 TEntity 映射确保 SP returns 所有预期的列。