【发布时间】:2014-07-14 06:45:28
【问题描述】:
我的目标很简单,我想做异步 I/O 调用(使用 async await) - 但是:
- 不使用 DataFlow 依赖 (like in this answer)
- 没有中间缓冲区(not like this answer)
- Projector 函数应作为参数发送。 (not like this answer)
好的。
目前这是我的代码,它的工作是从 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>' 不是迭代器接口类型
可以编译。
问题
如何转换我的代码以支持完全异步的 IO 调用?
(条件:不依赖DataFlow,发送投影函数作为参数,无中间缓冲区)
【问题讨论】:
-
对于这种情况,如果 C# 支持
IAsyncEnumerator,那就太好了。如果您可以以完全异步的方式返回一个急切填写的列表,那么问题就会变得容易得多。 -
ToArray确实创建了一个中间缓冲区。 -
您的代码迭代。编译的代码没有。 IEnumerable 不是问题。迭代(即:
yield return)是问题所在。缓冲区有什么问题? -
@RoyiNamir 您必须向调用者公开每个元素的异步性。 IEnumerable 无法做到这一点。获取元素始终是同步的。您需要使用像 IAsyncEnumerator 这样的异步模型。围绕这个想法似乎有合理的库 (asyncenum.codeplex.com)。没有内置任何东西。此外,与所有 ADO.NET 和 SQL 工作相比,缓冲对性能的影响非常小,因此避免缓冲对吞吐量没有任何有意义的影响。对于流式传输大量数据集仍然有意义。
标签: c# asynchronous io async-await .net-4.5