Changing the c# code to Async method

Kalis

New member
Joined
Feb 4, 2020
Messages
4
Programming Experience
Beginner
Hello,

I am using the below code to programatically call an API and purge the cache requests. It works as is but I have a need to change this to an async method instead of sync. I tried to use the async..wait method below. Can you please see if it looks OK.
Sync Method:
using Akamai.EdgeGrid.Auth;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;
using System.Configuration;
using System.Text;
using System.Net.Security;
using System.Net.Http;
using System.Threading.Tasks;

namespace AkamaiPurge
{
    class Program
    {
        public class PurgeObject
        {
            [JsonProperty("objects")]
            public string[] Objects { get; set; }
            [JsonProperty("hostname")]
            public string Hostname { get; set; }
        }
        static void Main(string[] args)
        {
            var urlv3 = new PurgeObject();
            string[] paths = System.IO.File.ReadAllLines(@"urlpaths.txt");
            urlv3.Objects = paths;
            Console.WriteLine(paths.Length);
            urlv3.Hostname = "example.com";
            Purge(urlv3);
            Console.ReadLine();
        }
        static WebResponse Purge(PurgeObject purgeObject)
        {
            var client_token = ConfigurationManager.AppSettings["client_token"];
            var access_token = ConfigurationManager.AppSettings["access_token"];
            var secret = ConfigurationManager.AppSettings["secret"];
            var api_url = ConfigurationManager.AppSettings["api_url"];
            var ccu_url = ConfigurationManager.AppSettings["ccu_url"];

            ClientCredential credentials = new ClientCredential(client_token, access_token, secret);
            EdgeGridV1Signer signer = new EdgeGridV1Signer(null, 100000);

            Uri uri = new Uri(api_url + ccu_url);
            WebRequest request = WebRequest.Create(uri);
            request.Method = "POST";
            request.ContentType = "application/json";

            // convert string to stream
            var serializedObject = JsonConvert.SerializeObject(purgeObject);
            Console.WriteLine(serializedObject);
            byte[] byteArray = Encoding.UTF8.GetBytes(serializedObject);
            MemoryStream uploadStream = new MemoryStream(byteArray);

            try
            {
                // Signs the current request using the Akamai C# library. Creates an Authentication header for the request.
                request = signer.Sign(request, credentials, uploadStream);
            }
            catch (Exception error)
            {
                Console.WriteLine("Error creating signature");
                throw (error);
            }

            // avoid internal memory allocation before buffering the output
            if (request is HttpWebRequest)
                ((HttpWebRequest)request).AllowWriteStreamBuffering = false;

            using (Stream requestStream = request.GetRequestStream())
            using (uploadStream)
                uploadStream.CopyTo(requestStream, 1024 * 1024);
            try
            {
                var httpResponse = (WebResponse)request.GetResponse();
                using (Stream data = httpResponse.GetResponseStream())
                using (var reader = new StreamReader(data))
                {
                    string text = reader.ReadToEnd();
                    Console.WriteLine(text);
                }
                return httpResponse;
            }
            catch (WebException error)
            {
                using (WebResponse response = error.Response)
                {
                    HttpWebResponse httpResponse = (HttpWebResponse)response;
                    Console.WriteLine("Error code: {0}", httpResponse.StatusCode);
                    using (Stream data = response.GetResponseStream())
                    using (var reader = new StreamReader(data))
                    {
                        string text = reader.ReadToEnd();
                        Console.WriteLine(text);
                    }
                }
                return error.Response;
            }
        }
    }
}
Async Method:
using Akamai.EdgeGrid.Auth;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;
using System.Configuration;
using System.Text;
using System.Net.Security;
using System.Net.Http;
using System.Threading.Tasks;

namespace AkamaiPurge
{
    class Program
    {
        public class PurgeObject
        {
            [JsonProperty("objects")]
            public string[] Objects { get; set; }
            [JsonProperty("hostname")]
            public string Hostname { get; set; }
        }
        static async Task Main(string[] args)
        {
            var urlv3 = new PurgeObject();
            string[] paths = System.IO.File.ReadAllLines(@"urlpaths.txt");
            urlv3.Objects = paths;
            Console.WriteLine(paths.Length);
            urlv3.Hostname = "example.com";
            await Purge(urlv3);
            Console.ReadLine();
        }
        private static async Task<WebResponse> Purge(PurgeObject purgeObject)
        {
            var client_token = ConfigurationManager.AppSettings["client_token"];
            var access_token = ConfigurationManager.AppSettings["access_token"];
            var secret = ConfigurationManager.AppSettings["secret"];
            var api_url = ConfigurationManager.AppSettings["api_url"];
            var ccu_url = ConfigurationManager.AppSettings["ccu_url"];

            ClientCredential credentials = new ClientCredential(client_token, access_token, secret);
            EdgeGridV1Signer signer = new EdgeGridV1Signer(null, 100000);

            Uri uri = new Uri(api_url + ccu_url);
            WebRequest request = WebRequest.Create(uri);
            request.Method = "POST";
            request.ContentType = "application/json";

            // convert string to stream
            var serializedObject = JsonConvert.SerializeObject(purgeObject);
            Console.WriteLine(serializedObject);
            byte[] byteArray = Encoding.UTF8.GetBytes(serializedObject);
            MemoryStream uploadStream = new MemoryStream(byteArray);

            try
            {
                // Signs the current request using the Akamai C# library. Creates an Authentication header for the request.
                request = signer.Sign(request, credentials, uploadStream);
            }
            catch (Exception error)
            {
                Console.WriteLine("Error creating signature");
                throw (error);
            }

            // avoid internal memory allocation before buffering the output
            if (request is HttpWebRequest)
                ((HttpWebRequest)request).AllowWriteStreamBuffering = false;

            using (Stream requestStream = request.GetRequestStream())
            using (uploadStream)
                uploadStream.CopyTo(requestStream, 1024 * 1024);
            try
            {
                //var httpResponse = (WebResponse)request.GetResponse();
                var httpResponse = await request.GetResponseAsync();
                using (Stream data = httpResponse.GetResponseStream())
                using (var reader = new StreamReader(data))
                {
                    string text = reader.ReadToEnd();
                    Console.WriteLine(text);
                }
                return httpResponse;
            }
            catch (WebException error)
            {
                using (WebResponse response = error.Response)
                {
                    HttpWebResponse httpResponse = (HttpWebResponse)response;
                    Console.WriteLine("Error code: {0}", httpResponse.StatusCode);
                    using (Stream data = response.GetResponseStream())
                    using (var reader = new StreamReader(data))
                    {
                        string text = reader.ReadToEnd();
                        Console.WriteLine(text);
                    }
                }
                return error.Response;
            }
        }
    }
}
 
Last edited by a moderator:

JohnH

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
1,073
Location
Norway
Programming Experience
10+
insertcode.png
 

jmcilhinney

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
3,563
Location
Sydney, Australia
Programming Experience
10+
Asking us to see if it looks OK is not the first step. The first step is to test it for yourself. Does it work when you do so? If not then obviously it is not OK and you should tell us exactly what actually happens. If you have tested it and it works but you're concerned that there might be scenarios that you haven't considered in which it will fail then you need to state that. ALWAYS provide a FULL and CLEAR explanation of the problem.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
2,604
Location
Chesapeake, VA
Programming Experience
10+
No it is not okay. All you did was change the call to request.GetResponse() to request.GetResponseAsync() and dealt with the resulting fallout that the compiler started whining about. You did nothing to take full advantage of asynchronous calls. You don't asynchronously read from your text file. You don't asynchronously send your request. You don't asynchronously read your response stream. You don't asynchronously read the error response stream. You don't asynchronously write out to the console.

Out of curiosity, why do you need asynchronous calls for this program. It's a console application that does a single request. It's not like your blocking user interaction while waiting for the request to be processed.

Out of further curiosity, why use the lower level HttpWebRequest when you could be writing less code using the WebClient? NVM. After reading the Akamai edge signer source code, it looks like you are forced to use HttpWebRequest due to the nature of their Library/API.

As an aside, your exception handling for a failed request looks suspect. But if it works for you, so be it.
 
Last edited:

Kalis

New member
Joined
Feb 4, 2020
Messages
4
Programming Experience
Beginner
Asking us to see if it looks OK is not the first step. The first step is to test it for yourself. Does it work when you do so? If not then obviously it is not OK and you should tell us exactly what actually happens. If you have tested it and it works but you're concerned that there might be scenarios that you haven't considered in which it will fail then you need to state that. ALWAYS provide a FULL and CLEAR explanation of the problem.
Yes, I should have been more clear. I am able to test and its working fine. The part I'm suspicious is about the asynchronous calls.
 

Kalis

New member
Joined
Feb 4, 2020
Messages
4
Programming Experience
Beginner
No it is not okay. All you did was change the call to request.GetResponse() to request.GetResponseAsync() and dealt with the resulting fallout that the compiler started whining about. You did nothing to take full advantage of asynchronous calls. You don't asynchronously read from your text file. You don't asynchronously send your request. You don't asynchronously read your response stream. You don't asynchronously read the error response stream. You don't asynchronously write out to the console.

Out of curiosity, why do you need asynchronous calls for this program. It's a console application that does a single request. It's not like your blocking user interaction while waiting for the request to be processed.

Out of further curiosity, why use the lower level HttpWebRequest when you could be writing less code using the WebClient? NVM. After reading the Akamai edge signer source code, it looks like you are forced to use HttpWebRequest due to the nature of their Library/API.

As an aside, your exception handling for a failed request looks suspect. But if it works for you, so be it.
Thank you Skydiver. This is only for test and in production there will be batches of text files on a daily basis and so the asynchronous call was requested. I knew it would be a premature way of just changing that one method to async but my coding skill is only that much. I can easily pass it on to the other team to enhance this but I wanted to give it a try. Yes, exception handling is working as expected with the scenarios I tried.
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
2,604
Location
Chesapeake, VA
Programming Experience
10+
In production, will it be a console app? Or a WPF or Xamarin app? Or a web application? A web service? A Windows Service?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
2,604
Location
Chesapeake, VA
Programming Experience
10+
So the thought behind the web application is that the user will select some paths to be deleted, and then click the submit button, or an AJAX call is perform to initiate the delete. I'm not seeing how async will help in this situation since the user will have to wait for confirmation that the deletes were completed by Akamai. Is the though behind using async is that the web server can service other web requests while waiting for Akamai to complete the deletes? If so, how many other users do you realistically expect to also be doing web requests against the same server? I'm wondering if this is a case of premature optimization, or if your profiling has already identified that ASP.NET threads are being tied up waiting for Akamai to respond.
 
Top Bottom