c#


Fill c# Dictionary asynchronously


I am using this code below that loops through a list of Contracts and gets a list of Rates for each Contract. Then it stores the result in a Dictionary.
/// <summary>
/// Get the Rates for the Job keyed to the id of the Organisation
/// </summary>
async Task<Dictionary<int, IReadOnlyList<Rate>>> GetRatesAsync(int jobId)
{
var jobContracts = await _contractClient.GetJobContractsByJobAsync(jobId);
var result = new Dictionary<int, IReadOnlyList<Rate>>();
foreach (var jobContract in jobContracts)
{
result.Add(jobContract.Contract.Organisation.Id, await _contractClient.GetContractRatesByContractAsync(jobContract.Contract.Id));
}
return result;
}
Resharper helpfully advises me that "Loop can be converted into LINQ-expression". However the auto-generated fix by Resharper (below) does not compile.
return jobContracts.ToDictionary(jobContract => jobContract.Contract.Organisation.Id, jobContract => await _contractClient.GetContractRatesByContractAsync(jobContract.Contract.Id));
and then
The error is The await operator can only be used within an async lamba expression. So chucking an async in and changing it to below:
return jobContracts.ToDictionary(jobContract => jobContract.Contract.Organisation.Id, async jobContract => await _contractClient.GetContractRatesByContractAsync(jobContract.Contract.Id));
and then
the error is now Cannot implicitly convert type 'System.Collections.Generic.Dictionary<int, System.Threading.Tasks.Task<System.Collections.Generic.IReadOnlyList<Rate>>>' to 'System.Collections.Generic.Dictionary<int, System.Collections.Generic.IReadOnlyList<Rate>>' and it wants me to change the signature to:
async Task<Dictionary<int, Task<IReadOnlyList<Rate>>>> GetContractRatesAsync()
and then
This last change now compiles, but all my calling methods are expecting a Task of Dictionary<int, IReadOnlyList<Rate>> to await, not a Dictionary<int, Task<IReadOnlyList<Rate>>.
Is there some way of awaiting this to convert it to a Dictionary<int, IReadOnlyList<Rate>>? Or some other way to get the data out asynchronously?
Actually the generic method can be rather interesting so I'll post a solution. Your initial implementation has a performance issue you could solve if you wished, and your application domain allows it.
If you want to add items to the dictionary asynchronously then it is probably desirable to allow those async items to run in parallel. You want to start with a method that accepts an enumerable of keys and values. The values should be functions which generate tasks, not tasks. This is so you don't have all the tasks running at once.
You then use a semaphore to control how many tasks are started at the same time. There is also a fallback function below for when you are happy for all tasks to start at the same time.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace _43909210
{
public static class EnumerableExtensions
{
private static async Task<Dictionary<TKey, TValue>> ToDictionary<TKey,TValue>
(this IEnumerable<(TKey key, Func<Task<TValue>> valueTask)> source)
{
var results = await Task.WhenAll
( source.Select( async e => ( key: e.key, value:await e.valueTask() ) ) );
return results.ToDictionary(v=>v.key, v=>v.value);
}
public class ActionDisposable : IDisposable
{
private readonly Action _Action;
public ActionDisposable(Action action) => _Action = action;
public void Dispose() => _Action();
}
public static async Task<IDisposable> Enter(this SemaphoreSlim s)
{
await s.WaitAsync();
return new ActionDisposable( () => s.Release() );
}
/// <summary>
/// Generate a dictionary asynchronously with up to 'n' tasks running in parallel.
/// If n = 0 then there is no limit set and all tasks are started together.
/// </summary>
/// <returns></returns>
public static async Task<Dictionary<TKey, TValue>> ToDictionaryParallel<TKey,TValue>
( this IEnumerable<(TKey key, Func<Task<TValue>> valueTaskFactory)> source
, int n = 0
)
{
// Delegate to the non limiting case where
if (n <= 0)
return await ToDictionary( source );
// Set up the parallel limiting semaphore
using (var pLimit = new SemaphoreSlim( n ))
{
var dict = new Dictionary<TKey, TValue>();
// Local function to start the task and
// block (asynchronously ) when too many
// tasks are running
async Task
Run((TKey key, Func<Task<TValue>> valueTask) o)
{
// async block if the parallel limit is reached
using (await pLimit.Enter())
{
dict.Add(o.key, await o.valueTask());
}
}
// Proceed to start the tasks
foreach (var task in source.Select( Run ))
await task;
// Wait for all tasks to finish
await pLimit.WaitAsync();
return dict;
}
}
}
}

Related Links

Detect exception while deserializing in Servicestack's JsConfig
How to Assign tags for treeview Nodes with multiple classes and properties
How to check if a property is decorated with a custom attribute using Roslyn?
How to specify index in Entity Framework 5.0
Bold a string in c#
regular expression allowing only english alphanumeric and “-” [duplicate]
Creating a Minecraft-esc Game. Problems with Chunks
Avoid call by reference
How to change the button's text on openFileDialog ShowDialog window? [duplicate]
Reading UTM_ parameters from Google AdWords and Bing Ads
Web api routing two controller similar action and parameter not populating parameter value in one action
Why am I getting “The object is in a detached or deleted state” when deleting an unmodified row?
Confused by variable evaluation in parent thread
Underlying IWebBrowser com object in outlook folder
How can i loop in treeView1 SelectedNode to the last childe node and go back to the level 0?
MissingMemberException was unhandled EntitySet X object has no attribute Y

Categories

HOME
gulp
kendo-ui-angular2
stanford-nlp
customization
openacc
cxf
azure-eventhub
windows-server-2008
spotfire
mingw
terminal
ms-access-2010
psexec
qt4
webpack-dev-server
rtc
angularjs-ng-repeat
normals
elastalert
email-attachments
resharper
http-status-codes
fine-uploader
lambda-calculus
loss
k-means
google-street-view
accordion
multipart
bootstrap-accordion
android-xml
ds-5
intermec
textview
tikz
blockly
jql
contextmenustrip
ilog
restler
imageprocessor
machine-code
tunnel
asp.net-webhooks
mongoid5
jibx
jvisualvm
rexx
jenkins-2
activeandroid
easyquery
serialversionuid
appcmd
visio-2010
nservicebus6
try-catch-finally
disassembly
firebase-job-dispatcher
mac-app-store
xcode7.1
change-password
qtruby
system-integration
3d-rendering
python-rq
window.location
jmap
factorization
android-contentresolver
maven-antrun-plugin
callfire
firewire
stereotype
opennebula
accpac
douglas-peucker
iphone-6
random-access
disabled-control
xpand
rssi
imaplib
ekevent
aspnet-compiler
kohana-orm
angularjs-select2
leap-year
coinbase-php
pudb
pageload
pymel
screensharing
cryptarithmetic-puzzle
linear-interpolation
wpml
custom-cursor
xcode4.6.3
django-1.4
animationdrawable
unordered-set
facebook-winjs-sdk
towerjs
umfpack
mscorlib
httppostedfilebase
vectorwise
xpolog
gmagick
hwnd
lgpl
text-manipulation
numerical-computing
mysql-pconnect
konsole
hp-trim
time-management
user-preferences
disk-based
cstring
cross-cutting-concerns

Resources

Encrypt Message



code
soft
python
ios
c
html
jquery
cloud
mobile