0

I have one method in Windows Form App(.NET) where I make Rest call, deserialize the json in object and show the result in UI.

So I copied the same exact method from Windows Form App(.NET) to a new WPF App(.NET) but the Rest response time changed.

Here is the Rest response time in:

Windows Form App(.NET) in milliseconds

WPF App(.NET) in milliseconds

The method in Windows Form App(.NET):

TargetFramework

    private Stopwatch Stopwatch = new Stopwatch();
    public Uri url = new Uri("http://xxx.xxx.xxx.xxx");
    private readonly HttpClient _client;

    //constructor
    public Main()
    {
       _client = new HttpClient();
       _client.BaseAddress = url;
       var contentType = new MediaTypeWithQualityHeaderValue("application/json");
       _client.DefaultRequestHeaders.Accept.Add(contentType);
       timer.Start();
       timer.Interval = 1;
     }

 public void Timer_Tick(object sender, EventArgs e)
        {
            timer.Stop();

            try
            {
                 string JsonD= @"{""bal"":{""bla"":0},""data"":{""bla"":""bla""}}";

                _stopwatch.Restart();
                //JsonD is string fot HttpContent
                var contentData = new StringContent(JsonD);
                using (var responseMessage = _client.PostAsync("/xxx/xxx/xxx", contentData).Result)
                {
                    if (responseMessage.IsSuccessStatusCode)
                    {
                        string strContext = responseMessage.Content.ReadAsStringAsync().Result;
                        var result= System.Text.Json.JsonSerializer.Deserialize<Dis>(strContext); 
                    }
                }
             _stopwatch.Stop();
            txbRestResp.Text  = _stopwatch.ElapsedMilliseconds.ToString();
 catch (Exception ex)
            {
                MessageBox.Show(ex.StackTrace.ToString() + " - " + ex.Message);
            }
            timer.Start();
        }

The method in WPF App(.NET):

TargetFramework

private Stopwatch Stopwatch = new Stopwatch();

public MainWindow()
{
  InitializeComponent();
  BgWorker = new BackgroundWorker { WorkerReportsProgress = true };
  BgWorker.DoWork += ResetAll;
}

private void ResetAll(object sender, DoWorkEventArgs e)
{
        while (true)
        {
          _stopwatch.Restart();
          Display();
          _stopwatch.Stop();

          lblRestResponse.Content = _stopwatch.ElapsedMilliseconds.ToString();             
        }
}

private void Display()
{ 
        string JsonD= @"{""bal"":{""bla"":0},""data"":{""bla"":""bla""}}";
        string BaseUrl = "http://xxx.xxx.xxx.xxx";
        StringContent httpContentDistanza = new StringContent(JsonD);
        using var client = new HttpClient { BaseAddress = new Uri(BaseUrl) };

        using var responseMessage = client.PostAsync("/xxx/xxx/xxx", httpContentDistanza).Result;
        if (responseMessage.IsSuccessStatusCode)
        {
            string strContext = responseMessage.Content.ReadAsStringAsync().Result;

            var result = System.Text.Json.JsonSerializer.Deserialize<Dis>(strContext);
          }
}

Why is there so much difference between the response ? Right now I'm working in WPF App(.NET) and I need to obtain the same Rest response like in Windows Form App(.NET). I was expecting that WPF should be better.

Am I doing somthing wrong?

Any suggestions/improvements?

ewardz1
  • 29
  • 9
  • 4
    Not sure how much impact, but you are including the creation of your `HttpClient` in your timer process in WPF, but it has already been created in your WinForms app. Try moving the creation of `HttpClient` out into your main method. Also, make sure you are not recreating multiple `HttpClients` (it doesn't look like you are). See: https://learn.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/ – Jonathan Dec 29 '20 at 19:49
  • You might consider switching from `BackgroundWorker` to async/await in WPF. See: [How to run and interact with an async Task from a WPF gui](https://stackoverflow.com/q/27089263/3744182) and [Async/await vs BackgroundWorker](https://stackoverflow.com/q/12414601/3744182), specifically [this answer](https://stackoverflow.com/a/16366418/3744182). – dbc Dec 29 '20 at 20:07
  • Actually I have more than 5 methods like Display(). I put it in infinite loop because I need to know every moment if result of Rest call change. And if the result changes I need to make some operation. So in some way is interrogation... – ewardz1 Dec 29 '20 at 20:15

1 Answers1

3

I did a simple WPF program that reads google main page and shows the average time of the HTTP call.

Then I did the same with a console application that reads the same page 100 times and shows the average time.

Both applications give me the same average time (about 98ms).

So, I will post my code, but the main topic is: do not create a HttpClient for each call! Microsoft advices to create a single instance of HttpClient and use it as many time as possible.

This is my MainWindow.

XAML

<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
    <Label>Call counter:</Label>
    <TextBlock x:Name="txtCallCounter" />
    <Label>Last call time:</Label>
    <TextBlock x:Name="txtCallTimer" />
    <Label>Average call time:</Label>
    <TextBlock x:Name="txtCallAverage" />
</StackPanel>

Code behind

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        // Http Client
        client = new HttpClient();

        // Worker
        worker = new BackgroundWorker();
        worker.DoWork += ResetAll;
        worker.RunWorkerAsync();
    }



    /// <summary>
    /// Http client
    /// </summary>
    private HttpClient client { get; }

    /// <summary>
    /// Background worker
    /// </summary>
    private BackgroundWorker worker { get; }

    /// <summary>
    /// Background worker procedure
    /// </summary>
    private void ResetAll(object sender, DoWorkEventArgs e)
    {
        int counter = 0;
        long totalTime = 0;
        long average = 0;
        var stopWatch = new Stopwatch();
        while (true)
        {
            try
            {
                stopWatch.Restart();

                // Read google page
                var responseMessage = client.GetAsync("http://www.google.com").GetAwaiter().GetResult();
                if (responseMessage.IsSuccessStatusCode)
                {
                    var googlePageContent = responseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
                }

                stopWatch.Stop();

                counter++;
                totalTime += stopWatch.ElapsedMilliseconds;
                average = totalTime / counter;

                // Use dispatcher since the worker and UI thread has to be syncronized
                Dispatcher.Invoke(() =>
                {
                    txtCallCounter.Text = counter.ToString();
                    txtCallTimer.Text = stopWatch.ElapsedMilliseconds.ToString();
                    txtCallAverage.Text = average.ToString();
                });

                break;
            }
            // Exceptions not managed due the case that the client or any other object could
            // be disposed on window closing/closed. The best is to manage the
            // background worker in a better way, but it is not the topic of the post
            catch { }
        }
    }


    /// <summary>
    /// On close, clear objects
    /// </summary>
    protected override void OnClosing(CancelEventArgs e)
    {
        base.OnClosing(e);
        worker.DoWork -= ResetAll;
        worker.Dispose();
        client.Dispose();
    }
}

Adapt this code to your example (url, request/response, ...) and see if things work better

PiGi78
  • 415
  • 2
  • 6
  • Thanks! I istanziate HttpClient in the construct and pass it in the method. That changed a lot. From 16 milliseconds has arrived to 4 milliseconds in WPF. – ewardz1 Dec 30 '20 at 07:46