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

Posted

技术标签:

【中文标题】如何在 Entity Framework Core 2.0 中从 DbDataReader 转换为 Task<IEnumerable<TEntity>>?【英文标题】:How to convert from DbDataReader to Task<IEnumerable<TEntity>> in Entity Framework Core 2.0? 【发布时间】:2018-02-01 14:57:00 【问题描述】:

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

private async Task<IEnumerable<TEntity>> InvokeStoredProcedureAsync(string entityName)

    var storedProcedureName = string.Format(CultureInfo.InvariantCulture, "sp_0BulkSelect", 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&lt;IEnumerable&lt;TEntity&gt;&gt;

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

阅读器关闭时调用 FieldCount 的尝试无效。

请参考所附截图。

【问题讨论】:

【参考方案1】:

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

EF Core 目前没有公开公开的方式来做到这一点。但是如果TEntity是模型实体类,可以简单的使用FromSql方法:

private async Task<IEnumerable<TEntity>> InvokeStoredProcedureAsync(string entityName)

    var storedProcedureName = string.Format(CultureInfo.InvariantCulture, "sp_0BulkSelect", entityName);
    using (var db = new MyDbContext(_options))
    
        var result = await db.Set<TEntity>().FromSql(storedProcedureName).ToListAsync();
        return result;
    

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

【讨论】:

它给出编译错误。 “非泛型声明不允许约束” 错误“'DbContext.Set()' 是在给定上下文中无效的方法” 哎呀,对不起,我猜这当然是一些通用存储库类的一部分。我在Set&lt;TEntity&gt; 之后忘记了()。固定。【参考方案2】:

在调用您的阅读器时,它已经关闭,因为 using-block 已完成。您还需要阅读“逐行”。尝试以下操作:

private async Task<IEnumerable<TEntity>> InvokeStoredProcedureAsync(string entityName)

    var storedProcedureName = string.Format(CultureInfo.InvariantCulture, "sp_0BulkSelect", 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;

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

【讨论】:

@viveknuna 您需要在using-block 中执行此操作 - 您尝试过吗? @viveknuna 那例外会是什么?我猜它不再是Invalid attempt to call FieldCount when reader is closed.了? 是的,不一样,我会在 15 分钟后发布 @viveknuna ...已经找到了问题,感谢 Prateek。请查看我的更新...【参考方案3】:

在异步方法中,无需强制转换为任务。这将自行发生。阅读器对象有单行。应该一次读取每一行。

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

【讨论】:

我需要在这里做的 temp.add(//##add 代码以读取每一列##) ; 更新了代码。可以通过@ChrFin - here 提供的链接来完成读者到对象的通用方法 @Prateek 出现编译错误。 “无法创建变量类型 'TEntity' 的实例,因为它没有 new() 约束” TEntity - 它是抽象类吗?如果是那么你需要继承这个类并初始化派生类... 是的,但是大约有 20 个类继承了这个抽象类。因此,根据您的解决方案,我将不得不编写多个 if else 语句来创建不同的对象,因为不同的类具有不同的属性

以上是关于如何在 Entity Framework Core 2.0 中从 DbDataReader 转换为 Task<IEnumerable<TEntity>>?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Entity Framework Core 2 中播种?

如何处置 Entity Framework Core 内存数据库

如何在 Entity Framework Core 中将连接字符串传递给 DBContext?

如何使用 Entity Framework Core 正确保存 DateTime?

如何在 .NET Core 3.0 Entity Framework 中执行组加入?

请问在 .NET Core 中如何让 Entity Framework Core 在日志中记录由 LINQ 生成的SQL语句?