我的目标很简单,我想做异步I / O调用(使用异步等待) - 但是:
确定。
目前这是我的代码,它的工作是从db读取并将每一行投影到Func<>
public IEnumerable < T > GetSomeData < T > (string sql, Func < IDataRecord, T > projector)
{
using(SqlConnection _conn = new SqlConnection(@"Data Source=..."))
{
using(SqlCommand _cmd = new SqlCommand(sql, _conn))
{
_conn.Open();
_cmd.CommandTimeout = 100000;
using(IDataReader rdr = _cmd.ExecuteReader())
{
while (rdr.Read()) yield return projector(rdr);
}
}
}
}
那么,什么是投影仪?
每个类都有一个函数,它获得record
(IDataRecord
)并创建一个实体:
示例:
public class MyClass
{
public static MyClass MyClassFactory(IDataRecord record)
{
return new MyClass
{
Name = record["Name"].ToString(),
Datee = DateTime.Parse(record["Datee"].ToString()),
val = decimal.Parse(record["val"].ToString())
};
}
public string Name { get; set; }
public DateTime Datee { get; set; }
public decimal val { get; set; }
}
所以在这里,MyClassFactory
将是Func
那么我目前如何运行呢?
var sql = @"SELECT TOP 1000 [NAME],[datee] ,[val] FROM [WebERP].[dbo].[t]";
var a = GetSomeData < MyClass > (sql, MyClass.MyClassFactory).Where(...); //notice the Func
一切都好。
问题现在就开始了:
在方法中添加async
会产生错误:(是的,我知道Ienumerable是 Synchronous 接口,因此出现了问题)
public async Task<IEnumerable < T >> GetSomeData < T > (string sql, Func < IDataRecord, T > projector)
不能是迭代器块,因为 'System.Threading.Tasks.Task&GT;' 不是迭代器接口类型
DOES 编译。
问题
如何转换 my code 以支持完全异步IO调用?
(条件:没有DataFlow依赖,发送投影函数作为参数,没有中间缓冲区)
答案 0 :(得分:8)
我想做异步I / O调用(使用异步等待) - 但是:
- 不使用DataFlow依赖项(如本答案中所述)
- 没有中间缓冲区(不像这个答案)
- Projector函数应作为参数发送。 (不像这个答案)
您可能需要查看Stephen Toub的"Tasks, Monads, and LINQ",了解有关如何处理异步数据序列的一些好主意。
({1}}和yield
合并不可能,但我会在这里成为一名动词:引用的要求没有列出await
和LINQ。所以,这是一个可能的解决方案,形状为两个协同程序(几乎未经测试)。
数据生成器例程(对应IEnumerable
与IEnumarable
):
yield
数据使用者例程(对应于public async Task GetSomeDataAsync<T>(
string sql, Func<IDataRecord, T> projector, ProducerConsumerHub<T> hub)
{
using (SqlConnection _conn = new SqlConnection(@"Data Source=..."))
{
using (SqlCommand _cmd = new SqlCommand(sql, _conn))
{
await _conn.OpenAsync();
_cmd.CommandTimeout = 100000;
using (var rdr = await _cmd.ExecuteReaderAsync())
{
while (await rdr.ReadAsync())
await hub.ProduceAsync(projector(rdr));
}
}
}
}
或LINQ表达式):
foreach
协程执行助手(也可以实现为一对custom awaiters):
public async Task ConsumeSomeDataAsync(string sql)
{
var hub = new ProducerConsumerHub<IDataRecord>();
var producerTask = GetSomeDataAsync(sql, rdr => rdr, hub);
while (true)
{
var nextItemTask = hub.ConsumeAsync();
await Task.WhenAny(producerTask, nextItemTask);
if (nextItemTask.IsCompleted)
{
// process the next data item
Console.WriteLine(await nextItemTask);
}
if (producerTask.IsCompleted)
{
// process the end of sequence
await producerTask;
break;
}
}
}
这只是一个想法。对于像这样的简单任务来说,这可能是一种过度杀伤,并且可以在某些方面进行改进(例如线程安全,竞争条件和处理序列结束而不触及public class ProducerConsumerHub<T>
{
TaskCompletionSource<Empty> _consumer = new TaskCompletionSource<Empty>();
TaskCompletionSource<T> _producer = new TaskCompletionSource<T>();
// TODO: make thread-safe
public async Task ProduceAsync(T data)
{
_producer.SetResult(data);
await _consumer.Task;
_consumer = new TaskCompletionSource<Empty>();
}
public async Task<T> ConsumeAsync()
{
var data = await _producer.Task;
_producer = new TaskCompletionSource<T>();
_consumer.SetResult(Empty.Value);
return data;
}
struct Empty { public static readonly Empty Value = default(Empty); }
}
)。然而,它说明了异步数据检索和处理如何可能解耦。
答案 1 :(得分:0)
此Medium article描述了另一种解决方案,即使用Dasync/AsyncEnumerable
库。
该库是开放源代码,可在NuGet和GitHub上使用,并提供一种可读的语法,以IAsyncEnumerable
到C# 8.0 comes out and provides its own implementation and language support的形式{ {1}}和async ... yield return
。
(我与库没有任何联系;我发现它是可能的非常有用的解决方案-我认为是-与我正在开发的项目中的问题相同)。