How to properly make app async with "async" and "await"? - c#

class Website
{
public Website(string link)
{
_linkToWeb = new RestClient(link);
}
public async Task<string> DownloadAsync(string path)
{
var request = new RestRequest(path, Method.GET);
var response = _linkToWeb.ExecuteAsync(request);
return response.Result.Content;
}
public RestClient _linkToWeb { get; set; }
}
class Program
{
public static Website API = new Website("https://api.collegefootballdata.com");
public static async Task<string> _downloadTeamsFromAPI()
{
return API.Download("/teams/fbs");
}
public static async Task<string> _downloadAdvancedInfoFromAPI()
{
return API.Download("/stats/season/advanced?year=2010");
}
public static async Task<Teams> _addTeamToDB(Teams item)
{
var tmp = new Teams
{
School = item.School,
Abbreviation = item.Abbreviation,
Conference = item.Conference,
Divison = item.Divison,
Color = item.Color,
Alt_Color = item.Alt_Color,
Team = await _getAdvancedInfoFromAPI(item.Conference)
};
return tmp;
}
public static async Task<string> _getAdvancedInfoFromAPI(string _conferenceName)
{
List<Advanced> advancedDataList = new List<Advanced>();
var advancedData = await _downloadAdvancedInfoFromAPI();
var advancedDataDeserialized = JsonSerializer.Deserialize<Advanced[]>(advancedData, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
foreach (var item in advancedDataDeserialized)
{
advancedDataList.Add(new Advanced
{
Team = item.Team,
//Conference = item.Conference,
Year = item.Year,
excludeGarbageTime = item.excludeGarbageTime,
startWeek = item.startWeek,
endWeek = item.endWeek
});
}
return await _lookForMatch(_conferenceName, advancedDataList);
}
public static async Task<string> _lookForMatch(string _Confa, List<Advanced> lista)
{
return lista
.Where(x => x.Conference == _Confa)
.Select(x => x.Team)
.FirstOrDefault();
}
public static async Task Main()
{
Console.WriteLine("Odpaliłem program!\n");
using var db = new Context();
db.Database.EnsureCreated();
Console.WriteLine("Stworzylem baze!\n");
var teams = await _downloadTeamsFromAPI();
var deserializer = JsonSerializer.Deserialize<Teams[]>(teams, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
Console.WriteLine("Zdeserializowalem dane!\n");
foreach (var item in deserializer)
{
db.Teams.Add(await _addTeamToDB(item));
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss"));
Console.WriteLine("Dodalem element do bazy...\n");
};
db.SaveChanges();
Console.WriteLine("Zapisalem dane do bazy!");
}
}
I know it's a noob question but I don't know how to make it work :/
I want to make it a bit asynchronous, because the words async and await doesn't exactly make it more asynchronous, but I don't know how to make it work anyhow asynchronous.
The app first downloads the information from API, deserializes it and stores it into var type variable. Then it downloads the advanced info from API and joins it by "Conference" item. (that is on purpose, even though it's not optimal).
There are a lot of asyncs and awaits but I don't think it anyhow runs asynchronously. What should I change to make this app actually async?

I appreciate your motive to write asynchronous code to make your application more scalable.
However after going through the sample code posted, I am afraid you need to do more learning on the concepts of asynchronous programming rather than just jumping into the console and trying to write some code which looks like asynchronous code.
Start slowly and try to understand the purpose of Task library, when to use it. What await does behind the scenes. When to wrap a return type with Task and when to mark a method as async. These are some of the main keywords which you come across in asynchronous code and a good understanding of these is a must to write/understand asynchronous code.
There are plenty of resources available online to get a hang of these concepts. For starters, you can begin looking into Microsoft Documentation
Having said that, inline is a rewrite of the sample code with proper use of async/await.
Please use this for references purpose only. Do not try to put into some production code until unless you have a good understanding of the concept.
Necessary comments are provided to explain some critical changes made.
class Website
{
public Website(string link)
{
_linkToWeb = new RestClient(link);
}
public async Task<string> DownloadAsync(string path)
{
var request = new RestRequest(path, Method.GET);
var response = await _linkToWeb.ExecuteAsync(request); //await an asynchronous call.
return response.Content; //No need to call response.Result. response content can be fetched after successful completion of asynchronous call.
}
public RestClient _linkToWeb { get; set; }
}
class Program
{
public static Website API = new Website("https://api.collegefootballdata.com");
public static async Task<string> _downloadTeamsFromAPI()
{
return await API.DownloadAsync("/teams/fbs");
}
public static async Task<string> _downloadAdvancedInfoFromAPI()
{
return await API.DownloadAsync("/stats/season/advanced?year=2010");
}
public static async Task<Teams> _addTeamToDB(Teams item)
{
var tmp = new Teams
{
School = item.School,
Abbreviation = item.Abbreviation,
Conference = item.Conference,
Divison = item.Divison,
Color = item.Color,
Alt_Color = item.Alt_Color,
Team = await _getAdvancedInfoFromAPI(item.Conference)
};
return tmp;
}
//Return type has to be Task<Teams> rather than Task<string> because the return object is Teams.
public static async Task<Teams> _getAdvancedInfoFromAPI(string _conferenceName)
{
List<Advanced> advancedDataList = new List<Advanced>();
var advancedData = await _downloadAdvancedInfoFromAPI();
var advancedDataDeserialized = JsonSerializer.Deserialize<Advanced[]>(advancedData, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
foreach (var item in advancedDataDeserialized)
{
advancedDataList.Add(new Advanced
{
Team = item.Team,
//Conference = item.Conference,
Year = item.Year,
excludeGarbageTime = item.excludeGarbageTime,
startWeek = item.startWeek,
endWeek = item.endWeek
});
}
return _lookForMatch(_conferenceName, advancedDataList);
}
//Return type is Teams and not string.
//Moreover Task<string> not required because we are not awaiting method call in this method.
public static Teams _lookForMatch(string _Confa, List<Advanced> lista)
{
return lista.Where(x => x.Conference == _Confa)
.Select(x => x.Team)
.FirstOrDefault();
}
public static async Task Main()
{
Console.WriteLine("Odpaliłem program!\n");
using var db = new Context();
db.Database.EnsureCreated();
Console.WriteLine("Stworzylem baze!\n");
var teams = await _downloadTeamsFromAPI();
var deserializer = JsonSerializer.Deserialize<Teams[]>(teams, new JsonSerializerOptions()
{
PropertyNameCaseInsensitive = true
});
Console.WriteLine("Zdeserializowalem dane!\n");
foreach (var item in deserializer)
{
db.Teams.Add(await _addTeamToDB(item));
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss"));
Console.WriteLine("Dodalem element do bazy...\n");
};
db.SaveChanges();
Console.WriteLine("Zapisalem dane do bazy!");
}
}

Related

How to consume multiple REST API in C#

Initially, I have consumed a single REST API request in C# and desearilsed the JSON response. I want to consume multiple API(2 or 3). How do I modify my code for that?
static void Main(string[] args)
{
api1();
}
public static void api1()
{
var client = new RestClient("https://dummy.restapiexample.com/api/");
var request = new RestRequest("Data");
var response = client.Execute(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string rawResponse = response.Content;
var root = JsonConvert.DeserializeObject<Rootobject>(rawResponse)
}
}
I tried to create function for each API request but I am not sure what will be the return type and how am i going to call all the functions.
public async Task<Var> api2()
{
var client = new RestClient("https://dummy.restapiexample2.com/api2/");
var request = new RestRequest("Data");
var response = client.Execute(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string rawResponse = response.Content;
var root = JsonConvert.DeserializeObject<Rootobject>(rawResponse)
return root;
}
else
return null;
}
static void Main(string[] args)
{
api1();
api2();
}
You can't return var as you have done in above, you need to return what are expecting to get from that method, for example, Rootobject.
public async Task Main(string[] args)
{
var result = await Api2();
var myProVal = result.MyProperty;
}
public static async Task <Rootobject> Api2()
{
var client = new RestClient("https://dummy.restapiexample2.com/api2/");
var request = new RestRequest("Data");
var response = await client.ExecuteAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string rawResponse = response.Content;
var root = JsonConvert.DeserializeObject<Rootobject>(rawResponse);
return root;
}
else
return null;
}
}
And here you have been using async. The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method. if you don't have such a requirement don't use async, just make the method as synchronous.
Read more: https://learn.microsoft.com/en-us/dotnet/csharp/async
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-7.0
Updated:
in the above code snippet, my Rootobject class as follows,
public class Rootobject
{
public int MyProperty { get; set; }
public string Name { get; set; }
public string age { get; set; }
public string addr { get; set; }
}
In the Main method, MyProperty is a sample property in that class. In your case it should be another one, it's up to you. I think it's better if you can learn the basics before moving to this level. good luck

C# Using async await inside foreach to execute API

Is there a way to use a foreach() to call an API multiple times with different parameters. Currently the code only executes the first item (itemOne) and not the rest of the items. I want to get back all the results and then exit the function.
The method that calls TestExecute() comes from the controller. I haven't shown controller method since it only calls TestLogs. There are no errors being shown. Basically I want to loop through all three of the items to get the itemId and store it in the list. In this case it would call the API 3 times and store the results and only then do I want to exit this function.
public class TestLogs
{
private readonly ITest _test;
public TestLogs()
{
_test = new test();
}
private async Task<TestProjectsDto> GetProjectId()
{
var date = new DateTime(2020, 04, 15);
var sapProjectNum = new TestProjectsDto
{
Projects = new List<TestProjectDto>()
};
var list = new List<string>()
{
"itemOne",
"itemTwo",
"itemThree"
};
foreach (var divList in list)
{
var proIds = await _test.GetProjectItem(divList, date);
if (proIds != null)
{
sapProjectNum.Projects.AddRange(proIds.Projects);
}
}
return sapProjectNum;
}
public async Task TestExecute()
{
// Where I call GetProjectId();
var listProjectIds = GetProjectId();
// etc
}
}
}
public class TestService : ITest
{
//etc
public async Task<TestProjectsDto> GetProjectOnSpecificDate(string divisionName, DateTime date)
{
var url = $"{test}/GetProjectOnSpecificDate.xsjs?TYPE=item={item}&Date={date:yyyy-MM-dd}";
var response = await HttpClient.GetAsync(url).ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsAsync<TestProjectsDto>().ConfigureAwait(false);
return content;
}
return null;
}
}

Anonymous asynchronicity, what is the right way?

I have a simple class that does a synchronous thing,
public static class Synchronous
{
public static void DoTheWholeThing()
{
AStuff aStuff;
using (var a = new A())
{
aStuff = a.GetStuff();
}
BStuff bStuff;
using (var b = new B())
{
bStuff = b.GetStuff();
}
var combination = CombineStuff(aStuff, bStuff);
}
private static Combination CombineStuff(AStuff aStuff, BStuff bStuff)
{
//// Magic Here
}
}
Obviously, this code is not fully defined but it does illustrate my question.
Now, the classes A and B are both responsible for retrieving data from different remote sources. Consequently, the developers of A and B have implemented asynchronous entry points called GetStuffAsync which return Task<AStuff> and Task<BStuff> respectively.
I want to take maximum advantage of the asynchronous methods and call them concurrently so I can reduce the overall wait time of my code.
Here is what I've concocted, so far.
public static class Asynchronous
{
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
var getAStuffTask = new Func<Task<AStuff>>(
async () =>
{
using (var a = new A())
{
return await a.GetStuffAsync(cancellationToken);
}
})();
var getBStuffTask = new Func<Task<BStuff>>(
async () =>
{
using (var b = new B())
{
return await b.GetStuffAsync(cancellationToken);
}
})();
var combination = CombineStuff(
await getAStuffTask,
await getBStuffTask);
}
private Combination CombineStuff(AStuff aStuff, BStuff bStuff)
{
//// Magic Here
}
}
Aside from this code looking curiously like the javascript module pattern, is this the correct approach. I don't think I should be using Task.Run as this code is clearly not CPU bound.
It seems a bit "clunky" that I need to instantiate typed delegates to do this. Is there a better way?
EDIT
following two good answers I'm in a quandary between named functions and continuations.
The code becomes radically simpler when you simply extract the anonymous methods out into named methods:
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
var getAStuffTask = GetAStuffAsync(cancellationToken);
var getBStuffTask = GetBStuffAsync(cancellationToken);
var combination = CombineStuff(
await getAStuffTask,
await getBStuffTask);
}
private static async Task<AStuff> GetAStuffAsync(CancellationToken cancellationToken)
{
using (var a = new A())
{
return await a.GetStuffAsync(cancellationToken);
}
}
private static async Task<BStuff> GetBStuffAsync(CancellationToken cancellationToken)
{
using (var b = new B())
{
return await b.GetStuffAsync(cancellationToken);
}
}
That said, if you really want to stick with the anonymous methods, you can create a helper method that will allow generic type inference and lambdas to implicitly figure out the type of the delegate:
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
var getAStuffTask = Start(async () =>
{
using (var a = new A())
{
return await a.GetStuffAsync(cancellationToken);
}
});
var getBStuffTask = Start(async () =>
{
using (var b = new B())
{
return await b.GetStuffAsync(cancellationToken);
}
});
var combination = CombineStuff(
await getAStuffTask,
await getBStuffTask);
}
public static Task<T> Start<T>(Func<Task<T>> asyncOperation)
{
return asyncOperation();
}
Use TPL continuations to call Dispose as soon as the task is complete.
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
var a = new A();
var b = new B();
// start the tasks and store them for awaiting later
var getAStuffTask = a.GetStuffAsync(cancellationToken);
var getBStuffTask = b.GetStuffAsync(cancellationToken);
// queue up continuations to dispose of the resource as soon as it is not needed
getAStuffTask.ContinueWith(() => a.Dispose());
getBStuffTask.ContinueWith(() => b.Dispose());
// await as normal
var combination = CombineStuff(
await getAStuffTask,
await getBStuffTask);
}
I am unsure if wrapping the whole method in an addition using block will accomplish anything but it may provide peace of mind.
You don't need to wrap your async calls in delegates to get them to execute immediately. If you call the GetStuffAsync methods directly without awaiting them you will have the same result.
public static class Asynchronous
{
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
using (var a = new A())
using (var b = new B()) {
var taskA = a.GetStuffAsync(cancellationToken);
var taskB = b.GetStuffAsync(cancellationToken);
await Task.WhenAll(new [] { taskA, taskB });
var combination = CombineStuff(taskA.Result, taskB.Result);
}
}
private Combination CombineStuff(AStuff aStuff, BStuff bStuff)
{
//// Magic Here
}
}
Note that this does keep the a and b objects alive during the call to CombineStuff as #Servy notes. If that is a problem the declaration of the Task objects can be moved outside of the using blocks as below:
public static class Asynchronous
{
public async static Task DoTheWholeThing(CancellationToken cancellationToken)
{
Task taskA;
Task taskB;
using (var a = new A())
using (var b = new B()) {
taskA = a.GetStuffAsync(cancellationToken);
taskB = b.GetStuffAsync(cancellationToken);
await Task.WhenAll(new [] { taskA, taskB });
}
var combination = CombineStuff(taskA.Result, taskB.Result);
}
private Combination CombineStuff(AStuff aStuff, BStuff bStuff)
{
//// Magic Here
}
}
Although this still holds onto a and b as long as both tasks are running, rather than disposing of each as they return.

Fan-out computation in WebApi using async / await

I have say an interface called:
interface IExecutor {
String Name { get; }
Task<int> Execute();
}
And two implementations of it (details are irrelevant and you can assume methods are marked with async and work as expected). Each implementation takes between 2-3 seconds to run and there will ever be between 2 and 5 implementations of IExecutor
I have a controller that needs to run all executors and return the results in as ExecutorResult objects. Where ExecutorResult is:
class ExecutorResult {
int Result; // assume usual get n set
String ExecutorName;
}
Ideally this should happen in a Fan-Out approach.
I have thought of the following approaches:
List<Task<int>> tasks = new List<Task<int>>();
foreach(var executor in executors) {
tasks.Add(executor.Execute());
}
var results = Task.WhenAll(tasks);
The problem I have with this approach is that I m not sure if this is best practice in an ASP WebAPI application. Also - given I would like to return Result objects - where Result needs an the name of the executor and the int result from Execute the above solution doesn't work as outside of the for loop I no longer have access to the Name property of each executor.
So what is the best practice for this approach (again - given a Web Api application and not a Console app)
What you have already is a best practice. What you're doing is asynchronous concurrency, which is best done with Task.WhenAll.
Note that the code:
List<Task<int>> tasks = new List<Task<int>>();
foreach(var executor in executors) {
tasks.Add(executor.Execute());
}
var results = Task.WhenAll(tasks);
can be simplified to:
var results = Task.WhenAll(executors.Select(e => e.Execute()));
In spite of the countless examples using List<Task>, you don't actually have to build one up explicitly.
This worked for me:
public class Executor : IExecutor
{
public String Name { get; set;}
public async Task<int> Execute()
{
Console.WriteLine("Executing " + Name);
await Task.Delay(3000);
Console.WriteLine("Finished Executing " + Name);
return 0;
}
}
public async Task<ExecutorResult> Execute(IExecutor executor)
{
return new ExecutorResult { ExecutorName = executor.Name,
Result = await executor.Execute() };
}
public async Task MainAsync()
{
var executors = new List<IExecutor>
{
new Executor { Name = "Executor1" },
new Executor { Name = "Executor2" },
new Executor { Name = "Executor3" }
};
List<Task<ExecutorResult>> tasks = new List<Task<ExecutorResult>>();
foreach(var executor in executors)
{
tasks.Add(Execute(executor));
}
var results = await Task.WhenAll(tasks);
}
void Main()
{
MainAsync().Wait();
}
If I understood it correctly, you're looking for somethng like this:
var tasks = new List<Task<ExecutorResult>>();
foreach (var executor in executors)
{
tasks.Add(((Func<IExecutor, Task<ExecutorResult>>)(
async (e) => new ExecutorResult
{
ExecutorName = e.Name,
Result = await e.Execute()
}))(executor));
}
var results = Task.WhenAll(tasks);
OR, following Stephen Clearly suggestion:
var results = Task.WhenAll(
from executor in executors
select ((Func<IExecutor, Task<ExecutorResult>>)(
async (e) => new ExecutorResult
{
ExecutorName = e.Name,
Result = await e.Execute()
}))(executor))
);

async / await continuation

In the code below I need to execute the three Get... methods in parallel. When a Get... method completes I need to immediately call the Save... method. Note Save... takes thing as a parameter.
All Get and Save methods must complete before DoStuffAsync returns.
My guess is that I need a continuation on the Get... methods but I dont know how to construct it.
protected async void DoStuffAsync()
{
SomeThing thing = new SomeThing { ID = 5 };
SomeRepository rep = new SomeRepository();
// We need to run all three Get... methods in parallel
// As soon as a Get... method completes we need to save the result to the correct property on thing and call the Save... method .
var getRed = rep.GetRedAsync().ContinueWith<Task<string>>(async x => { thing.Color1 = x.Result; await rep.SaveRedAsync(thing); return x; }); // does not compile
var getBlue = rep.GetBlueAsync();
var getGreen = rep.GetGreenAsync();
string red = await getRed.Result; // this is not good because getBlue may finish before getRed. We want dont want to wait on getRed before calling SaveBlue
await rep.SaveRedAsync(thing);
var b = await getBlue;
var c = await getGreen;
// thing must be fully initialized before DoStuffAsync returns
}
public class SomeThing
{
public int ID { get; set; }
public string Color1 { get; set; }
public string Color2 { get; set; }
public string Color3 { get; set; }
}
public class SomeRepository
{
public async Task<string> GetRedAsync()
{
return await Task.Run(() => "red");
}
public async Task<string> GetBlueAsync()
{
return await Task.Run(() => "blue");
}
public async Task<string> GetGreenAsync()
{
return await Task.Run(() => "green");
}
public async Task SaveRedAsync(SomeThing thing)
{
// We need thing.ID here as well as other properties
await Task.Delay(1);
}
public async Task SaveBlueAsync(SomeThing thing)
{
await Task.Delay(1);
}
public async Task SaveGreenAsync(SomeThing thing)
{
await Task.Delay(1);
}
}
Well, you could explicitly use ContinueWith - or you could break off each "get and save" into a separate async method or async lambda. For example:
async Task GetAndSaveRedAsync(SomeThing thing, SomeRepository rep)
{
var red = await rep.GetRedAsync();
thing.Red = red;
await SaveRedAsync(red);
// Return red if you want (change the return type to Task<string>)
}
// Ditto for the others
Then:
protected async void DoStuffAsync()
{
SomeThing thing = new SomeThing { ID = 5 };
SomeRepository rep = new SomeRepository();
var handleRed = GetAndSaveRedAsync(thing, rep);
var handleBlue = GetAndSaveBlueAsync(thing, rep);
var handleYellow = GetAndSaveYellowAsync(thing, rep);
// Or use Task.WhenAll
await handleRed;
await handleBlue;
await handleYellow;
}
I would not mix ContinueWith and await and rather use an async lambda directly:
protected async Task DoStuffAsync()
{
SomeThing thing = new SomeThing { ID = 5 };
SomeRepository rep = new SomeRepository();
// We need to run all three Get... methods in parallel
// As soon as a Get... method completes we need to save the result to the correct property on thing and call the Save... method .
Func<Task<X>> getRedFunc = async() =>
{
var result = await rep.GetRedAsync();
thing.Color1 = result;
await rep.SaveRedAsync(thing);
return result;
};
var getRed = getRedFunc();
var getBlue = rep.GetBlueAsync();
var getGreen = rep.GetGreenAsync();
await Task.WhenAll(getRed, getBlue, getGreen);
}
Also, don't use async void methods for anything but event handlers. You won't be able to observe the completion of a method like this or handle exceptions possibly thrown inside it.
You can try parallel framework:
using System.Threading;
using System.Threading.Tasks;
Func<Task<string>>[] functions = { rep.GetRedAsync, rep.GetBlueAsync, rep.GetGreenAsync };
Var[] GetArray = new Var[functions.Length]
int i=0;
Parallel.ForEach (var function in functions)
{
GetArray[i++]=function();
}
Note: Require .Net 4

Categories

Resources