使用async / await与DataReader? (没有中间缓冲区!)

时间:2014-05-25 09:59:05

标签: c# asynchronous io async-await .net-4.5

我的目标很简单,我想做异步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);
            }
        }
    }
}

那么,什么是投影仪?

每个类都有一个函数,它获得recordIDataRecord)并创建一个实体:

示例:

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;'   不是迭代器接口类型

But this guy here did - :

enter image description here

DOES 编译。

问题

如何转换 my code 以支持完全异步IO调用?

(条件:没有DataFlow依赖,发送投影函数作为参数,没有中间缓冲区)

2 个答案:

答案 0 :(得分:8)

  

我想做异步I / O调用(使用异步等待) - 但是:

     
      
  • 不使用DataFlow依赖项(如本答案中所述)
  •   
  • 没有中间缓冲区(不像这个答案)
  •   
  • Projector函数应作为参数发送。 (不像这个答案)
  •   

您可能需要查看Stephen Toub的"Tasks, Monads, and LINQ",了解有关如何处理异步数据序列的一些好主意。

({1}}和yield合并不可能,但我会在这里成为一名动词:引用的要求没有列出await和LINQ。所以,这是一个可能的解决方案,形状为两个协同程序(几乎未经测试)。

数据生成器例程(对应IEnumerableIEnumarable):

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库。

该库是开放源代码,可在NuGetGitHub上使用,并提供一种可读的语法,以IAsyncEnumerableC# 8.0 comes out and provides its own implementation and language support的形式{ {1}}和async ... yield return

(我与库没有任何联系;我发现它是可能的非常有用的解决方案-我认为是-与我正在开发的项目中的问题相同)。