在WPF应用程序中正确实现任务

时间:2019-03-20 13:17:19

标签: c# wpf asynchronous async-await task

我正在寻找有关WPF中Task的一些建议,并且想知道是否有人可以查看我的代码并指出我在做错什么?

基本上,该应用程序从UI中获取用于实例化Task服务的邮政编码,该服务将获得经度/纬度,可通过实例访问该经度以用于其他服务或UI本身。

我认为我可能想解决一种竞态条件,因为当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();
    }
}

UI代码

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 个答案:

答案 0 :(得分:2)

事件处理程序允许使用异步void

引用Async/Await - Best Practices in Asynchronous Programming

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而不是在需要执行此操作时创建并处理它

引用You're using HttpClient wrong

服务的设计应重构为单独的关注点

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
        }

    }
}

答案 1 :(得分:2)

您的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来调用异步方法。