2

I have an API call from a web app that uses Dapper to query some results and return them back to the user. However, why does this work asynchronously

IEnumerable<Task<Policy>> getPolicyTasks = from policy in policyList select GetPolicyDetailAsync(policy);

IEnumerable<Task<Policy>> tasks = policyList.Select(policy => Task.Run(() => GetPolicyDetailAsync(policy)));

var policyResults = await Task<Policy>.WhenAll(tasks);

but this doesn't?

List<Task<Policy>> getPolicyTasks = new List<Task<Policy>>();

foreach (var policy in policyList)
      getPolicyTasks.Add(GetPolicyDetailAsync(policy));

var policyResults = await Task.WhenAll(getPolicyTasks);

policies = policyResults.ToList();

The GetPolicyAsync is a straightforward Dapper SQL call like such:

policies = await db.QueryAsync<Policy, Address, Policy>(sql,
   (policy, address) =>
   {
       policy.Address = address;
       return policy;
   },
   param: new { input = policy },
   splitOn: "Address1");

return policy;

It seems all of my services that the API call need to use Task.Run() to run async. Why is this the case when the methods are async and awaited?

Dan
  • 746
  • 1
  • 9
  • 14
  • Which `SqlClient` package are you using `System.Data.SqlClient` or `Microsoft.Data.SqlClient`? And why would you want to do this in a loop anyway, when you could just do a single query to get all the `PolicyDetail` in one go – Charlieface Jun 10 '21 at 16:24
  • It's actually IBM Net5.IBM.Data.Db2 (Informix). I understand it might make more sense to do it all at once in this particular area but this is just a general example. I have some long running DB operations that need to run asynchronously. – Dan Jun 10 '21 at 16:26
  • 1
    The provider is probably not built to use `async` properly – Charlieface Jun 10 '21 at 16:28
  • Thanks @Charlieface you're right. It turns out IBM DB2 doesn't fully support async - thanks for the help. – Dan Jun 10 '21 at 16:31
  • As I said, you probably want to find a way to execute this in one single query. Does DB2 support table parameters? – Charlieface Jun 10 '21 at 16:32
  • @Charlieface it doesn't that I know of - I did refactor it to a single query though. Changed it to an "in" statement that takes a list. That does the trick... thanks again! – Dan Jun 10 '21 at 18:10

1 Answers1

1

It turns out IBM DB2 provider doesn't fully support async operations and thus why I need to use Task.Run for it. Makes sense. Thanks @Charlieface for pointing me in the right direction.

Dan
  • 746
  • 1
  • 9
  • 14
  • Nice to know that it's not only the [Oracle](https://stackoverflow.com/questions/29016698/can-the-oracle-managed-driver-use-async-wait-properly) driver that has this problem! – Theodor Zoulias Jun 10 '21 at 16:54
  • 1
    `I need to use Task.Run for it` - Don't use `Task.Run` on ASP.NET for this. Just let it run synchronously. Using `Task.Run` is a way for your code to free up a thread... by using another thread instead. It just causes a context switch for no benefit at all. – Stephen Cleary Jun 10 '21 at 18:49
  • Thanks in the refractored method it is running synchronously but there are other instances where it makes sense to so those will stay (IE creating an object from multiple methods) the time it takes to execute is now only taking a fraction of the time – Dan Jun 10 '21 at 19:07