LP13
LP13

Reputation: 34089

Asynchronous programming with .NET Core?

I am newbie to .NET Core and asynchronous programming. I am trying to implement a console application to do the following:

  1. The console application should work as intermediator between two external APIs. eg. API-1 and API-2.

  2. It should call API-1 after every 10 milliseconds to get data.

  3. Immediately call API-2 to submit the data that is received from API-1.

  4. Console Application needs to wait for API-1 to receive data, but does not have to wait for the response from API-2.

Below is my code. It not working as expected. At first it invokes API-1 in 10 milliseconds as expected, but after that its invoking API-1 ONLY AFTER it receives response from API-2.
So assume API-2 takes 20 seconds, API-1 is also getting invoked after 20 seconds.

How do I make API-2 call asynchronous so it does not have to wait for API-2 response?

namespace ConsoleApp1
{
    public class Program
    {
        private static Timer _timer;
        private const int TIME_INTERVAL_IN_MILLISECONDS = 10; // 10 milliseconds
        private const int API2_DELAY = 20000; // 20 seconds
        public static void Main(string[] args)
        {
            Dowork().Wait();
            Console.WriteLine("Press Any Key to stop");
            Console.ReadKey();
            Console.WriteLine("Done");
        }

        private static async Task Dowork()
        {
            var data = new SomeData();
            _timer = new Timer(CallAPI1, data, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite);
            await Task.Yield();            
        }

        private static async void CallAPI1(object state)
        {           
            var data = state as SomeData;

            Console.WriteLine("Calling API One to get some data.");
            data.SomeStringValue = DateTime.Now.ToString();

            await CallAPI2(data);
            _timer.Change(TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite);
        }

        private static async Task CallAPI2(SomeData data)
        {           
            Console.WriteLine("Calling API Two by passing some data received from API One " + data.SomeStringValue);

            // the delay represent long running call to API 2
            await Task.Delay(API2_DELAY);
        }
    }
}

POCO class

namespace ConsoleApp1
{
    public class SomeData
    {
        public string SomeStringValue { get; set; }
    }
}

Also note that API-1 and API-2 will be developed in ASP.NET Core 1

Update1
Let me rephrase above sentence. The API-1 would be developed in .Net core but API-2 would be windows workflow service. That means we can make multiple calls to WF. The WF will persist the requests and process one at a time.

Update2
After going through all the answers and links provided. I am thinking to use windows service as intermediator instead of console application. Right now .Net core does not support window service but has this nuget-package that can host .Net core inside windows service or I might use classic windows service using 4.6.2. I guess I can do the asyncrous implementation inside windows service as well.

Upvotes: 0

Views: 8395

Answers (2)

Bob
Bob

Reputation: 555

Remove the await when calling API2

    private static async void CallAPI1(object state)
    {
        var data = state as SomeData;

        Console.WriteLine("Calling API One to get some data.");
        data.SomeStringValue = DateTime.Now.ToString();
        //Before this will cause the program to wait
        await CallAPI2(data);
        // Now it will call and forget
        CallAPI2(data);
        _timer.Change(TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite);
    }

Edit:

As David points out, of course there is many way to solve this problem. This is not a correct approach to solve your problem.

Another method of doing things is use quartz.net

  1. Schedule API1 as a repeating job
  2. When API1 is done, schedule another job to run API2 as a standalone job

This way when API2 fails you can replay/repeat the job.

Upvotes: 0

David Pine
David Pine

Reputation: 24525

There are a lot of things that I would do differently in this situation. Rather than using a timer I would use Task.Delay, also - I would most certainly wait for API2 to complete before attempting to throw more data at it. Additionally, I would ensure that my async methods are Task or Task<T> returning, notice your CallAPI1 call isn't, I understand it's a timer callback -- but that is another issue.

Consider the following:

async Task IntermediateAsync()
{
    Console.WriteLine("Press ESC to exit...");

    while (Console.ReadKey(true).Key != ConsoleKey.Escape)
    {
        var result = await _apiServiceOne.GetAsync();
        await _apiServiceTwo.PostAsync(result);

        // Wait ten milliseconds after each successful mediation phase
        await Task.Delay(10);
    }
}

This will act in the following manner:

  1. Print a line instructing the user how to exit
  2. Start loop
  3. Get the result of API1
  4. Pass the result to API2
  5. Wait 10 milliseconds
  6. [Step 2]

Finally, this is the same suggestion regardless of whether or not your using .NET Core. Any API interactions should follow the same guidelines.

Notes

Using a fire-and-forget on the second API call is simply setting your code up for failure. Since it is an API call there is more than likely going to be some latency with the I/O bound operations and one should assume that a tight loop of 10 milliseconds is only going to flood the availability on that endpoint. Why not simply wait for it to finish, what reason could you possibly have?

Upvotes: 2

Related Questions