AppShell多页面复用ViewModel时数据缓存逻辑异常问题求助
Let's break down exactly what's going wrong here and walk through the fixes:
Root Cause
The core issue isn't with how you're instantiating ViewModels (each page having its own UpdatesViewModel instance is actually a perfectly valid approach here) — it's with your DataStore's caching logic combined with DependencyService's default singleton behavior.
Here's the play-by-play:
- DependencyService returns a single instance of your DataStore by default. So both your
NewsPageandNotificationsPageViewModels are using the same DataStore object. - When
NewsPageloads, it callsGetItemsAsync("news"), which populates the DataStore's globalitemsvariable with news data. - When you switch to
NotificationsPage, its ViewModel callsGetItemsAsync("notifications"), but your cache checkforceRefresh || items == null || items.Count == 0fails:itemsalready has data (from the news load), so it skips fetching new data and returns the existing news list instead.
Your cache doesn't distinguish between different updateType values — it's using one single variable to store all types of updates, which is why you're seeing cross-page data leakage.
Solution 1: Update DataStore to Cache by Update Type
The cleanest fix is to modify your DataStore to cache data separately for each updateType. Use a dictionary to map each type to its corresponding data list:
// Replace your single 'items' variable with a dictionary private Dictionary<string, List<UpdatesModel>> _cachedItems = new Dictionary<string, List<UpdatesModel>>(); public async Task<IEnumerable<UpdatesModel>> GetItemsAsync(string updateType, bool forceRefresh = false) { // Check if we need to refresh or if there's no cached data for this type if (forceRefresh || !_cachedItems.ContainsKey(updateType) || _cachedItems[updateType] == null || _cachedItems[updateType].Count == 0) { Uri uri = new Uri(Constants.GetUpdatesUrl(updateType)); try { HttpResponseMessage response = await client.GetAsync(uri); if (response.IsSuccessStatusCode) { string content = await response.Content.ReadAsStringAsync(); var fetchedItems = (List<UpdatesModel>)JsonConvert.DeserializeObject<IEnumerable<UpdatesModel>>(content); // Update the cache for this specific update type if (_cachedItems.ContainsKey(updateType)) _cachedItems[updateType] = fetchedItems; else _cachedItems.Add(updateType, fetchedItems); } } catch (Exception ex) { Debug.WriteLine(@"\tERROR {0}", ex.Message); } } // Return the cached data for the requested type (or null if none exists) return await Task.FromResult(_cachedItems.TryGetValue(updateType, out var result) ? result : null); }
Solution 2: Force Refresh When Switching Tabs (Quick Fix)
If you want a quicker temporary fix without modifying the DataStore's caching structure, you can modify your ExecuteLoadItemsCommand to pass forceRefresh=true when fetching data. This skips the cache check every time the command runs:
async Task ExecuteLoadItemsCommand() { IsBusy = true; try { Items.Clear(); // Pass forceRefresh=true to bypass cache var items = await DataStore.GetItemsAsync(UpdateType, forceRefresh: true); if(items!=null) foreach (var item in items) { Items.Add(item); } } catch (Exception ex) { Debug.WriteLine(ex); } finally { IsBusy = false; } }
Note: This will hit your REST API every time you switch tabs, which negates your caching benefit — so Solution 1 is far better for production use.
Final Note on ViewModel Usage
To answer your original question: Instantiating a separate ViewModel for each page is not a bad practice here. Since each tab represents a distinct data set with its own state (like IsBusy), having independent ViewModels keeps your code clean and maintainable. The issue was purely with the DataStore's untyped caching, not your ViewModel structure.
内容的提问来源于stack exchange,提问作者M.Waseem

