/// <summary> /// Asynchronously load data for the provided given key. /// </summary> /// <param name="key">Key to use for loading data</param> /// <returns> /// An object representing a pending operation /// </returns> public virtual IDataLoaderResult <T> LoadAsync(TKey key) { lock (_sync) { //once it enters the lock, it is guaranteed to exit the lock, as it does not depend on external code if (_cachedList != null) { if (_cachedList.TryGetValue(key, out var ret2)) { return(ret2); } } if (_list != null) { if (_list.TryGetValue(key, out var ret2)) { return(ret2); } if (_list.Count >= MaxBatchSize) { _list = new DataLoaderList(this); } } else { _list = new DataLoaderList(this); } var ret = new DataLoaderPair <TKey, T>(_list, key); _list.Add(key, ret); _cachedList?.Add(key, ret); return(ret); } }
/// <summary> /// Internally used by DataLoaderList to start the fetch operation. /// </summary> /// <returns>A Task representing the asynchronous fetch operation</returns> private Task StartLoading(DataLoaderList listToLoad, CancellationToken cancellationToken) { if (listToLoad == null) { throw new ArgumentNullException(nameof(listToLoad)); } lock (_sync) { //once it enters the lock, it is guaranteed to exit the lock, as it does not depend on external code if (_list == listToLoad) { _list = null; } } return(FetchAsync(listToLoad.Values, cancellationToken)); }
/// <summary> /// Dispatch any pending operations. /// </summary> /// <param name="cancellationToken">Optional <seealso cref="CancellationToken"/> to pass to the fetch delegate</param> public Task DispatchAsync(CancellationToken cancellationToken = default) { //start loading the currently queued items DataLoaderList listToLoad; lock (_sync) { //once it enters the lock, it is guaranteed to exit the lock, as it does not depend on external code //cannot use Interlocked.Exchange here because that can execute during another lock listToLoad = _list; _list = null; } if (listToLoad == null) { return(Task.CompletedTask); } return(listToLoad.DispatchAsync(cancellationToken)); }
/// <summary> /// Asynchronously load data for the provided given key. /// If the key is <see langword="null"/> then a <see cref="DataLoaderResult{T}"/> containing /// <see langword="null"/> will be immediately returned. /// </summary> /// <param name="key">Key to use for loading data</param> /// <returns> /// An object representing a pending operation /// </returns> public virtual IDataLoaderResult <T> LoadAsync(TKey key) { // dictionaries do not support keys with null values (null reference values or null value types), // so in this case bypass the data loader and return null if (key == null) { return(DataLoaderResult <T> .DefaultValue); } lock (_sync) { //once it enters the lock, it is guaranteed to exit the lock, as it does not depend on external code if (_cachedList != null) { if (_cachedList.TryGetValue(key, out var ret2)) { return(ret2); } } if (_list != null) { if (_list.TryGetValue(key, out var ret2)) { return(ret2); } if (_list.Count >= MaxBatchSize) { _list = new DataLoaderList(this); } } else { _list = new DataLoaderList(this); } var ret = new DataLoaderPair <TKey, T>(_list, key); _list.Add(key, ret); _cachedList?.Add(key, ret); return(ret); } }