C# 异步任务<;IEnumerable>;有收益回报吗?
下面的方法无法编译。替代品C# 异步任务<;IEnumerable>;有收益回报吗?,c#,async-await,C#,Async Await,下面的方法无法编译。替代品 public static async Task<IEnumerable<object[]>> GetRecordsAsync( this Transaction transaction, string commandText, params SqlParameter[] parameters) { // Get a SqlDataReader var reader = await transaction
public static async Task<IEnumerable<object[]>> GetRecordsAsync(
this Transaction transaction,
string commandText,
params SqlParameter[] parameters)
{
// Get a SqlDataReader
var reader = await transaction.GetReaderAsync(commandText, parameters);
var fieldCount = -1;
// Begin iterating through records asynchronously
while (await reader.ReadAsync()) // Note we don't loop until .ReadAsync returns a boolean
{
// Grab all the field values out
if (fieldCount < 0)
fieldCount = reader.FieldCount;
var fields = new object[fieldCount];
reader.GetValues(fields);
// Yield return the field values from this record
yield return fields;
}
}
公共静态异步任务GetRecordsAsync(
本次交易,
字符串命令文本,
参数SqlParameter[]参数)
{
//获取SqlDataReader
var reader=await transaction.GetReaderAsync(commandText,参数);
变量fieldCount=-1;
//开始异步遍历记录
while(wait reader.ReadAsync())//注意,在.ReadAsync返回布尔值之前,我们不会循环
{
//抓取所有字段值
如果(字段计数<0)
fieldCount=reader.fieldCount;
变量字段=新对象[fieldCount];
reader.getValue(字段);
//Yield返回此记录中的字段值
产量回报场;
}
}
错误消息:
“TransactionExtensions.GetRecordsAsync(Transaction,string,params SqlParameter[])的主体不能是迭代器块,因为“Task>”不是迭代器接口类型
我看不到一种方法来适应一个类似的(但不同的)问题,因为我不知道循环会进行多少次
编辑:固定格式根据@SLaks对问题的评论,这里有一个通用的替代方法:
我在没有第三方扩展的情况下解决了这个问题:
public async Task<IEnumerable<Item>> GetAllFromDb()
{
OracleConnection connection = null;
DbDataReader reader = null;
try
{
connection = new OracleConnection(connectionString);
var command = new OracleCommand(queryString, connection);
connection.Open();
reader = await command.ExecuteReaderAsync();
return this.BuildEnumerable(connection, reader);
}
catch (Exception)
{
reader?.Dispose();
connection?.Dispose();
throw;
}
}
private IEnumerable<Item> BuildEnumerable(OracleConnection connection, DbDataReader reader)
{
using (connection)
using (reader)
{
while (reader.Read())
{
var item = new Item()
{
Prop = reader.GetString(0),
};
yield return item;
}
}
}
公共异步任务GetAllFromDb()
{
OracleConnection=null;
DbDataReader=null;
尝试
{
连接=新的OracleConnection(connectionString);
var命令=新的OracleCommand(查询字符串,连接);
connection.Open();
reader=wait命令.ExecuteReaderAsync();
返回此.BuildEnumerable(连接、读取器);
}
捕获(例外)
{
读卡器?.Dispose();
连接?.Dispose();
投掷;
}
}
私有IEnumerable BuildEnumerable(OracleConnection连接、DbDataReader)
{
使用(连接)
使用(读卡器)
{
while(reader.Read())
{
变量项=新项()
{
Prop=reader.GetString(0),
};
收益回报项目;
}
}
}
此示例适用于Oracle Data Reader,但相同的方法适用于任何与
收益返回相结合的异步操作
IEnumerable
本身不支持该操作。使用反应式扩展。您可以使用ObservableCollection监视正在添加的元素。创建并将其传递给GetRecordsAsync,它现在将只返回任务,并在准备好生成字段后添加到任务中。现在我考虑一下,简单地将“on fields received”委托传递给该方法也是可能的。@IllidanS4我认为这可以归结为SLaks给出的注释。这两个都是好主意,但也带来了很多其他好处bear@MattThomas,也可以通过via查看一些其他想法。谢谢链接。我从中得到的最好的选择是Rx。对我来说,答案就像Rx(或者更一般地说,pub-sub)所做的一样,但在这个问题的核心,你称之为reader.Read(),即阻塞方法。它可以编译并运行,但我认为您并没有获得真正的异步好处;因此,如果正确执行此操作,将解锁线程。关于读取操作-我同意@Henk Holterman的观点,它是同步的。但是,如果您想使用标准C#、Linq、foreach等,这是最好的折衷方案。
/// <summary>
/// Asynchronously processes each record of the given reader using the given handler
/// </summary>
static async Task ProcessResultsAsync(this SqlDataReader reader, Action<object[]> fieldsHandler)
{
// Set up async functions for the reader
var shouldLoopAsync = (Func<Task<bool>>)reader.ReadAsync;
var getAsync = new Func<SqlDataReader, Func<Task<object[]>>>(_reader =>
{
var fieldCount = -1;
return () => Task.Run(() =>
{
Interlocked.CompareExchange(ref fieldCount, _reader.FieldCount, -1);
var fields = new object[fieldCount];
_reader.GetValues(fields);
return fields;
});
})(reader);
// Turn the async functions into an IObservable
var observable = ToObservable(shouldLoopAsync, getAsync);
// Process the fields as they become available
var finished = new ManualResetEventSlim(); // This will be our signal for when the observable completes
using (observable.Subscribe(
onNext: fieldsHandler, // Invoke the handler for each set of fields
onCompleted: finished.Set // Set the gate when the observable completes
)) // Don't forget best practice of disposing IDisposables
// Asynchronously wait for the gate to be set
await Task.Run((Action)finished.Wait);
}
// Get a SqlDataReader
var reader = await transaction.GetReaderAsync(commandText, parameters);
// Do something with the records
await reader.ProcessResultsAsync(fields => { /* Code here to process each record */ });
public async Task<IEnumerable<Item>> GetAllFromDb()
{
OracleConnection connection = null;
DbDataReader reader = null;
try
{
connection = new OracleConnection(connectionString);
var command = new OracleCommand(queryString, connection);
connection.Open();
reader = await command.ExecuteReaderAsync();
return this.BuildEnumerable(connection, reader);
}
catch (Exception)
{
reader?.Dispose();
connection?.Dispose();
throw;
}
}
private IEnumerable<Item> BuildEnumerable(OracleConnection connection, DbDataReader reader)
{
using (connection)
using (reader)
{
while (reader.Read())
{
var item = new Item()
{
Prop = reader.GetString(0),
};
yield return item;
}
}
}