Правильное выполнение задач в приложении WPF - PullRequest
1 голос
/ 20 марта 2019

Я ищу несколько советов о том, как справиться с Task s в WPF, и мне было интересно, может ли кто-нибудь взглянуть на мой код и указать, что я делаю неправильно?

В основномприложение получает почтовый индекс из пользовательского интерфейса, который используется для создания службы Task, которая получит долготу / широту, к которой можно получить доступ через экземпляр для использования в другой службе или в самом интерфейсе.

Я думаю, что яможет иметь состояние гонки, которое, я надеюсь исправить, например, когда ResultTextBlock.Text установлено, это ноль, но, переходя к реализации, я вижу, что эти значения установлены.

Любой совет по реализации Task и подключению будетс благодарностью.

Сервисный код

class PostcodeService
{
    string _result;
    string _postcode;

    HttpResponseMessage _response;        
    RootObject rootObject;

    public double Latitude { get; private set; }
    public double Longitude { get; private set; }        

    public PostcodeService(string postcode)
    {
        this._postcode = postcode;
        rootObject = new RootObject();
    }

    public async Task<string> GetLongLatAsync()
    {        
        using (HttpClient client = new HttpClient())
        {
            client.BaseAddress = new Uri("https://api.postcodes.io/postcodes/" + this._postcode);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));  

            try
            {
                _response = await client.GetAsync(client.BaseAddress);
                if(_response.IsSuccessStatusCode)
                {
                    //cast result into model and then set long/lat properties which can then be used in the UI
                    _result = await _response.Content.ReadAsStringAsync();                     

                    rootObject = JsonConvert.DeserializeObject<RootObject>(_result);
                    Longitude = Double.Parse(rootObject.result.longitude.ToString());
                    Latitude =  Double.Parse(rootObject.result.latitude.ToString());
                }                                      
            }
            catch(Exception ex)
            {
                ex.ToString();
            }
        }

        TaskCompletionSource<string> tc = new TaskCompletionSource<string>(_result);

        return tc.ToString();
    }
}

Код пользовательского интерфейса

private void PostcodeButton_Click(object sender, RoutedEventArgs e)
{
    _clearStatus();

    if (_validatePostcode())
    {
        Task T1 = Task.Factory.StartNew(async () =>
        {
            // get long lat from api
            _postcode = new PostcodeService(PostcodeTextBox.Text);
            await _postcode.GetLongLatAsync();
        });

        //Race condition?
        ResultTextBlock.Text = _postcode.Latitude.ToString();
    }
}

Ответы [ 2 ]

2 голосов
/ 20 марта 2019

Ваш GetLongLatAsync() метод должен вернуть string:

public async Task<string> GetLongLatAsync()
{
    using (HttpClient client = new HttpClient())
    {
        client.BaseAddress = new Uri("https://api.postcodes.io/postcodes/" + this._postcode);
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(
            new MediaTypeWithQualityHeaderValue("application/json"));

        _response = await client.GetAsync(client.BaseAddress);
        string result = null;
        if (_response.IsSuccessStatusCode)
        {
            //cast result into model and then set long/lat properties which can then be used in the UI
            result = await _response.Content.ReadAsStringAsync();

            rootObject = JsonConvert.DeserializeObject<RootObject>(_result);
            Longitude = Double.Parse(rootObject.result.longitude.ToString());
            Latitude = Double.Parse(rootObject.result.latitude.ToString());
        }
        return result;
    }
}

... и вы просто должны ждать GetLongLatAsync() в пользовательском интерфейсе:

private async void PostcodeButton_Click(object sender, RoutedEventArgs e)
{
    _clearStatus();
    if (_validatePostcode())
    {
        // get long lat from api
        _postcode = new PostcodeService(PostcodeTextBox.Text);
        string result = await _postcode.GetLongLatAsync();
        ResultTextBlock.Text = result.ToString();
    }
}

Вам не нужно ни использовать TaskCompletionSource, ни начинать новый Task для вызова асинхронного метода.

2 голосов
/ 20 марта 2019

Обработчики событий позволяют использовать асинхронную пустоту

Ссылка Асинхронное / ожидание - лучшие практики асинхронного программирования

private async void PostcodeButton_Click(object sender, RoutedEventArgs e) {
    _clearStatus();

    if (_validatePostcode()) {
        // get long lat from api
        _postcode = new PostcodeService(PostcodeTextBox.Text);
        await _postcode.GetLongLatAsync(); //Offload UI thread
        //Back on UI thread
        ResultTextBlock.Text = _postcode.Latitude.ToString();
    }
}

Во-вторых, чтобы избежать исчерпания потока, создайтеодин HttpClient вместо того, чтобы создавать и удалять его, когда вам нужно выполнить это действие

Ссылка Вы используете HttpClient неправильно

Дизайн сервиса должен быть реорганизованна отдельные проблемы

public class Location {
    public Location(double lat, double lon) {
        Latitude = lat;
        Longitude = lon;
    }
    public double Latitude { get; private set; }
    public double Longitude { get; private set; }    
}

public class PostcodeService {
    private static Lazy<HttpClient> client;
    static PostcodeService() {
        client = new Lazy<HttpClient>(() => {
            HttpClient httpClient = new HttpClient();
            httpClient.BaseAddress = new Uri("https://api.postcodes.io/postcodes/");
            httpClient.DefaultRequestHeaders.Accept.Clear();
            httpClient.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
            return httpClient;
        });
    }

    public async Task<Location> GetLongLatAsync(string postcode) {}
        var response = await client.Value.GetAsync(postcode);
        if(response.IsSuccessStatusCode) {
            //cast result into model and then set long/lat properties which can then be used in the UI
            var rootObject = await response.Content.ReadAsAsync<RootObject>();
            var Longitude = Double.Parse(rootObject.result.longitude.ToString());
            var Latitude =  Double.Parse(rootObject.result.latitude.ToString());
            var result = new Location(Latitude, Longitude);
            return result;
        }
        return null;
    }
}

Теперь обработчик события можно изменить на

private async void PostcodeButton_Click(object sender, RoutedEventArgs e) {
    _clearStatus();

    if (_validatePostcode()) {
        // get long lat from api
        var service = new PostcodeService();
        var location = await service.GetLongLatAsync(PostcodeTextBox.Text); //Offload UI thread
        //Back on UI thread
        if(location != null) {

            ResultTextBlock.Text = location.Latitude.ToString();
        } else {
            //Some message here
        }

    }
}
...