昨天我們學會了如何從 UI 呼叫 API,把股票清單顯示在 DataGrid 上。
今天要解決一個新問題:離線狀態要怎麼用?
解法是:下載的資料存進 LiteDB,下次啟動時就能先讀取本地資料。
LiteDB 是一個輕量的 NoSQL 本地資料庫,只會生成一個 .db
檔案,非常適合桌面應用程式。
為了保持彈性,我們先定義一個資料存取介面 IStockRepository
,負責存股票清單。
// Repositories/IStockRepository.cs
using System.Collections.Generic;
public interface IStockRepository
{
void SaveStocks(IEnumerable<StockProfile> stocks);
IEnumerable<StockProfile> LoadStocks();
}
// Repositories/LiteDbStockRepository.cs
using LiteDB;
using System;
using System.Collections.Generic;
public class LiteDbStockRepository : IStockRepository
{
private readonly string _dbPath;
public LiteDbStockRepository(string dbPath = "StockData.db")
{
_dbPath = dbPath;
}
public void SaveStocks(IEnumerable<StockProfile> stocks)
{
using var db = new LiteDatabase(_dbPath);
var col = db.GetCollection<StockProfile>("stocks");
col.DeleteAll(); // 先清空,避免重複
col.InsertBulk(stocks); // 批次寫入
}
public IEnumerable<StockProfile> LoadStocks()
{
using var db = new LiteDatabase(_dbPath);
var col = db.GetCollection<StockProfile>("stocks");
return col.FindAll();
}
}
LoadStocksCommand
:呼叫 API → 存 LiteDB → 更新 UILoadFromLocalCommand
:從本地 LiteDB 載入// ViewModels/StockListViewModel.cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Input;
public class StockListViewModel : INotifyPropertyChanged
{
private readonly IStockApiService _api;
private readonly IStockRepository _repo;
public ObservableCollection<StockProfile> Stocks { get; } = new ObservableCollection<StockProfile>();
public ICommand LoadStocksCommand { get; }
public ICommand LoadFromLocalCommand { get; }
public StockListViewModel(IStockApiService api, IStockRepository repo)
{
_api = api;
_repo = repo;
LoadStocksCommand = new AsyncCommand(LoadStocksAsync);
LoadFromLocalCommand = new RelayCommand(_ => LoadFromLocal());
}
private async Task LoadStocksAsync()
{
var list = await _api.GetStocksAsync();
Stocks.Clear();
foreach (var s in list) Stocks.Add(s);
_repo.SaveStocks(list); // 存入本地資料庫
}
private void LoadFromLocal()
{
var list = _repo.LoadStocks();
Stocks.Clear();
foreach (var s in list) Stocks.Add(s);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
<StackPanel Orientation="Horizontal" Margin="8" Spacing="8">
<Button Content="下載股票 (API)"
Command="{Binding LoadStocksCommand}" Width="150"/>
<Button Content="載入本地資料"
Command="{Binding LoadFromLocalCommand}" Width="150"/>
</StackPanel>
<DataGrid ItemsSource="{Binding Stocks}" AutoGenerateColumns="False" Margin="8">
<DataGrid.Columns>
<DataGridTextColumn Header="代號" Binding="{Binding Code}" Width="100"/>
<DataGridTextColumn Header="名稱" Binding="{Binding Name}" Width="200"/>
<DataGridTextColumn Header="產業" Binding="{Binding Industry}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
// MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var http = new HttpClient();
var api = new TwseStockApiService(http);
var repo = new LiteDbStockRepository("StockData.db");
this.DataContext = new StockListViewModel(api, repo);
}
}
StockData.db
載入股票清單今天我們學會了: