簡單五步驟:以EF Core整合SQLite儲存口罩剩餘數量資訊
前篇,我們展示使用 ASP.NET Core Web API 來快速提供口罩剩餘數量查詢API,但這個展示專案有二個較大缺點,我們持續來改進它。
- 原始資料採用 Unicode 編碼,尤其是中文欄位,這會造成傳輸資料過程的資料量不小。
- 我們只是單純轉拋政府資料開放平臺的口罩剩餘數量的資料,每次請求就重新請求與轉拋,它們的資料更新並不即時。
我們會先處理第二點,這裡我選擇利用 EF Core 整 SQLite 來當暫存資料的儲存點,因為原始資料約 6000 筆上下,這樣的處理量 SQLite 非常合適,而且它的特色是帶著就走且免費,發行部署也相當方便。在未超過更新間隔時前,就由 SQLite 裡去取資料,以減輕資料源的壓力。在處理第二點時,會隨便處理第一點,在取回政府資料開放平臺的口罩剩餘數量資料並儲存前,我們利用 Model 裡的一點小技巧來轉換資料源到英文欄位,並以英文欄位來輸出。
安裝 SQLite
EF Core 直接支援 SQLite。
# PowerShell
Install-Package Microsoft.EntityFrameworkCore.Sqlite -Version 3.1.2
# .NET Core
dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 3.1.2
建立 Model
依照中文欄位名稱,我們簡單翻譯為英文欄位名稱來建立 Model。
public class MedicalMask
{
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string Tel { get; set; }
public int AdultNum { get; set; }
public int KidNum { get; set; }
public DateTime UpdateTime { get; set; }
}
建立 MaskContext
繼承 DbContext
建立 MaskContext
:
public class MaskContext : DbContext
{
public DbSet<MedicalMask> MedicalMasks { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlite("Data Source=MedicalMask.db");
}
先 override OnConfiguring
設定使用 SQLite 並指定資料庫名稱,並設定 DbSet<MedicalMask>
以存取資料表。完成後,將 MaskContext
加入 Startup.ConfigureServices()
裡:
services.AddScoped<MaskContext>();
進行 Migration
開啟 cmd.exe 執行以下指令:
dotnet tool install --global dotnet-ef
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet ef migrations add Initial
dotnet ef database update
如果你未安裝過 dotnet-ef
全域工具,才需要安裝它。之後會產生 Migrations 相關檔案與資料庫在專案根目錄。
改寫 MaskController
public class MaskController : ControllerBase
{
private readonly MaskContext _maskContext;
private readonly MaskService _maskService;
public MaskController(MaskContext maskContext, MaskService maskService)
{
_maskContext = maskContext;
_maskService = maskService;
}
public async Task<IActionResult> Get()
{
async Task InitialDB(MedicalMask medical)
{
if (medical == null)
{
var maskInfos = await _maskService.GetMaskInfo();
foreach (var maskInfo in maskInfos)
{
_maskContext.Add(new MedicalMask()
{
Code = maskInfo.醫事機構代碼,
Name = maskInfo.醫事機構名稱,
Address = maskInfo.醫事機構地址,
Tel = maskInfo.醫事機構電話,
AdultNum = int.Parse(maskInfo.成人口罩剩餘數),
KidNum = int.Parse(maskInfo.兒童口罩剩餘數),
UpdateTime = DateTime.Parse(maskInfo.來源資料時間)
});
}
await _maskContext.SaveChangesAsync();
}
}
async Task MaskDataUpdate()
{
var nextTime = _maskContext.MedicalMasks.First().UpdateTime.AddSeconds(600);
var nowTime = DateTime.Now;
if (nextTime < nowTime)
{
_maskContext.RemoveRange(_maskContext.MedicalMasks);
var maskInfos = await _maskService.GetMaskInfo();
foreach (var maskInfo in maskInfos)
{
_maskContext.Add(new MedicalMask()
{
Code = maskInfo.醫事機構代碼,
Name = maskInfo.醫事機構名稱,
Address = maskInfo.醫事機構地址,
Tel = maskInfo.醫事機構電話,
AdultNum = int.Parse(maskInfo.成人口罩剩餘數),
KidNum = int.Parse(maskInfo.兒童口罩剩餘數),
UpdateTime = DateTime.Parse(maskInfo.來源資料時間)
});
}
await _maskContext.SaveChangesAsync();
}
}
try
{
var medical = _maskContext.MedicalMasks.FirstOrDefault();
await InitialDB(medical);
await MaskDataUpdate();
return Ok(_maskContext.MedicalMasks.AsNoTracking());
}
catch (HttpRequestException ex)
{
return Problem(ex.Message);
}
}
}
這一次我們加上 MaskContext
。流程上有二段,第一次 Migration 與 Database Update 之後,資料庫為空,我們必須執行 InitialDb
來進行第一次的請求與寫入資料庫,為了不過度複雜範例,就不採用 SeedData 方式。第二次,MaskDataUpdate
會先判斷時間,確認是否需要更新資料庫,Model 裡的 Id
只是為了 EF Core 的 Migration 加上去的,對我們而言沒有用。所以我偷懶將整個表格刪除再全部重塞一次。
另外測試過程發現,政府資料開放平臺的口罩剩餘數量的資料並未如網頁上所寫 30 秒更新一次。像中午或晚上時段未更新資料的情況。我們修正一下判斷式:
if (updateData)
{
var maskInfos = await _maskService.GetMaskInfo();
var sourceTime = DateTime.Parse(maskInfos.First().來源資料時間);
// 0 is same as value
// Ref: https://docs.microsoft.com/en-us/dotnet/api/system.datetime.compareto?view=netcore-3.1#remarks
if (sourceTime.CompareTo(updateTime) != 0)
{
_maskContext.RemoveRange(_maskContext.MedicalMasks);
foreach (var maskInfo in maskInfos)
{
_maskContext.Add(new MedicalMask()
{
Code = maskInfo.醫事機構代碼,
Name = maskInfo.醫事機構名稱,
Address = maskInfo.醫事機構地址,
Tel = maskInfo.醫事機構電話,
AdultNum = int.Parse(maskInfo.成人口罩剩餘數),
KidNum = int.Parse(maskInfo.兒童口罩剩餘數),
UpdateTime = DateTime.Parse(maskInfo.來源資料時間)
});
}
await _maskContext.SaveChangesAsync();
}
}
做為展示,設定 10 鐘會更新一次,但更新前再確認一下資料來源的時間是否有異動,有才進行刪除與重塞作業。
小結
- 安裝 SQLite
- 建立 Model
- 建立 DbContext
- 進行 Migration
- 改寫 MaskController
簡單五步驟,我們就能把 SQLite 資料庫整合進來,並且整合 EF Core 來進行存取操作。
沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。