Domanda

I need to do 4 HttpClient requests from different method using the same HttpClient instance, which I think causing a thread safety issue. Let me explain this more.

Inside a Windows Phone application i'm developing there is a 'MainViewModel' class. This class has an async method to get data from a webserver and process the response. The Async method is named 'LoadData'. I even have 2 more Async methods ('ScheduleArrayAsync' and 'CurrActivityAsync') in this class to help process the data I'm getting from the server.

From the 'LoadData' method I do 3 HttpClient requests (this part is working like charm), while processing the response from all those three requests, I need to call 'ScheduleArrayAsync' method. From there I have to make a new HttpClient Request (here comes the problem). This last request will never generate and no error code is generated not even when I use try/catch statement.

What makes me think it's a threading issue is that, if I move the last HttpClient request to the 'LoadData' method, just as a test, it works again.

The MainViewModel class:-

public class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel()
    {
        this.Employees = new ObservableCollection<Employee>();
    }

    public ObservableCollection<Employee> Employees { get; private set; }

    private JsonTextWriter jsonW;

    private string Owner;

    private RequestResponse reqPList;

    public bool IsDataLoaded
    {
        get;
        private set;
    }
    public async void LoadData()
    {
        var baseUri = new Uri("https://uri/");

        await CookieHandler.GetCookies(baseUri); // A separate request to get some cookies

        reqPList = new RequestResponse();  // The class that handle the Httpclinet

        await reqPList.GetResponse(baseUri, pList); // First request
        XmlConvertor.ConvertToXml(reqPList.Response);
        var phoneListResponse = XmlConvertor.XmlString;

        await reqPList.GetResponse(baseUri, currActiv); // Second request
        XmlConvertor.ConvertToXml(reqPList.Response);
        var currActivResponse = XmlConvertor.XmlString;

        await reqPList.GetResponse(baseUri, sched);  // Third request
        XmlConvertor.ConvertToXml(reqPList.Response);
        var schedResponse = XmlConvertor.XmlString;

        //await reqPList.GetSlotInforPOST("154215");
        var handler = new DataHandler();
        await handler.phoneListHandler(phoneListResponse);
        await handler.CurrActivitiesHandler(currActivResponse);
        await handler.ScheduleHandler(schedResponse);
        /// Do some processing included call this line 

                    #region Current activity
                    CurrActivityAsync(item, handler.currActivitiesJSON);
                    #endregion


        this.IsDataLoaded = true;
    }

    private async void CurrActivityAsync(JToken token, string jString)
    {
        // Some processing
    }

    private async void ScheduleArrayAsync(JToken token, string jString)
    {
        try
        {
            // Do some more processing and call the fourth request 
                            if (addedInfo[0].Contains("slotInfo"))
                                await reqPList.GetSlotInforPOST(addedInfo[1]);
                            else if (addedInfo[0].Contains("vacationInfo"))
                                await reqPList.GetVacationSlotInfoPOST(addedInfo[1], addedInfo[2]);

        }
        catch (Exception exp)
        {

            var d = exp.Message;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (null != handler)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Below is the RequestResponse class:-

public class RequestResponse
{
    public string Response { get; private set; }

    private HttpClient client = new HttpClient(new HttpClientHandler()
    {
        UseCookies = true,
        CookieContainer = CookieHandler.Cookiejar,
        AllowAutoRedirect = false,
        AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
    });

    public async Task<string> GetResponse(Uri baseuri, string uriString)
    {
        if (client.BaseAddress == null)
        {
            client.BaseAddress = baseuri; 
        }
        client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/xhtml+xml"));
        var response = await client.GetAsync(baseuri + uriString);
        string webresponse = null;
        if (response.IsSuccessStatusCode)
        {
            var resp = await response.Content.ReadAsByteArrayAsync();
            var encode = Encoding.GetEncoding("iso-8859-1");
            var respString = encode.GetString(resp, 0, resp.Length - 1);
            webresponse = respString;
        }
        return Response = webresponse;
    }

    public async Task<string> GetSlotInforPOST(string timeId)
    {
        /// If the method is called from inside 'LoadData' it works.
        /// But if it is called from inside ScheduleArrayAsync, it will break at line marked with //***
        try
        {
            var baseUri = new Uri("https://uri/");
            const string slotInfo = "cgi-bin/slotInfo.pl";

            if (client.BaseAddress == null)
                client.BaseAddress = baseUri;
            client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
            HttpContent content = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string,string>("timeId",timeId)
            });
            var response = await client.GetAsync(baseUri + slotInfo);  // ***
            string webresponse;
            if (response.IsSuccessStatusCode)
            {
                var respo = await client.PostAsync(baseUri + slotInfo, content);
                var resp = await respo.Content.ReadAsByteArrayAsync();
                var encode = Encoding.GetEncoding("iso-8859-1");
                var respString = encode.GetString(resp, 0, resp.Length - 1);
                webresponse = respString;

            }
        }
        catch (Exception exp)
        {
            var s = exp.Message;
        }
        return Response;
    }

    public async Task<string> GetVacationSlotInfoPOST(string vacationId, string date)
    {
        // Do some HttpClient Request
    }
}

Am I understanding the issue right? How to overcome it? as I really need to make the last HttpClient Request from 'ScheduleArrayAsync' and not from 'LoadData'

Edit Just wanted to mention that during my efforts to solve the problem I had different instance of HttpClient in each method inside the RequestResponse class. Beside as I mentioned above, when I call the fourth request from inside 'LoadData' method everything is working as it is intended even when it's one instance of httpclient.

È stato utile?

Soluzione

It's possibly the async void that you have as the signature in some methods. Using async void will prevent calling functions from trapping throw exceptions. Exceptions thrown from async void methods can only be trapped by global exception handlers.

Altri suggerimenti

Lastly i have fixed it, thanks to Darrel.

To fix the problem i needed to change the signature of 'CurrActivityAsync' method.

from

private async void CurrActivityAsync(JToken token, string jString)

to

private void CurrActivityAsync(JToken token, string jString)

As this method had no await inside it that is why it didn't need async in the signature. My bad i didn't mentioned that in my original post.

This was the only thing i needed to do, But i even changed the signature of my 'ScheduleArrayAsync' to be more correct for Async.

from

private async void ScheduleArrayAsync(JToken token, string jString)

to

private async Task ScheduleArrayAsync(JToken token, string jString)

Darrel please can you write your last comment as an answer so you get some credits.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top