Variable not getting populated after async operation - c#

I have a result variable that is set to null. This variable is then passed into this.GetAsyncData where an async operation is performed that populates the results variable with data.
public async Task<IActionResult> MyAction()
{
IEnumerable<object> result = null;
string fileName;
switch (type)
{
case type1:
fileName = await this.GetAsyncData(result);
break;
default:
throw new Exception();
}
await _call.External(result, fileName);
}
private async Task<string> GetAsyncData(IEnumerable<object> result)
{
result = await anotherCall();
return "NewFileName";
}
However, in my case, when it reaches _call.External(result, fileName);, result is always null. Even though, when I step through the this.GetAsyncData, its getting populated with data.

You are not returning the result. I.e., you use it as input parameter. In a normal method, you could use an out parameter to return this result, but here, you must use the regular task return value. You can do it by using a Value Tuple containing both, the file name and the enumerable:
private async Task<(string, IEnumerable<object>)> GetAsyncData()
{
IEnumerable<object> result = await anotherCall();
return ("NewFileName", result);
}
Usage:
public async Task<IActionResult> MyAction()
{
switch (type)
{
case type1:
var (fileName, result) = await this.GetAsyncData();
break;
default:
throw new Exception();
}
await _call.External(result, fileName);
}
Btw.: you can simply use an IEnumerable instead of an IEnumerable<object>.

The local variable result cannot be changed by the async GetAsyncData method because this variable is provided by-value, not by ref.
Unfortunately, you cannot use ref arguments in async methods.
Just use tuples to get both values back (file name and your enumerable).
IEnumerable<object> result = null;
string fileName;
(fileName, result) = await this.GetAsyncData();
private async Task<(string, IEnumerable<object>)> GetAsyncData()
{
var result = await anotherCall();
return ("NewFileName", result);
}

As #Spevacus said above, you need to pass result by reference, because you are not modifying the collection itself, rather where the variable holding the reference to the collection (you're setting it from null to something non-null).
But unfortunately, async methods cannot have ref or in/out parameters, so perhaps you can do something like:
private async Task<(string, IEnumerable<object>)> GetAsyncData()
{
var result = new List<object>();
return ("NewFileName", result);
}
and call it like:
(fileName, result) = await this.GetAsyncData();

Async methods can't have ref parameters. You can package your data in the result of your async method using a Tuple or other data structure.
public async Task<IActionResult> MyAction()
{
IEnumerable<object> result = null;
string fileName;
switch (type)
{
case type1:
Tuple<string, IEnumerable<object>> tuple;
tuple = await this.GetAsyncData();
fileName = tuple.Item1;
result = tuple.Item2;
break;
default:
throw new Exception();
}
await _call.External(result, fileName);
}
private async Task<Tuple<string, IEnumerable<object>>> GetAsyncData()
{
IEnumerable<object> data = await anotherCall();
string fileName = "NewFileName";
var tuple = new Tuple<string, IEnumerable<object>>(fileName, data);
return tuple;
}

Instead of changing where result points (which will have no effect after the method leaves), try adding to it.
public async Task<IActionResult> MyAction()
{
List<object> result = new List<object>(); //This is a list now
string fileName;
switch (type)
{
case type1:
fileName = await this.GetAsyncData(result);
break;
default:
throw new Exception();
}
await _call.External(result, fileName);
}
private async Task<string> GetAsyncData(List<object> result)
{
result.AddRange( await anotherCall() ); //Add the result of anotherCall() to result
return "NewFileName";
}

Related

Failing to write async recursive iteration function

I'm trying to write a recursion method to retrieve the parent of a object (and that parent etc). This in itself isn't a problem but the calls are async which result in the following error:
The body of '.....Recursive(string)' cannot be an iterator block because 'Task<IEnumerable>' is not an iterator interface type [...]csharp(CS1624)
The code:
private async Task<IEnumerable<string>> Recursive(string objectId)
{
var result = await GetParent(objectId);
if (result?.Length > 0)
{
yield return result;
await Recursive(objectId);
}
}
private async Task<string> GetParent(string objectId)
{
await Task.Run(() => { return $"{objectId}/parent"; });
}
I have also tried IAsyncEnumerable but that resulted in the folllowing error:
'IAsyncEnumerable' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'IAsyncEnumerable' could be found (are you missing a using directive or an assembly reference?) [...]csharp(CS1061)
private async IAsyncEnumerable<string> Recursive(string objectId)
{
var result = await GetParent(objectId);
if (result?.Length > 0)
{
yield return result;
await Recursive(objectId);
}
}
private async Task<string> GetParent(string objectId)
{
await Task.Run(() => { return $"{objectId}/parent"; });
}
I'm going to write a while loop to get this to work. But I'm interested if this is possible at all.
Update 2:
Ok, I think I got it. Thanks guys.
private async IAsyncEnumerable<string> Recursive(string objectId)
{
var result = await GetParent(objectId);
if (result?.Length > 0)
{
yield return result;
await foreach (var r2 in Recursive(objectId))
{
yield return r2;
}
}
}
private async Task<string> GetParent(string objectId)
{
await Task.Run(() => { return $"{objectId}/parent"; });
}
The current code wouldn't compile even if it was synchronous, and the result was IEnumerable<string>. The results of Recursive are never returned. It's not possible to just return an IEnumerable from an iterator either.
This code would work. Whether it does anything useful is another matter :
private IEnumerable<string> Recursive(string objectId)
{
var result = GetParent(objectId);
if (!string.IsNullOrEmpty(result))
{
yield return result;
foreach(var r in Recursive(result))
{
yield return r;
}
}
}
private string GetParent(string objectId)
{
return $"{objectId}/parent";
}
Getting it to work asynchronously only needs changing to IAsyncEnumerable and using await:
private async IAsyncEnumerable<string> Recursive(string objectId)
{
var result = await GetParent(objectId);
if (!string.IsNullOrEmpty(result))
{
yield return result;
await foreach(var r in Recursive(result))
{
yield return r;
}
}
}
private Task<string> GetParent(string objectId)
{
return Task.FromResult($"{objectId}/parent");
}

Cleaning up code with generics

I am somewhat new to Generics and I came across this issue, I have repetitive code that I am trying to clean up. The signature is different but the code being executed is the same, is there a way to pass in a generic type instead of having to specify each type in a new signature?
public JsonResult<JsonData> GetServiceData(Func<IServiceResponse<IEnumerable<Order>>> func)
{
var response = func();
var jsonDataContainer = Mapper.Map<JsonData>(response);
var result = GetJsonResult(jsonDataContainer);
return result;
}
public JsonResult<JsonData> GetServiceData(Func<IServiceResponse<List<int>>> func)
{
var response = func();
var jsonDataContainer = Mapper.Map<JsonData>(response);
var result = GetJsonResult(jsonDataContainer);
return result;
}
public JsonResult<JsonData> GetServiceData(Func<IServiceResponse<User>> func)
{
var response = func();
var jsonDataContainer = Mapper.Map<JsonData>(response);
var result = GetJsonResult(jsonDataContainer);
return result;
}
It's hard to answer this question definitively, because you haven't specified the signature of Mapper.Map.
But, if Mapper.Map can take an IServiceResponse<T> of any type T, then this would work.
public JsonResult<JsonData> GetServiceData<T>(Func<IServiceResponse<T>> func)
{
IServiceResponse<T> response = func();
var jsonDataContainer = Mapper.Map<JsonData>(response);
var result = GetJsonResult(jsonDataContainer);
return result;
}

How to add an async "await" to an addrange select statement?

I have a function like this:
public async Task<SomeViewModel> SampleFunction()
{
var data = service.GetData();
var myList = new List<SomeViewModel>();
myList.AddRange(data.select(x => new SomeViewModel
{
Id = x.Id,
DateCreated = x.DateCreated,
Data = await service.GetSomeDataById(x.Id)
}
return myList;
}
My await isn't working as it can only be used in a method or lambda marked with the async modifier. Where do I place the async with this function?
You can only use await inside an async method/delegate. In this case you must mark that lambda expression as async.
But wait, there's more...
Select is from the pre-async era and so it doesn't handle async lambdas (in your case it would return IEnumerable<Task<SomeViewModel>> instead of IEnumerable<SomeViewModel> which is what you actually need).
You can however add that functionality yourself (preferably as an extension method), but you need to consider whether you wish to await each item before moving on to the next (sequentialy) or await all items together at the end (concurrently).
Sequential async
static async Task<TResult[]> SelectAsync<TItem, TResult>(this IEnumerable<TItem> enumerable, Func<TItem, Task<TResult>> selector)
{
var results = new List<TResult>();
foreach (var item in enumerable)
{
results.Add(await selector(item));
}
return results.ToArray();
}
Concurrent async
static Task<TResult[]> SelectAsync<TItem, TResult>(this IEnumerable<TItem> enumerable, Func<TItem, Task<TResult>> selector)
{
return Task.WhenAll(enumerable.Select(selector));
}
Usage
public Task<SomeViewModel[]> SampleFunction()
{
return service.GetData().SelectAsync(async x => new SomeViewModel
{
Id = x.Id,
DateCreated = x.DateCreated,
Data = await service.GetSomeDataById(x.Id)
}
}
You're using await inside of a lambda, and that lambda is going to be transformed into its own separate named method by the compiler. To use await it must itself be async, and not just be defined in an async method. When you make the lambda async you now have a sequence of tasks that you want to translate into a sequence of their results, asynchronously. Task.WhenAll does exactly this, so we can pass our new query to WhenAll to get a task representing our results, which is exactly what this method wants to return:
public Task<SomeViewModel[]> SampleFunction()
{
return Task.WhenAll(service.GetData().Select(
async x => new SomeViewModel
{
Id = x.Id,
DateCreated = x.DateCreated,
Data = await service.GetSomeDataById(x.Id)
}));
}
Though maybe too heavyweight for your use case, using TPL Dataflow will give you finer control over your async processing.
public async Task<List<SomeViewModel>> SampleFunction()
{
var data = service.GetData();
var transformBlock = new TransformBlock<X, SomeViewModel>(
async x => new SomeViewModel
{
Id = x.Id,
DateCreated = x.DateCreated,
Data = await service.GetSomeDataById(x.Id)
},
new ExecutionDataflowBlockOptions
{
// Let 8 "service.GetSomeDataById" calls run at once.
MaxDegreeOfParallelism = 8
});
var result = new List<SomeViewModel>();
var actionBlock = new ActionBlock<SomeViewModel>(
vm => result.Add(vm));
transformBlock.LinkTo(actionBlock,
new DataflowLinkOptions { PropagateCompletion = true });
foreach (var x in data)
{
transformBlock.Post(x);
}
transformBlock.Complete();
await actionBlock.Completion;
return result;
}
This could be substantially less long-winded if service.GetData() returned an IObservable<X> and this method returned an IObservable<SomeViewModel>.

how to return method which has task<t> return type

public override Task<IdentityResult> ValidateAsync(AppUser item)
{
if(item.Email != "a#a.com")
{
IEnumerable<string> errors = new List<string>() { "error1" };
}
}
The IdentityResultjust needs a simple array or ienumerable of strings in its constructor.
Whats the correct syntax?
If you absolutely need to have ValidateAsync() instead of just Validate(), use a TaskCompletionSource to simulate.
public override Task<IdentityResult> ValidateAsync(AppUser item)
{
var result = new IdentityResult();
var tcs = new TaskCompletionSource<IdentityResult>();
if(item.Email != "a#a.com")
{
IEnumerable<string> errors = new List<string>() { "error1" };
result.Add(errors)
}
tcs.SetResult(result);
return tcs.Task;
}
Using Task.Run creates unnecessary overhead.
EDIT: I'm not exactly sure if TaskCompletionSource is any better than Task.Run. I'd love to hear the answer.
You could use Task.Run() to start and get a reference to a new Task:
public IdentityResult Validate( AppUser item )
{
IdentityResult result;
// do stuff
return result;
}
public override Task<IdentityResult> ValidateAsync( AppUser item )
{
return Task.Run( () => Validate(item) );
}

Issue creating generic wrapper for async library

I have a class Library https://github.com/trydis/FIFA-Ultimate-Team-2014-Toolkit I want to make a wrapper for, instead of just calling FutClient I want to make a FutClientWrapper where I can handle exceptions, reloggin and loggin the interface I want to wrap is https://github.com/trydis/FIFA-Ultimate-Team-2014-Toolkit/blob/master/UltimateTeam.Toolkit/IFutClient.cs
So I made http://pastebin.com/0EJdCbbr
Code snippet
public async Task<AuctionResponse> SearchAsync(SearchParameters searchParameters)
{
return await Invoke(f => f.SearchAsync(searchParameters), searchParameters);
}
public async Task<AuctionResponse> PlaceBidAsync(AuctionInfo auctionInfo, uint bidAmount = 0)
{
return await Invoke(f => f.PlaceBidAsync(auctionInfo, bidAmount), auctionInfo);
}
public async Task<Item> GetItemAsync(long resourceId)
{
return await Invoke(f => f.GetItemAsync(resourceId), resourceId);
}
public TResult Invoke<TResult>(Func<FutClient, TResult> func, object requestDetails = null,
[CallerMemberName] string exceptionMessage = null)
{
try
{
if (LastException + new TimeSpan(0, 0, 30) < DateTime.Now)
{
TResult result = func(_futClient);
return result;
}
}
catch (Exception e)
{
Here I try to wrap and catch any exception thrown inside the Futclient, but instead I get a null exception in for example this line
return await Invoke(f => f.GetItemAsync(resourceId), resourceId);
And it seems its because my
TResult result = func(_futClient);
return result;
Is not run async and it just returns an empty response (All null values)
So I changed it to look like http://pastebin.com/pJkPr2xN
Code snippet:
public async Task<AuctionResponse> GetTradePileAsync()
{
return await NewInvoke<AuctionResponse>(new Task<object>(() => _futClient.GetTradePileAsync()));
}
public async Task<WatchlistResponse> GetWatchlistAsync()
{
return await NewInvoke<WatchlistResponse>(new Task<object>(() => _futClient.GetWatchlistAsync()));
}
public async Task<TResult> NewInvoke<TResult>(Task<object> taskToRun) where TResult : class
{
try
{
taskToRun.Start();
Task.WaitAll(taskToRun);
return taskToRun.Result as TResult;
}
catch (AggregateException ex)
{
//ex.InnerException
return null;
}
catch (Exception e)
{
return null;
}
}
But also here I get exceptions outside my invoke method
So how should I make this so it handles both Task and Task as a response type and takes x number of arguments
And it should then throw any exceptions inside the Invoke method so I only have one place where exceptions needs to be caught
You must await inside the try block before you return, otherwise the exception will not be cought by the catch. Your first attempt was actually closer to the correct way to do it.
public async Task<TResult> Invoke<TResult>(Func<FutClient, Task<TResult>> func, object requestDetails = null,
[CallerMemberName] string exceptionMessage = null)
{
try
{
if (LastException + new TimeSpan(0, 0, 30) < DateTime.Now)
{
TResult result = await func(_futClient).ConfigureAwait(false); //Here we both await for the result and set `ConfigureAwait` to false so we don't need to waist extra time trying to marshal back on to the original Synchronization context as we have no need for it for the rest of the method.
return result;
}
}
catch (Exception e)
{
//...
You then just use it like the following
public Task<AuctionResponse> PlaceBidAsync(AuctionInfo auctionInfo, uint bidAmount = 0)
{
return Invoke(f => f.PlaceBidAsync(auctionInfo, bidAmount), auctionInfo);
}
There is no need to await in this outer function, you can just directly return the Task returned by Invoke

Categories

Resources