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
相关产品推荐
相关产品推荐

