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.

LP13
  • 30,567
  • 53
  • 217
  • 400
  • Possible duplicate of [async at console app in C#?](http://stackoverflow.com/questions/17630506/async-at-console-app-in-c) – Jonathan Allen Aug 11 '16 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 – Gerardo Grignoli Aug 12 '16 at 03:16
  • Avoid [async void](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx) – Jeroen Heier Aug 12 '16 at 06:06
  • API-2 is windows workflow service doing some long running task. For console application it should be fire and forget call. – LP13 Aug 12 '16 at 14:53
  • @JeroenHeier why do I need to avoid async void? – LP13 Aug 12 '16 at 16:54
  • There are several arguments. See link in the previous comment: "Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started." – Jeroen Heier Aug 12 '16 at 17:09
  • @JeroenHeier Got it. Thank you – LP13 Aug 12 '16 at 17:18

2 Answers2

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?

David Pine
  • 23,787
  • 10
  • 79
  • 107
  • 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 Aug 12 '16 at 17:10
  • @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. – David Pine Aug 12 '16 at 18:10
  • 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. – LP13 Aug 12 '16 at 18:20
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.

Bob
  • 545
  • 3
  • 8
  • 1
    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. – David Pine Aug 12 '16 at 01:23