Implementing Periodic API Calls and Error Handling in C# Windows Forms Application

Introduction

integrating APIs to fetch and process data is a common requirement. This article explores how to implement periodic API calls and robust error handling in a C# Windows Forms application. We'll delve into a practical example that demonstrates fetching data from multiple APIs, processing it, and handling potential errors gracefully.

Setting Up the Windows Forms Application

To begin, let's outline the basic setup of our Windows Forms application. We'll create a form (Form1) where we'll initiate our API calls using a timer for periodic updates.

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

In the Main method, we initialize our application and start Form1, which will host our UI and API interaction logic.

Implementing API Interaction and Timer

The core functionality revolves around fetching data from configured API endpoints at regular intervals. Here’s how we can set up the timer and API interaction in Form1.

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}
public partial class Form1 : Form
{
    System.Timers.Timer t;
    public Form1()
    {
        InitializeComponent();
        // Initialize timer
        t = new System.Timers.Timer();
        t.AutoReset = false; // Set to false to handle timing manually
        t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
        t.Interval = Convert.ToInt32(WebConfigurationManager.AppSettings["GetOrdertimer"]);
        t.Start(); // Start the timer immediately
        // Initiate the initial API call
        demoGet();
    }
    private void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        try
        {
            // Reset timer interval
            t.Interval = Convert.ToInt32(WebConfigurationManager.AppSettings["GetOrdertimer"]);
            t.Start(); // Restart the timer for the next interval
            // Perform the API call
            demoGet();
        }
        catch (Exception ex)
        {
            // Handle any unexpected exceptions here
            MessageBox.Show($"An error occurred: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
    private void demoGet()
    {
        try
        {
            // Retrieve API URLs from configuration
            var url1 = WebConfigurationManager.AppSettings["apiurl1"];
            var url2 = WebConfigurationManager.AppSettings["apiurl2"];
            var url3 = WebConfigurationManager.AppSettings["apiurl3"];
            // Example of retrieving data using a stored procedure
            var ds = ClassDals.DataSet(" spname ()");
            // Process retrieved data into a model
            PostModel Posresponse = new PostModel();
            Posresponse.POSModeldetails1 = ds.Tables[0].AsEnumerable().Select(x => new PostModel()
            {
                id = x.Field<string>("id"),
                desc = x.Field<string>("desc"),
                amount = x.Field<decimal>("amount"),
            }).ToList();
            // Example of splitting data into batches
            var list = StaticItems.SplitList(Posresponse.POSModeldetails1, 100);
            foreach (var a1 in list)
            {
                // Example of filtering based on a condition
                var Updatefilteredresp = a1.Where(x => !string.IsNullOrEmpty(x.pg_trackid)).ToList();
                if (Updatefilteredresp.Any())
                {
                    Updatefilteredresp.ToList().ForEach(x =>
                    {
                        string lstrError = "";
                        // Example of API interaction based on Type description
                        if (x.Typedesc == "tstbk")
                        {
                            var getres = url1;
                            var UpdateAPIURL = getres + x.id;
                            var response = PostDataJSON(UpdateAPIURL, "", "POST", "application/json; charset=utf-8", "", ref lstrError);
                            // Process response as needed
                        }
                        else if (x.Typedesc == "ice")
                        {
                            var getres = url2;
                            var UpdateAPIURL = getres + x.ckid;
                            var response = PostDataJSON(UpdateAPIURL, "", "POST", "application/json; charset=utf-8", "", ref lstrError);
                            // Process response as needed
                        }
                        else if (x.Typedesc == "Note")
                        {
                            var getres = url3;
                            var UpdateAPIURL = getres + x.ackid;
                            var response = PostDataJSON(UpdateAPIURL, "", "POST", "application/json; charset=utf-8", "", ref lstrError);
                            // Process response as needed
                        }
                    });
                }
            }
        }
        catch (Exception ex)
        {
            // Handle any unexpected exceptions here
            MessageBox.Show($"An error occurred: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
    public static string PostDataJSON(string url, string content, string method, string contentType, string customHeader, ref string errorMessage)
    {
        try
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            HttpWebRequest request = CreateWebRequestJSON(url, method, contentType, customHeader);
            if (method.ToUpper() == "POST")
            {
                byte[] byteArray = Encoding.UTF8.GetBytes(content);
                request.ContentType = contentType;
                request.ContentLength = byteArray.Length;
                using (Stream dataStream = request.GetRequestStream())
                {
                    dataStream.Write(byteArray, 0, byteArray.Length);
                }
            }
            using (WebResponse response = request.GetResponse())
            using (Stream dataStream = response.GetResponseStream())
            using (StreamReader reader = new StreamReader(dataStream))
            {
                return reader.ReadToEnd();
            }
        }
        catch (WebException webEx)
        {
            // Handle web exceptions
            return HandleWebException(webEx);
        }
        catch (Exception ex)
        {
            // Handle other exceptions
            errorMessage = ex.Message;
            return string.Empty;
        }
    }
    private static string HandleWebException(WebException webEx)
    {
        string errorMessage;
        if (webEx.Response != null)
        {
            using (Stream stream = webEx.Response.GetResponseStream())
            using (StreamReader reader = new StreamReader(stream))
            {
                errorMessage = reader.ReadToEnd();
            }
        }
        else
        {
            errorMessage = webEx.Message;
        }
        return errorMessage;
    }
    public static HttpWebRequest CreateWebRequestJSON(string url, string method, string contentType, string customHeader)
    {
        string apId = WebConfigurationManager.AppSettings["Id"];
        string seKey = WebConfigurationManager.AppSettings["Key"];
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = method;
        request.ContentType = contentType;
        request.Headers.Add("x-api-version", "2000-01-01");
        request.Headers.Add("x-client-id", apId);
        request.Headers.Add("x-client-secret", seKey);
        if (!string.IsNullOrEmpty(customHeader))
        {
            var headers = customHeader.Split('$');
            if (headers.Length >= 2)
            {
                request.Headers.Add("Token", headers[0]);
                request.Headers.Add("Authorisedkey", headers[1]);
            }
        }
        return request;
    }
}

Explanation

  1. Timer Initialization and API Calls

    • We use System.Timers.Timer to trigger the demoGet() method at regular intervals (GetOrdertimer from configuration).
    • Upon timer expiration (t_Elapsed), we reset and restart the timer, ensuring periodic updates.
  2. Fetching Data and Processing

    • demoGet() fetches API URLs from configuration and retrieves data using a stored procedure (ClassDals.DataSet(" spname ()").
    • Data is processed into a PostModel and split into manageable batches (StaticItems.SplitList).
  3. API Interaction

    • For each batch, data is filtered (Updatefilteredresp) based on specific conditions (!string.IsNullOrEmpty(x.pg_trackid)).
    • Depending on Typedesc values ("Booking", "ice", "Note"), corresponding API URLs are constructed (UpdateAPIURL) and POST requests are sent using PostDataJSON.
  4. Error Handling

    • PostDataJSON method handles exceptions (WebException, general exceptions) and returns error messages if the request fails.
    • HandleWebException extracts and returns detailed error messages from WebException responses, enhancing error handling robustness.

Conclusion

Integrating periodic API calls and robust error handling in a C# Windows Forms application ensures reliable data synchronization with external APIs. By leveraging timers and structured error management, developers can create responsive applications capable of seamlessly interacting with remote data sources while maintaining resilience against potential failures. This approach not only enhances application reliability but also facilitates efficient data-driven workflows within the Windows Forms environment.