0

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.

7
  • Possible duplicate of async at console app in C#? Commented Aug 11, 2016 at 23:53
  • @LP13 Why is API2 slow? what is it doing? Doing multiple calls to a slow service would probably saturate it. You could possibly suffer either thread starvation, I/O saturation among other things... so could you be more specific Commented Aug 12, 2016 at 3:16
  • Avoid async void Commented Aug 12, 2016 at 6:06
  • API-2 is windows workflow service doing some long running task. For console application it should be fire and forget call. Commented Aug 12, 2016 at 14:53
  • @JeroenHeier why do I need to avoid async void? Commented Aug 12, 2016 at 16:54

2 Answers 2

2

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?

Sign up to request clarification or add additional context in comments.

3 Comments

Thank You. Performance is the reason I don't want to wait for WF (i.e API-2). I want to submit as many request as possible to WF without waiting for its response. Now 10 milliseconds I used here is just for example, In reality it would be polling API-1 every after 10 minutes. Please see update1
@LP13, my answer is still the desired functionality. Especially if in your real-world situation, you'd want to wait for it to complete. Performance isn't going to matter at this point since you're a background worker -- and waiting ten minutes between loops anyways.
The intermediator certainly cannot wait for Workflow to complete ( sorry I called it API-2 earlier) These are long running processes and that's the reason we are using Windows Workflow.
0

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.

1 Comment

Just because the OP is explicitly asking for this doesn't mean it is correct. Feel free to tell the OP when you believe they should consider something else.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.