Quantcast
Viewing latest article 33
Browse Latest Browse All 38

Centralizing the API CRUD calls in WinForms

I need to do CRUD calls inside WinForms application. For this I want to create helper methods for Post, Put, Delete and Get. This method needs to return data back to me.

For post and put, I might be passing List, myobject, string, int, bool etc. My return type might not be the same as model I am putting in.

To accomplish the above I am thinking along following line...

Class to return back to the caller

public class ApiCallResult<X> where X: class {    public HttpStatusCode StatusCode { get; set; }    public string ReasonPhrase { get; set; }    public bool IsError { get; set; }    public bool IsException { get; set; }    public string Message { get; set; }    public X Response { get; set; } //deserialized object, could be List, int string or just a single object}

And then creating the method along the following lines

public static async Task<ApiCallResult<X>> Post<T, X>(T data, X returnModel, string apiUrl){    var apiCallResult = new ApiCallResult<X> {IsError = true, Message = "No run"};    try    {        //json string         var jsonString = JsonConvert.SerializeObject(data);        using (var client = new HttpClient())        {            var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");            var response = await client.PostAsync(apiUrl, httpContent);            var jsonResponseString = await response.Content.ReadAsStringAsync();            //fill            apiCallResult.StatusCode = response.StatusCode;            apiCallResult.ReasonPhrase = response.ReasonPhrase;            if (response.IsSuccessStatusCode)            {                //deserialize                if (typeof(X).GetGenericTypeDefinition() == typeof(List<>))                {                    // X is a generic list                    apiCallResult.Response = JsonConvert.DeserializeObject<List<X>>(jsonResponseString).ToList();                }                else                {                    //single object                    apiCallResult.Message = JsonConvert.DeserializeObject<X>(jsonResponseString);                }                apiCallResult.IsError = false;            }            else            {                //error response                apiCallResult.Message = jsonResponseString;            }        }    }    catch (Exception ex)    {        apiCallResult.IsException = true;        apiCallResult.Message = ex.Message;    }    return apiCallResult;}

But obviously this has multiple issues

  1. async can return a Task and I want to return apiCallResult

  2. var apiCallResult = new ApiCallResult<T> {IsError = true, Message = "No run"}; line is resulting in The type 'T' must be a reference type in order to use it as parameter 'T'

  3. apiCallResult.Response = JsonConvert.DeserializeObject<List<X>>(jsonResponseString).ToList(); line is resulting in a Cannot convert source type 'System.Collections.Generic.List<X>' to target type 'T'

How best can I do something like this? Don't want to write multiple methods to perform this.

Update 1: Here is working sample code and its use, has some rough edges but working...

CRUD Methods

public static class ApiCrudCallHelper{    /// <summary>    /// Performs Post and returns ApiCallResult    /// </summary>    /// <typeparam name="T">model to Post, could be null, T, List T</typeparam>    /// <typeparam name="X">return model by API, could be X, List X, string </typeparam>    /// <param name="data">data to post of type T, List T</param>    /// <param name="apiUrl">api full URL like http://localhost:65152/API/Test if executing custom action, provide that as well at the end </param>    /// <returns>    /// ApiCallResult    ///     StatusCode: status code returned by the API    ///     ReasonPhrase: reason phrase returned by the API    ///     IsError: true/false    ///     IsException: true/false    ///     Message: error message, exception message, or result of OK etc results by API    ///     X ResponseObject: model returned by the API, it might not be available in all cases. Could be X, List X or string as provided by X above    /// </returns>    public static async Task<ApiCallResult<X>> Post<T, X>(T data, string apiUrl) where X : class    {        var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };        try        {            //json string             var jsonString = JsonConvert.SerializeObject(data);            using (var client = new HttpClient())            {                var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");                var response = await client.PostAsync(apiUrl, httpContent);                var jsonResponseString = await response.Content.ReadAsStringAsync();                //fill                if (response.IsSuccessStatusCode)                {                    //deserialize                    if (!typeof(X).Equals(typeof(string)))                    {                        apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);                    }                    apiCallResult.IsError = false;                }                apiCallResult.StatusCode = response.StatusCode;                apiCallResult.ReasonPhrase = response.ReasonPhrase;                apiCallResult.Message = jsonResponseString;            }        }        catch (Exception ex)        {            apiCallResult.IsException = true;            apiCallResult.Message = ex.Message;        }        return apiCallResult;    }    /// <summary>    /// Performs Put and returns ApiCallResult    /// </summary>    /// <typeparam name="T">model to Post, could be null, T, List T</typeparam>    /// <typeparam name="X">return model by API, could be X, List X, string </typeparam>    /// <param name="data">data to post of type T, List T</param>    /// <param name="apiUrl">api full URL including the Id like http://localhost:65152/API/Test/12345 if executing custom action, provide that as well </param>    /// <returns>    /// ApiCallResult    ///     HttpStatusCode StatusCode: status code returned by the API    ///     string ReasonPhrase: reason phrase returned by the API    ///     bool IsError: true/false    ///     bool IsException: true/false    ///     string Message: error message, exception message, or result of OK etc results by API    ///     X ResponseObject: model returned by the API, it might not be available in all cases. Could be X, List X or string as provided by X above    /// </returns>    public static async Task<ApiCallResult<X>> Put<T, X>(T data, string apiUrl) where X : class    {        var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };        try        {            //json string             var jsonString = JsonConvert.SerializeObject(data);            using (var client = new HttpClient())            {                var httpContent = new StringContent(jsonString, Encoding.UTF8, "application/json");                var response = await client.PutAsync(apiUrl, httpContent);                var jsonResponseString = await response.Content.ReadAsStringAsync();                //fill                if (response.IsSuccessStatusCode)                {                    //deserialize                    if (!typeof(X).Equals(typeof(string)))                    {                        apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);                    }                    apiCallResult.IsError = false;                }                apiCallResult.StatusCode = response.StatusCode;                apiCallResult.ReasonPhrase = response.ReasonPhrase;                apiCallResult.Message = jsonResponseString;            }        }        catch (Exception ex)        {            apiCallResult.IsException = true;            apiCallResult.Message = ex.Message;        }        return apiCallResult;    }    /// <summary>    /// Performs Delete and returns ApiCallResult    /// </summary>    /// <typeparam name="X">return model by API, could be X, List X, string. Usually you'll only get Ok result etc for delete, so specify string  </typeparam>    /// <param name="apiUrl">api full URL including the Id like http://localhost:65152/API/Test/12345 if executing custom action, provide that as well </param>    /// <returns>    /// ApiCallResult    ///     HttpStatusCode StatusCode: status code returned by the API    ///     string ReasonPhrase: reason phrase returned by the API    ///     bool IsError: true/false    ///     bool IsException: true/false    ///     string Message: error message, exception message, or result of OK etc results by API    ///     X ResponseObject: will only be available if api is returning a model (should not), in most cases it will not be available. Could be X, List X or string as provided by X above    /// </returns>    public static async Task<ApiCallResult<X>> Delete<X>(string apiUrl) where X : class    {        var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };        try        {            using (var client = new HttpClient())            {                var response = await client.DeleteAsync(apiUrl);                var jsonResponseString = await response.Content.ReadAsStringAsync();                //fill                if (response.IsSuccessStatusCode)                {                    //deserialize                    if (!typeof(X).Equals(typeof(string)))                    {                        apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);                    }                    apiCallResult.IsError = false;                }                apiCallResult.StatusCode = response.StatusCode;                apiCallResult.ReasonPhrase = response.ReasonPhrase;                apiCallResult.Message = jsonResponseString;            }        }        catch (Exception ex)        {            apiCallResult.IsException = true;            apiCallResult.Message = ex.Message;        }        return apiCallResult;    }    /// <summary>    /// Performs Get and returns ApiCallResult    /// </summary>    /// <typeparam name="X">return model by API, could be X, List X, string. </typeparam>    /// <param name="apiUrl">api full URL </param>    /// <returns>    /// ApiCallResult    ///     HttpStatusCode StatusCode: status code returned by the API    ///     string ReasonPhrase: reason phrase returned by the API    ///     bool IsError: true/false    ///     bool IsException: true/false    ///     string Message: error message, exception message, or result of OK etc results by API    ///     X ResponseObject: Could be X, List X or string as provided by X above    /// </returns>    public static async Task<ApiCallResult<X>> Get<X>(string apiUrl) where X : class    {        var apiCallResult = new ApiCallResult<X> { IsError = true, Message = "No run" };        try        {            using (var client = new HttpClient())            {                var response = await client.GetAsync(apiUrl);                var jsonResponseString = await response.Content.ReadAsStringAsync();                //fill                if (response.IsSuccessStatusCode)                {                    //deserialize                    if (!typeof(X).Equals(typeof(string)))                    {                        apiCallResult.ResponseObject = JsonConvert.DeserializeObject<X>(jsonResponseString);                    }                    apiCallResult.IsError = false;                }                apiCallResult.StatusCode = response.StatusCode;                apiCallResult.ReasonPhrase = response.ReasonPhrase;                apiCallResult.Message = jsonResponseString;            }        }        catch (Exception ex)        {            apiCallResult.IsException = true;            apiCallResult.Message = ex.Message;        }        return apiCallResult;    }}

and call (WinForms)

public partial class Form1 : Form{    public Form1()    {        InitializeComponent();    }    private void btnClear_Click(object sender, EventArgs e)    {        txtResults.Text = "";    }    //standard post    private void btnTestApiPost_Click(object sender, EventArgs e)    {        btnClear.PerformClick();        DoPost();    }    private async void DoPost()    {        var dp = new DummyPost { Id = 12345, Name = "XYZ" };        //we might be posting one model and getting back another model. Here the same model is getting posted. So .Post<inputModel Type, returnModel Type>        var apiCallResult = await ApiCrudCallHelper.Post<DummyPost, DummyPost>(dp, ApiUrls.TestApiUrl);        txtResults.AppendText(apiCallResult.Message);    }    //post to custom action    private void btnTestApiPostExtra_Click(object sender, EventArgs e)    {        btnClear.PerformClick();        DoPostExtra();    }    private async void DoPostExtra()    {        var dp = new DummyPost { Id = 12345, Name = "XYZ" };        //we might be posting one model and getting back another model. Here the same model is getting posted. So .Post<inputModel Type, returnModel Type>        var apiUrl = string.Format("{0}/ExtraPost", ApiUrls.TestApiUrl);        var apiCallResult = await ApiCrudCallHelper.Post<DummyPost, DummyPost>(dp, apiUrl);        txtResults.AppendText(apiCallResult.Message);    }    //post to custom action and getting ok result back     private void btnTestApiPostExtraOkResult_Click(object sender, EventArgs e)    {        btnClear.PerformClick();        DoPostExtraOkResult();    }    private async void DoPostExtraOkResult()    {        var dp = new DummyPost { Id = 12345, Name = "XYZ" };        //we might be posting one model and getting back another model. Here the same model is getting posted. So .Post<inputModel Type, returnModel Type>        var apiUrl = string.Format("{0}/ExtraPostOk", ApiUrls.TestApiUrl);        var apiCallResult = await ApiCrudCallHelper.Post<DummyPost, string>(dp, apiUrl);        txtResults.AppendText(apiCallResult.Message);    }    //post with multiline model return    private void btnTestApiPostExtraMultiLine_Click(object sender, EventArgs e)    {        btnClear.PerformClick();        DoPostExtraMultiLineReturn();    }    private async void DoPostExtraMultiLineReturn()    {        var dp = new DummyPost { Id = 12345, Name = "XYZ" };        //we might be posting one model and getting back another model. Here the same model is getting posted. So .Post<inputModel Type, returnModel Type>        var apiUrl = string.Format("{0}/ExtraPostMultiLineReturn", ApiUrls.TestApiUrl);        var apiCallResult = await ApiCrudCallHelper.Post<DummyPost, List<DummyPost>>(dp, apiUrl);        txtResults.AppendText(apiCallResult.Message);    }    //standard put    private void btnTestApiPut_Click(object sender, EventArgs e)    {        btnClear.PerformClick();        DoPut();    }    private async void DoPut()    {        var dp = new DummyPost { Id = 12345, Name = "XYZ" };        //we might be posting one model and getting back another model. Here the same model is getting posted. So .Post<inputModel Type, returnModel Type>        //since this is a PUT call, append the id to the url as well        var apiUrl = string.Format("{0}/98745", ApiUrls.TestApiUrl);        var apiCallResult = await ApiCrudCallHelper.Put<DummyPost, DummyPost>(dp, apiUrl);        txtResults.AppendText(apiCallResult.Message);    }    //standard delete    private void btnTestApiDelete_Click(object sender, EventArgs e)    {        btnClear.PerformClick();        DoDelete();    }    private async void DoDelete()    {        //not posting any model, should get back a string responce but we can get a model as well (why?)        //since this is a DELETE call, append the id to the url as well        var apiUrl = string.Format("{0}/98745", ApiUrls.TestApiUrl);        var apiCallResult = await ApiCrudCallHelper.Delete<string>(apiUrl);        txtResults.AppendText(apiCallResult.Message);    }    //standard get    private void btnTestApiGet_Click(object sender, EventArgs e)    {        btnClear.PerformClick();        DoGet();    }    private async void DoGet()    {        var apiUrl = ApiUrls.TestApiUrl;        var apiCallResult = await ApiCrudCallHelper.Get<string>(apiUrl);        txtResults.AppendText(apiCallResult.Message);    }    //get by id    private void btnTestApiGetId_Click(object sender, EventArgs e)    {        btnClear.PerformClick();        var apiUrl = string.Format("{0}/98745", ApiUrls.TestApiUrl);        DoGetId();    }    private async void DoGetId()    {        var apiUrl = string.Format("{0}/98745", ApiUrls.TestApiUrl);        var apiCallResult = await ApiCrudCallHelper.Get<string>(apiUrl);        txtResults.AppendText(apiCallResult.Message);    }    //custom get action    private void btnTestApiGetAll_Click(object sender, EventArgs e)    {        btnClear.PerformClick();        DoGetAll();    }    private async void DoGetAll()    {        var apiUrl = string.Format("{0}/All", ApiUrls.TestApiUrl);        var apiCallResult = await ApiCrudCallHelper.Get<List<DummyPost>>(apiUrl);        txtResults.AppendText(apiCallResult.Message);    }}

Viewing latest article 33
Browse Latest Browse All 38

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>