如何在 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")
};
除了明显的异步代码问题外,您无法通过简单地调用 Cast
将 DbDataReader
实现为 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 所有预期的列。
我通过以下方式调用 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")
};
除了明显的异步代码问题外,您无法通过简单地调用 Cast
将 DbDataReader
实现为 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 所有预期的列。