Hello, 各位 iT 邦幫忙 的粉絲們大家好~~~
在本系列文會利用目前 Visual Studio 內建的專案樣本建立一個初始的 .NET MAUI 專案,並且透過此 .NET MAUI 專案來把 TopStore App 的開發從 Xamairn.Forms 轉換到 .NET MAUI 上進行。
本篇是 Re: 從零改成用 .NET MAUI 技術來繼續過去用 Xamarin 開發的一個 App : TopStore 系列 系列文的 EP12。
回到 .NET MAUI 先來談談 .NET MAUI 的跟過去 Xamarin.Forms 在設計上的重大改變 DependencyInjection (依賴注入,簡稱 DI) :
https://learn.microsoft.com/dotnet/architecture/maui/dependency-injection
約莫在 .NET 5 推出之後,其類別庫的設計中就帶入了許多 "Dependency Injection Containers" 的運用,使得多平台應用程式可以使用 Microsoft.Extensions.DependencyInjection
來管理應用程式中 View、ViewModel 和 Service 的各式物件實體產生。
Microsoft.Extensions.DependencyInjection
在對於應用程式的耦合性降低的處理上有很大的幫助,並提供在 "Dependency Injection Containers" 中多數常見的所有功能,其中包括註冊類型對應和物件實體建立的方法、解析物件、管理物件存活週期,以及將相依物件注入其解析的物件建構函式中。
而在 .NET MAUI 中也透過類似的手法,透過類別 MauiProgram
呼叫方法 CreateMauiApp()
建立 MauiAppBuilder
物件。而 MauiAppBuilder
則是設計了一個前述提到的具備 "Dependency Injection Containers" 類型的 IServiceCollection
屬性,可提供註冊 View、ViewModel 和 Service...等程式元件進行 "依賴注入 (DI)" 的註冊。
在最後呼叫 MauiAppBuilder.Build()
方法,任何有向 IServiceCollection
屬性註冊的 View、ViewModel 和 Service...等程式元件,都會提供給這個 "Dependency Injection Containers"。
更多可詳細的 Microsoft.Extensions.DependencyInjection 資訊,請參考 .NET Core 的依賴注入 (DI)
:
https://learn.microsoft.com/dotnet/core/extensions/dependency-injection
那就進到 TopStoreApp 的專案程式當中來改變看看吧!
首先,先調整在 TopStoreApp 當中所規劃的 "資料來源" 設計,在這個 App 當中目前有兩種資料來源,其一是透過程式碼寫死的 "聯絡人" 透過 MockData 進行處理;其二是透過 SQLite 紀錄的 "聯絡人" 透過 DbService 進行處理。
當初在設計時,為了可以讓 MockData 與 DbService 在 App 之間能互相切換,規劃了一個 IDataServcie
的 interface ,來對 "資料來源" 的 MockData 與 DbService 進行解耦的設計。
於是讓 MockData 與 DbService 透過實作 IDataService
這個 interface 之外,也讓 MockData 與 DbService 這兩個類別都具有 Singleton 的設計:
由於 .NET MAUI 已經能使用 Microsoft.Extensions.DependencyInjection
的類別庫設計,所以要來把當初的 Singleton 的設計移除(在這邊用註解表示,即下圖的藍色框),並且將其建構式的封裝描述詞變更為 public
:
接著打開 App.xaml.cs:
在 App.xaml.cs 當中的原建構方法,當中的程式碼刪除(在這邊用註解表示,即下圖的藍色框),以及封裝描述詞變更為 private
,並且另外設計一個建構方法與把原本的 DataService 欄位封裝描述詞變更為 public
,也把原本欄位設計改成屬性設計(僅開放公開取得):
接著連帶的 IDataService 的介面設計封裝也要變更為 public
:
最後,就是調整今天的重頭戲 MauiProgram.cs
:
在原本的 CreateMauiApp()
方法底下增加如下程式碼:
private static MauiAppBuilder RegisterAppDataService(this MauiAppBuilder builder)
{
var isDbService = Preferences.Get("UserSwitchToDataService", false);
if (isDbService)
builder.Services.AddSingleton<Services.IDataService, Services.DbService>();
else
builder.Services.AddSingleton<Services.IDataService, Utilities.MockData>();
return builder;
}
並且回到原本的 CreateMauiApp()
當中在 .ConfigureFonts()
之後增加其呼叫,如增加完成後下圖紅框所示:
其實這裡並不陌生,先前在切換 MockData 與 DbService 的使用是透過 Preferences
的紀錄,所以在這邊也是透過此紀錄判定要讓 MockData 建立物件實體加入註冊還是 DbService 建立物件實體加入註冊。
下面就使用偵錯模式搭配幾個重要的中斷點來觀察一下整體的執行效果:
首先透過 "設定" 的 UI 調整確定是使用 "MockData":
接著偵錯執行此 TopStoreApp 後,觀察在 MauiProgram.cs
的執行中斷,確實執行了 MockData 的註冊運作:
再到 App.xaml.cs
的建構方法確認傳入的 dataService 是否為 MockData
物件:
執行起來為 MockData 的聯絡人列表:
這次透過 "設定" 的 UI 調整確定是使用 "DbService":
接著偵錯執行此 TopStoreApp 後,觀察在 MauiProgram.cs
的執行中斷,確實執行了 DbService 的註冊運作:
再到 App.xaml.cs
的建構方法確認傳入的 dataService 是否為 DbService
物件:
執行起來為 DbService 的聯絡人列表:
以上就保持了之前透過 IDataService 的 interface 設計來解耦其資料來源的依賴性的彈性設計,並能順利的在 App 當中切換 MockData 與 DbService 資料的運用。
更重要的是也把 Maui 加入的設計 "Dependency Injection" 撰寫到 TopStoreApp 當中,讓程式的撰寫上更具便利性與維護性(也省去要自己撰寫 Singleton 的處理)。
本 EP 介紹所完成的範例程式碼可在此下載。