【问题标题】:HttpClient To Get List With Async/Await OperationHttpClient 使用 Async/Await 操作获取列表
【发布时间】:2017-05-29 13:43:04
【问题描述】:

我正在尝试从我之前编写的 web api 中获取列表。然后我将在 Xamarin.Forms ListView 中使用该列表。我的代码在这里:

public static class DataSource
{

    public static async Task<Restoran[]> GetRestoransAsync()
    {
        // ... Use HttpClient.
        using (HttpClient client = new HttpClient())
        using (HttpResponseMessage response = await client.GetAsync(page))
        using (HttpContent content = response.Content)
        {
            // ... Read the string.
            string result = await content.ReadAsStringAsync();

            var restorans = JsonConvert.DeserializeObject<Restoran[]>(result);
            return restorans;
        }

    }

}

我的内容页面:

public class MenuPage : ContentPage
{
    ListView listView;
    List<Restoran> restorans = new List<Restoran>();
    async Task LoadRestorans()
    {
        restorans = (await DataSource.GetRestoransAsync()).ToList();
    }

    public MenuPage(string masa)
    {
        var loadData = LoadRestorans();
        loadData.Wait();
        listView = new ListView(ListViewCachingStrategy.RecycleElement)
        {
        ItemsSource = restorans,
            ItemTemplate = new DataTemplate(() => {
                var nativeCell = new CustomCell();
                return nativeCell;
            })
        };
    }
}

但是当我调试这段代码时,“LoadRestorans()”方法在“restorans”列表初始化之前被调用。我想我不明白异步方法的心态。我该怎么办?

【问题讨论】:

    标签: c# asynchronous xamarin async-await httpclient


    【解决方案1】:

    你有两个选择。

    使用异步工厂方法创建页面

    public class MenuPage : ContentPage {
        ListView listView;
        List<Restoran> restorans = new List<Restoran>();
    
        private async Task LoadRestoransAsync() {
            restorans = (await DataSource.GetRestoransAsync()).ToList();
            listView = new ListView(ListViewCachingStrategy.RecycleElement) {
                ItemsSource = restorans,
                ItemTemplate = new DataTemplate(() => {
                    var nativeCell = new CustomCell();
                    return nativeCell;
                })
            };
        }
    
        public MenuPage(string masa) {
            //...        
        }
    
        public static async Task<MenuPage> CreateMenuPageAsync(string masa) {
            var page = new MenuPage(masa);
            await page.LoadRestoransAsync();
            return pagel
        }
    }
    

    然后在其他异步事件处理程序中像这样使用它

    var page = await MenuPage.CreateMenuPageAsync("<masa here>");
    

    OnAppearing 事件中进行。

    订阅页面/视图的Appearing事件

    protected override void OnAppearing() {
        this.Appearing += Page_Appearing;
    }
    

    并在实际的偶数处理程序上调用您的异步代码

    private async void Page_Appearing(object sender, EventArgs e) {
        //...call async code here
    
        //unsubscribing from the event
        this.Appearing -= Page_Appearing;
    }
    

    整个课程看起来像这样

    public class MenuPage : ContentPage {
        ListView listView;
        List<Restoran> restorans = new List<Restoran>();
    
        private async Task LoadRestoransAsync() {
            restorans = (await DataSource.GetRestoransAsync()).ToList();
            listView = new ListView(ListViewCachingStrategy.RecycleElement) {
                ItemsSource = restorans,
                ItemTemplate = new DataTemplate(() => {
                    var nativeCell = new CustomCell();
                    return nativeCell;
                })
            };
        }
    
        public MenuPage(string masa) {
            //...        
        }
    
        protected override void OnAppearing() {
            this.Appearing += Page_Appearing;
        }
    
        private async void Page_Appearing(object sender, EventArgs e) {
            //...call async code here
            await LoadRestoransAsync();
    
            //unsubscribing from the event
            this.Appearing -= Page_Appearing;
        }
    
    }
    

    之前发生的情况是,通过调用 .Wait()(这是一个阻塞调用),该类混合了异步调用和阻塞调用(如 .Result.Wait()),这可能导致死锁。因此,为什么您在尝试测试时无法通过该方法。

    【讨论】:

    • 我使用了您的第二个解决方案。它有效,但我必须更新 listview 和 ContentPage 的子项才能在屏幕上显示项目。也许我可以找到更好的解决方案,但现在没关系。我无法尝试您的第一个解决方案,因为我没有任何异步事件处理程序可以在其中使用等待行。无论如何,谢谢你的帮助!
    【解决方案2】:

    restorans之前调用,因为构造函数MenuPage在属性初始化之前被调用。

    建议

    public class MenuPage : ContentPage
    {
        ListView listView;
        List<Restoran> restorans;
        async Task<Restoran[]> LoadRestorans()
        {
            return await DataSource.GetRestoransAsync();
        }
    
        public MenuPage(string masa)
        {
            this.restorans = LoadRestorans().GetAwaiter().GetResult().ToList();
            listView = new ListView(ListViewCachingStrategy.RecycleElement)
            {
            ItemsSource = this.restorans,
                ItemTemplate = new DataTemplate(() => {
                    var nativeCell = new CustomCell();
                    return nativeCell;
                })
            };
        }
    }
    

    注意:我没有编译这段代码,但根据我的理解,这应该是正确的代码。

    【讨论】:

    • ItemSource 也接受数组,所以我的建议是使用数组。
    • 不工作,它不能通过“this.restorans =...”这一行。
    • 测试代码正在运行,但在我的情况下它不起作用。正如我之前所说,当我将该行放入MenuPage 方法时,它无法通过该行。我不知道为什么。
    猜你喜欢
    • 1970-01-01
    • 2018-07-09
    • 1970-01-01
    • 1970-01-01
    • 2017-10-04
    • 1970-01-01
    • 1970-01-01
    • 2017-03-27
    • 2018-09-14
    相关资源
    最近更新 更多