You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Xamarin Forms滚动加载Cosmos DB数据时列表未更新问题

问题描述

我正在学习Xamarin Forms,我的Cosmos DB中存储了大量数据,希望每次滚动ListView时加载14条数据。当前遇到的问题是:从Cosmos DB第二次加载数据时,列表视图无法更新,且代码执行过快导致数据未完成下载。

现有代码

APP初始化代码

int ndxData = 14;
int InitDataNulber = 14;
List<MyCommunityModel> MyListFollowVar;
ObservableRangeCollection<MyCommunityModel> datasource;
void Param()
{
    Device.BeginInvokeOnMainThread(async () =>
    {
        datasource = new ObservableRangeCollection<MyCommunityModel>();
        MyListFollowVar = new List<MyCommunityModel>();
        MyListFollowVar = await OnGetPerson(MyuserGlobalId, 0);

        MyFollowerCollectionList.ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem;
        MyFollowerCollectionList.ItemsSource = datasource = new ObservableRangeCollection<MyCommunityModel>(MyListFollowVar);
        MyFollowerCollectionList.RemainingItemsThreshold = 5;
        MyFollowerCollectionList.RemainingItemsThresholdReached += Followers_RemainingItemsThresholdReached;
    });
}

获取新数据代码

private void Followers_RemainingItemsThresholdReached(object sender, EventArgs e)
{
    MyListFollowVar.AddRange(await OnGetPerson(MyuserGlobalId, ndxData));

    //I would like to wait here for the data to be download 
    // then continue

    Device.BeginInvokeOnMainThread(() =>
    {
        datasource.AddRange(MyListFollowVar.Skip(ndxData).Take(InitDataNulber));
        ndxData += InitDataNulber;
    });
}

数据转换代码

async Task<List<MyCommunityModel>> OnGetPerson(string MyUserid, int NumbrToSkip)
{
    List<MyCommunityModel> TMMPMyListFollowVar = new List<MyCommunityModel>();
    List<UserInteractionModel> UsersInteractionList = await UserInteractionManager.GetFollowedUserInteraction(MyUserid, NumbrToSkip.ToString());
           
    CommunityUser = new MyCommunityModel();
    if (UsersInteractionList != null)
    {
        foreach (var u in UsersInteractionList)
        {
            CommunityUser = new MyCommunityModel();
            CommunityUser.MyId = new Guid(u.UserToFollowId);
            TMMPMyListFollowVar.Add(CommunityUser);
        }
        return TMMPMyListFollowVar;
    }
    return null;
}

Cosmos DB调用代码

public async static Task<List<UserInteractionModel>> GetFollowedUserInteraction(string Userid,string NumberToSkip)
{
    List<UserInteractionModel> UsersInteractionList = new List<UserInteractionModel>();
    string sql = "";
    
    if (NumberToSkip == "0")
    {
        sql = "SELECT top 14 * FROM (SELECT * FROM  u WHERE u.followeruserid='" + Userid + "' and u.complete=false ) as c ORDER BY c.Updated desc";
    }
    else {                
        sql = "SELECT * FROM (SELECT * FROM  u WHERE u.followeruserid='" + Userid + "' and u.complete=false ) as c ORDER BY c.Updated desc   OFFSET " + NumberToSkip + " LIMIT 14";
    }

    QueryDefinition query = new QueryDefinition(sql);

    using (FeedIterator<UserInteractionModel> iterator = container.GetItemQueryIterator<UserInteractionModel>(
     queryDefinition: query,
     requestOptions: new QueryRequestOptions()
     {
         MaxItemCount = -1,
     }))
    {
        while (iterator.HasMoreResults)
        {
            FeedResponse<UserInteractionModel> result = await iterator.ReadNextAsync();
            UsersInteractionList.AddRange(result.Resource);
        }
        return UsersInteractionList;
    }
}
问题分析与修复方案

核心问题1:事件处理未正确等待异步操作

Followers_RemainingItemsThresholdReached未标记为async void,导致内部异步加载操作被当作“火即忘”执行,UI更新代码可能在数据未加载完成时就运行,引发数据未更新的问题。

核心问题2:数据更新逻辑冗余易出错

通过MyListFollowVar中转再截取新数据的方式,容易因索引计算错误导致数据重复或不更新,直接使用新加载的数据集更新数据源更可靠。

核心问题3:SQL存在注入风险

直接拼接用户ID到SQL字符串,存在Cosmos DB SQL注入风险,需改用参数化查询。


修复后的代码

1. 修复滚动加载事件方法

// 标记为async void,确保异步操作能正确等待
private async void Followers_RemainingItemsThresholdReached(object sender, EventArgs e)
{
    // 先解绑事件,防止滚动时重复触发加载
    MyFollowerCollectionList.RemainingItemsThresholdReached -= Followers_RemainingItemsThresholdReached;
    
    try
    {
        // 等待新数据加载完成
        var newItems = await OnGetPerson(MyuserGlobalId, ndxData);
        if (newItems == null || newItems.Count == 0)
        {
            // 无更多数据,关闭自动加载
            MyFollowerCollectionList.RemainingItemsThreshold = -1;
            return;
        }
        
        // 主线程更新数据源
        Device.BeginInvokeOnMainThread(() =>
        {
            datasource.AddRange(newItems);
            ndxData += InitDataNulber;
        });
    }
    finally
    {
        // 重新绑定事件,允许下一次加载
        MyFollowerCollectionList.RemainingItemsThresholdReached += Followers_RemainingItemsThresholdReached;
    }
}

2. 修复Cosmos DB参数化查询

public async static Task<List<UserInteractionModel>> GetFollowedUserInteraction(string Userid, string NumberToSkip)
{
    List<UserInteractionModel> UsersInteractionList = new List<UserInteractionModel>();
    QueryDefinition query;
    
    if (NumberToSkip == "0")
    {
        query = new QueryDefinition(
            "SELECT TOP 14 * FROM (SELECT * FROM u WHERE u.followeruserid = @userId AND u.complete = false) as c ORDER BY c.Updated DESC")
            .WithParameter("@userId", Userid);
    }
    else
    {
        int skipCount = int.Parse(NumberToSkip);
        query = new QueryDefinition(
            "SELECT * FROM (SELECT * FROM u WHERE u.followeruserid = @userId AND u.complete = false) as c ORDER BY c.Updated DESC OFFSET @skip LIMIT 14")
            .WithParameter("@userId", Userid)
            .WithParameter("@skip", skipCount);
    }

    using (FeedIterator<UserInteractionModel> iterator = container.GetItemQueryIterator<UserInteractionModel>(
         queryDefinition: query,
         requestOptions: new QueryRequestOptions()
         {
             MaxItemCount = -1,
         }))
    {
        while (iterator.HasMoreResults)
        {
            FeedResponse<UserInteractionModel> result = await iterator.ReadNextAsync();
            UsersInteractionList.AddRange(result.Resource);
        }
        return UsersInteractionList;
    }
}

3. 简化初始化代码冗余赋值

void Param()
{
    Device.BeginInvokeOnMainThread(async () =>
    {
        MyListFollowVar = await OnGetPerson(MyuserGlobalId, 0);
        datasource = new ObservableRangeCollection<MyCommunityModel>(MyListFollowVar);
        
        MyFollowerCollectionList.ItemSizingStrategy = ItemSizingStrategy.MeasureFirstItem;
        MyFollowerCollectionList.ItemsSource = datasource;
        MyFollowerCollectionList.RemainingItemsThreshold = 5;
        MyFollowerCollectionList.RemainingItemsThresholdReached += Followers_RemainingItemsThresholdReached;
    });
}

额外优化建议

  • 添加加载状态:在加载数据时显示ActivityIndicator,提升用户体验。
  • 处理空数据场景:在初始化和加载时判断OnGetPerson返回值,为空则显示空状态提示。
  • 限制重复触发:事件处理中先解绑再重新绑定,避免快速滚动时多次触发加载。

内容的提问来源于stack exchange,提问作者hugo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 04:17:07