XAML in Xamarin.Forms 基礎篇 電子書

特別說明

2018/04/19

Entity Framework Core + SQLite 在 Xamarin .NET Standard 類別庫上的應用說明 (含 SAP)

當我們在進行跨平台 Cross-Platform 行動裝置應用程式開發的時候,一定會用到這樣的技巧,那就是如何將應用程式中的執行狀態,永久保存下來。例如,當使用者在登入頁面勾選了 記住我的登入帳號 這個選項,當下次使用者要重新進行登入作業的時候,我們需要把上次登入成功的帳號,顯示在螢幕上的帳號欄位中。
我個人在進行這類需求處理的時候,都是透過檔案的方式來進行設計,過程將是把 .NET 環境中要永久保存的物件進行 JSON 序列化處理,如此,這些 .NET 物件,就會變成 JSON 文字內容,接著,我再把這些文字內容寫入到檔案中;當要取得這些永久保存紀錄值的時候,我就會把該檔案的文字內容全部都讀取出來,接著,針對這些文字內容進行 JSON 反序列化處理(通常都會使用強型別的方式),如此,當初儲存起來的 .NET 物件就會重新恢復在 .NET 執行環境中了。
根據以往幫學員上課的經驗,很多人都習慣於使用資料庫(例如 SQLite)方式來進行永久性的儲存選擇對象,現在,有個好消息,那就是當您建立起一個新的 Xamarin.Forms 專案,共用的類別庫專案類型將會是 SCL (.NET Standard Class Library 標準類別庫),所以,我們就可以在 SCL 中使用 Entity Framework Core 框架,來幫助我們使用 ORM 方式,進行資料庫存取動作,這樣,我們就不再需要自己來撰寫 SQL 語言了。
在這個練習中,我們將會建立一個 Xamarin.Forms for Prism 的專案,並且加入 Entity Framework Core for SQLite 所需要的 NuGet 套件與相關程式碼,讓您體驗如何在 Xamarin.Forms 專案中,可以享受到如同 ASP.NET Core 專案中相關的 Entity Framework 使用方式;最後,您可以透過 Android 裝置監視器來查看我們所建立的 SQLite 資料庫檔案是否就是您所需要的需求。
更多關於 Entity Framework 介紹,可以參考 Entity Framework Core 快速概觀 文章。
這篇文章的專案原始碼可以從 https://github.com/vulcanlee/xamarin-forms-sample2018/tree/master/XFEFCore 取得

建立一個 Xamarin Forms for Prism 專案

  • 首先,我們打開 Visual Studio 2017 (任何一種版本 Visual Studio Community 2017 / Visual Studio Professional 2017 / Visual Studio Enterprise 2017皆可)
  • 點選 Visual Studio 2017 功能表的 [檔案] > [新增] > [專案]
  • 當出現 [新增專案] 對話窗,請依序選擇 [已安裝] > [Visual C#] > [Prism] > [Prism Blank App (Xamarin.Forms)]
  • 請在最下方 [名稱] 文字輸入盒欄位,輸入您想要的專案名稱
  • 最後,請點選 [確定] 按鈕
Visual Studio 2017 Xamarin Forms Prism new project
  • 現在,Visual Studio 2017 將會顯示 [PRISM PROJECT WIZARD] 這個對話窗,您可以在這個對話窗中,選擇需要跨平台的目標,在這裡,您將會有三種選擇: ANDROID, iOS, UWP,請依照您的需求,勾選需要建立的專案類型。接著,請在下方 [Container] 下拉選單中,選擇 Unity 這個選項;最後,請點選 [CREATE PROJECT] 這個按鈕,請 Prism Template Pack 所提供的樣板精靈,幫助我們產生出可用於 Prism 框架的 Xamarin.Forms 方案與專案。
PRISM PROJECT WIZARD
  • 此時,Visual Studio 2017 的 Prism Template Pack 將會開始幫您建立起四個專案(假設我們在上個步驟,勾選的所有平台目標),分別是:
    • Xamarin.Android 原生專案
    • Xamarin.iOS 原生專案
    • Windows UWP 原生專案
    • .NET Standard Class Library SCL 共用類別庫專案
  • 現在,我們可以從 Visual Studio 2017 的方案總管中,看到已經產生了四個專案,如下圖所示,其中,當我們使用 Xamarin.Forms 進行專案開發的時候,原則上大多只會在 SCL 專案上進行程式碼與 XAML 的設計,也就是 .NET Standard Class Library 類別庫內進行程式開發。在 SCL 專案中,我們看到建立了兩個資料夾 [ViewModels] & [Views],我們日後將會在這兩個資料夾來建立頁面的 XAML 標記宣告 (在 Views 資料夾)與每個頁面的商業邏輯(在 ViewModels 資料夾)。
Xamarin.Forms Solution and Projects

安裝使用 Entity Framework + SQLite 資料庫所需用到的 NuGet 套件

  • 請滑鼠右擊方案節點,選擇 [管理方案的 NuGet 套件]
  • 點選 瀏覽 標籤頁次,並在搜尋文字輸入盒中輸入 Microsoft.EntityFrameworkCore.Sqlite
  • 當出現搜尋結果之後,請勾選所有的專案檢查盒,將這個 Microsoft.EntityFrameworkCore.Sqlite NuGet 套件,安裝到所有的專案中
Microsoft.EntityFrameworkCore.Sqlite
預覽變更
接受授權

開始撰寫資料庫存取的程式碼

  • 滑鼠右擊 SCL 專案,選擇 [加入] > [類別]
  • 在 [新增項目 XFEFCore] 對話窗,在最下方了名稱欄位中,輸入 Blog
  • 之後,請點選 [新增] 按鈕
新增項目
  • 使用底下的程式碼,替換掉剛剛產生的檔案內容
C# CSharp
using System.ComponentModel.DataAnnotations;

namespace XFEFCore
{
    public class Blog
    {
        [Key]
        public int BlogId { get; set; }
        public string Url { get; set; }
        public int Rating { get; set; }
    }
}
  • 滑鼠右擊 SCL 專案,選擇 [加入] > [類別]
  • 在 [新增項目 XFEFCore] 對話窗,在最下方了名稱欄位中,輸入 SCLHelper
  • 之後,請點選 [新增] 按鈕
新增項目
  • 使用底下的程式碼,替換掉剛剛產生的檔案內容
C# CSharp
namespace XFEFCore
{
    public class SCLHelper
    {
        public static string DBPath { get; set; } = "My.db";
    }
}
  • 滑鼠右擊 SCL 專案,選擇 [加入] > [類別]
  • 在 [新增項目 XFEFCore] 對話窗,在最下方了名稱欄位中,輸入 Blog
  • 之後,請點選 [新增] 按鈕
新增項目
  • 使用底下的程式碼,替換掉剛剛產生的檔案內容
C# CSharp
using Microsoft.EntityFrameworkCore;

namespace XFEFCore
{
    public class DatabaseContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        private string _databasePath = SCLHelper.DBPath;

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite($"Filename={_databasePath}");
        }
    }
}
  • 在 SCL 專案內的 ViewModels 資料夾中,打開 [MainPageViewModel.cs] 檔案
  • 使用底下的程式碼,替換掉剛剛打開的檔案內容
C# CSharp
using Prism.Navigation;
using System.Linq;
using Microsoft.EntityFrameworkCore;


namespace XFEFCore.ViewModels
{
    public class MainPageViewModel : ViewModelBase
    {
        public MainPageViewModel(INavigationService navigationService) 
            : base (navigationService)
        {
            Title = "Main Page";
        }

        public async override void OnNavigatedTo(NavigationParameters parameters)
        {
            using (var db = new DatabaseContext())
            {
                db.Database.Migrate();

                if((await db.Blogs.CountAsync()) == 0)
                {
                    db.Add(new Blog() { Rating = 5, Url = "https://mylabtw.blogspot.com/" });
                    db.SaveChanges();
                }

                Title = db.Blogs.ToList().FirstOrDefault().Url;
            }
        }
    }
}
  • 請在 Android 原生專案中找到 [MainActivity.cs] 檔案,並且打開它
  • 請找到 base.OnCreate(bundle); 敘述,在該行敘述下,加入底下這行程式碼
C# CSharp
SCLHelper.DBPath = System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "My.db");

執行與測試

  • 設定 Android 專案為預設起始專案
  • 請重建 Android 原生專案
  • 若沒有問題,請執行這個 Android 專案
  • 雖然您可以正常建置這個 Android 專案,可是,您會發現到,當你一執行這個專案,就會立即閃退
  • 我們可以從 Visual Studio 輸出視窗中,看到底下錯誤訊息
  • 其中,無法載入這個 [System.Runtime.CompilerServices.Unsafe] 組件,應該是最主要的問題。另外,您也可以展開 Android 專案的 bin\Debug 資料夾來查看。
方案總管
F/monodroid-assembly( 1537): Could not load assembly 'System.Runtime.CompilerServices.Unsafe' during startup registration.
F/monodroid-assembly( 1537): This might be due to an invalid debug installation.
F/monodroid-assembly( 1537): A common cause is to 'adb install' the app directly instead of doing from the IDE.
  • 請滑鼠右擊 Android 原生專案的 參考 節點,接著選擇 [管理 NuGet 套件]
  • 點選 瀏覽 標籤頁次,在搜尋文字輸入盒中,輸入 System.Runtime.CompilerServices.Unsafe
  • 請記得要選擇安裝 4.3.0 版本的套件
  • 完成後,請清除所有的專案,並且重建 Android 原生專案
  • 現在,可以執行這個 Android 專案,此時,您將發現到剛剛的問題將不再出現,不過,現在又出現另外一個錯誤訊息(您可以從 Visual Studio 的輸出視窗中看到)
Microsoft.Data.Sqlite.SqliteException: SQLite Error 1: 'no such table: Blogs'
  • 這個問題將會是執行到 MainPageViewModel 中的 OnNavigatedTo 事件內
OnNavigatedTo

解決 Entity Framework Core Migration 的問題

  • 會發生這樣的問題,那是因為我們還沒有啟用 EF Core 資料庫轉移設定,更多這方面的資訊,可以參考 移轉 - EF Core 與 ASP.NET Core MVC 教學課程 (4/10)
  • 因此,我們需要使用 Entity Framework Core 工具,不過,因為 Entity Framework Core 工具 支援目標設為 .NET Framework 或 .NET Core 的專案,我們並無法從 .NET Standard 類別庫專案中直接來進行,更多這方面的說明,請參考 Entity Framework Core 工具
  • 所以,滑鼠右擊方案節點,選擇 [加入] > [新增專案]
  • 當出現新增專案對話窗,請在下方名稱輸入 DummyMigrationsProject,之後,點選 [確定] 按鈕
新增專案 .NET Core
  • 滑鼠右擊 DummyMigrationsProject 專案節點,選擇 [編輯 DummyMigrationsProject.csproj]
  • 將底下內容複製到這個檔案內,並且點選儲存
csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.2" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.2" />
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.2" />
    <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.2" />
  </ItemGroup>
</Project>
.NET Core
  • 請重新建立剛剛建立好的 .NET Core 專案
  • 開啟一個命令提示字元視窗,切換到這個 .NET Core 專案目錄下,接者輸入底下指令
dotnet restore
dotnet ef migrations add InitialCreate
dotnet ef migrations
  • 若執行上述命令之後,看到如同上面螢幕截圖內容,那就表示您執行成功了
  • 現在,您將會看到 .NET Core 專案中出現了一個 [Migrations] 資料夾
Migrations
  • 最後,我們可以將 [Migrations] 資料夾,複製到 SCL 專案內
  • 我們可以重新執行該專案,現在,您將會看到這個專案可以正確執行,結果如下圖所示;這也代表了我們已經可以透過 Entity Framework 來進行 SQLite 資料庫的存取。
Visual Studio Android Emulator Entity Framework Core for SQLite
  • 而我們的 SQLite 資料庫檔案是否真的有建立在 Android 裝置或模擬器上呢?
  • 點選 Visual Studio 2017 功能表中的 [工具] > [Android] > [Android 裝置監視器]
  • 接著,我們從 Android 專案屬性中,找到 Android 資訊清單 標籤頁次,請找出 套件名稱 的名稱,此時,該名稱為 com.companyname.appname
Android 資訊清單
  • 請在 Android 裝置監視器中的 /Data/Data 目錄下,找到 com.companyname.appname 這個目錄,接著,展開到 files 目錄,此時,您就可以看到我們這個專案的 SQLite Database 資料庫檔案;您可以透過 Android 裝置監視器把這個 SQLite Database 檔案從裝置中複製出來,驗證該資料庫的結構。
Android Device Monitor

進階延伸改良

  • 由於在現在這個時間點,我們並沒有辦法直接在 .NET Standard Class Library 類別庫內來進行 Entity Framework Core 的資料庫自動轉移工作,所以,需要採用上述的作法,透一個 .NET Core Console 專案,並且把相關的 dbContext 的類別從 SCL 複製到 .NET Core 專案內,這樣的做法似乎又有點繁瑣,現在,我們可以進行改良的這樣的做法。
  • 現在,請建立一個 [共用專案]
    (更多關於這方面的資訊,可以參考 共用的專案)
新建 共用專案
  • 建立完成之後,請修正 SCL 專案與 .NET Core 專案,都要參考這個 SAP 共用專案
  • 接著,可以參考下圖,把 dbContext 相關的檔案,拖拉到 SAP 共用專案內,此時,原先的 SCL 與 .NET Core 專案內的這些檔案,就可以刪除了。
  • 不過,對於資料夾 [Migrations] 請不要搬移到 SAP 共用專案內
新建 共用專案
  • 現在,我們 Blog 類別中,加入一個新的欄位,如下程式碼:
C# CSharp
using System.ComponentModel.DataAnnotations;

namespace XFEFCore
{
    public class Blog
    {
        [Key]
        public int BlogId { get; set; }
        public string Url { get; set; }
        public int Rating { get; set; }
        public int Total { get; set; }
    }
}
  • 同樣的,我們需要在命令提示字元視窗中(需要切換到 .NET Core 專案的根目錄下),輸入底下命令
dotnet ef migrations add NewBlogTotalColumn
  • 此時,您將會看到 .NET Core 專案內的資料夾 [Migrations] 多了兩個檔案,請將 .NET Core 專案內的資料夾 [Migrations],複製到 SCL 專案內。
  • 現在,讓我們來修改程式碼,請打開 SCL 專案內的 [ViewModels] > [MainPageViewModel.cs]檔案。
  • 找到 OnNavigatedTo 事件委派方法,修正成為如下程式碼
    在這裡,只要我們每次執行與顯示這個頁面,其 Total 數量就會加一,我們可以從該頁面的導航工具列中,看到 Title 的最後內容。
C# CSharp
public async override void OnNavigatedTo(NavigationParameters parameters)
{
    using (var db = new DatabaseContext())
    {
        db.Database.Migrate();

        if((await db.Blogs.CountAsync()) == 0)
        {
            db.Add(new Blog() { Rating = 5, Url = "https://mylabtw.blogspot.com/" });
            db.SaveChanges();
        }
        else
        {
            var item = db.Blogs.ToList().FirstOrDefault();
            item.Total += 1;
            db.SaveChanges();
        }

        Title = $"({db.Blogs.ToList().FirstOrDefault().Total})"+db.Blogs.ToList().FirstOrDefault().Url;
    }
}
  • 現在,我們每次進行資料庫轉移動作的時候,只需要直接下達指令,完成後,複製 資料夾 [Migrations] 回到 SCL 專案內即可。
  • 好,請執行這個專案多次,此時,您會看到如下圖, Total 的值會不斷的增加,在此,也可以證明,我們確實有將資料寫到手機內進行永久性的儲存,而且是儲存在 SQLite 資料庫內。
新建 共用專案



沒有留言:

張貼留言