My goal is simple , I want to do Asynchronous I/O calls (using async await) - but :
- Without using DataFlow dependency ( like in this answer)
- Without middle buffers( not like this answer)
- The Projector function should be sent as an argument. ( not like this answer)
Ok.
Currently here is my code which it's job is to read from db and project each line to a 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);
}
}
}
}
So , what is projector ?
Each class has a function which gets a record
( IDataRecord
) and create an entity :
Example :
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; }
}
So here , MyClassFactory
would be the Func
So how I currently run it ?
var sql = @"SELECT TOP 1000 [NAME],[datee] ,[val] FROM [WebERP].[dbo].[t]";
var a = GetSomeData < MyClass > (sql, MyClass.MyClassFactory).Where(...); //notice the Func
All ok.
The problems starts now :
Adding async
to the method yields an error : ( Yes I know that Ienumerable is a Synchronous interface hence the problem)
public async Task<IEnumerable < T >> GetSomeData < T > (string sql, Func < IDataRecord, T > projector)
cannot be an iterator block because 'System.Threading.Tasks.Task>' is not an iterator interface type
Which DOES compile.
Question
How can I convert my code to support fully asynchronse IO call ?
(under the conditions : without DataFlow dependency , send projector function as argument , no middle buffers)
You may want to check Stephen Toub's "Tasks, Monads, and LINQ" for some great ideas on how to process asynchronous data sequences.
It's not (yet) possible to combine
yield
andawait
, but I'm going to be a verbalist here: the quoted requirements didn't listIEnumerable
and LINQ. So, here's a possible solution shaped as two coroutines (almost untested).Data producer routine (corresponds to
IEnumarable
withyield
):Data consumer routine (correspond to
foreach
or a LINQ expression):Coroutine execution helper (can also be implemented as a pair of custom awaiters):
This is just an idea. It might be an overkill for a simple task like this, and it could be improved in some areas (like thread-safety, race conditions and handling the end of the sequence without touching
producerTask
). Yet it illustrates how the asynchronous data retrieval and processing could possibly be decoupled.