2021年5月8日 星期六

Blazor 使用 Parameter 資料綁定方式的觸發時機

Blazor 使用 Parameter 資料綁定方式的觸發時機

當在進行 Blazor 專案程式設計的時候,會將一個頁面切割成為不同的 Blazor 元件,接著便可以透過 Blazor 的 使用元件參數進行系結 功能,將 A 元件內的物件值,透過安樹綁定的方式傳遞到元件 B 內。

不過,最近許多夥伴和我反映,他們設計了一個這樣的情境,在一個頁面元件上,使用者可以輸入相關的資料,一旦輸入完成之後,會按下送出按鈕,此時會呼叫 Syncfusion 的 DataGrid 這個元件的 Refresh 方法,請求 DataGrid 這個元件,依據剛剛輸入的內容,重新透過 Adapter 這個元件,從資料庫內抓取符合設定條件的紀錄出來,並且顯示在螢幕上。

這樣看是十分簡單設計需求,卻面臨到了一個十分詭異的現象,那就是當第一次點選送出按鈕之後,無法使用剛剛設定的內容來進行資料庫的查詢,進而取得符合的紀錄,而是需要按次點選一次該送出按鈕,此時才會取得所設定內容的相關紀錄。

通常,我都是鼓勵大家,若遇到這樣詭異的問題,並且需要請求他人的協助的時候,要能夠使用一個簡單的專案、描述如何做出這樣的專案,並且可以在這個執行中的專案上,進行甚麼樣的操作,以便可以重現每次都會遇到的問題。

若沒有辦法做到這樣的自我技能能力提升的情況,將會造成要幫你查看問題的人,很難在第一時間內找出問題,因為,他需要使用你開發的專案,在自己的電腦上跑起來這個專案(若沒有相關指引或者方法來說明如何建立起資料庫或者相關設定步驟,想要幫你解決問題的人,在第一時間內,是無法幫你解決問題的),了解你專案上的商業邏輯,看懂你寫的程式碼(當然,若你的程式碼寫得十分混亂,或者使用很多非常規的寫法,就需要花費其他時間再來解決這些額外問題),才有可能開始幫你看看到底是哪裡出了問題。

面對這個問題,我設計了一個簡單的專案,設計一個新的元件,該元件會接收一個可以綁定的參數與一個方法。當透過參數綁定成功之後,會將當時使用的執行緒ID 與訊息顯示在螢幕上,而當在其他元件要呼叫該元件的方法時候,也會顯示當時使用的執行緒ID 與訊息顯示在螢幕上

現在來看看如何做出這樣的範例成程式碼。

這篇文章的原始碼位於 bzParameterBinding

建立Blazor Server 應用程式專案

  • 開啟 Visual Studio 2019
  • 選擇右下方的 [建立新的專案] 按鈕
  • 在 [建立新專案] 對話窗中
  • 從右上方的專案類型下拉按鈕中,找到並選擇 [Web]
  • 從可用專案範本清單內,找到並選擇 [空的 ASP.NET Core]
  • 點選左下方 [下一步] 按鈕
  • 在 [設定新的專案] 對話窗中
  • 在 [專案名稱] 欄位中輸入 bzParameterBinding
  • 點選左下方 [下一步] 按鈕
  • 在 [其他資訊] 對話窗中
  • 在 [目標 Framework] 下拉選單中,選擇 [.NET 5.0 (目前)]
  • 點選左下方 [建立] 按鈕

建立一個新的元件

  • 滑鼠右擊 [Pages] 資料夾
  • 選擇 [加入] > [Razor 元件]
  • 在 [新增項目 - bzParameterBinding] 對話窗內的 [名稱] 欄位,輸入 Component.razor
  • 點選該對話窗右下方的 [新增] 按鈕
  • 使用底下的內容替換原先檔案內容
<h3>Component</h3>
<h4>@MyProperty</h4>

@code {
    private int myVar;
    [Parameter]
    public int MyProperty
    {
        get { return myVar; }
        set
        {
            myVar = value;
            Console.WriteLine($"設定參數綁定 ({System.Threading.Thread.CurrentThread.ManagedThreadId})");
        }
    }
    protected override void OnParametersSet()
    {
        Console.WriteLine($"OnParametersSet ({System.Threading.Thread.CurrentThread.ManagedThreadId})");
    }
    public void ShowSomething()
    {
        Console.WriteLine($"呼叫方法 [{MyProperty}] ({System.Threading.Thread.CurrentThread.ManagedThreadId})");
    }
}

修正 Index.razor 元件

  • 打開 [Pages] 資料夾內的 [Index.razor] 檔案
  • 將該類別設計為底下內容
  • 使用底下的內容替換原先檔案內容
@page "/"

<h1>Hello, Parameter Binding</h1>

<br />
<button class="btn btn-primary"
        @onclick="btnClick">
    加一
</button>
<br />
<Component @ref="component" MyProperty="counter" />

<SurveyPrompt Title="How is Blazor working for you?" />

@code{
    int counter = 0;
    Component component;
    async Task btnClick()
    {
        #region 使用同步方式來執行
        //Console.WriteLine();
        //Console.WriteLine($"觸發按鈕事件 ({System.Threading.Thread.CurrentThread.ManagedThreadId})");
        //counter++;
        //Console.WriteLine($"計數器 + 1 ({System.Threading.Thread.CurrentThread.ManagedThreadId})");
        //component.ShowSomething();
        #endregion

        #region 呼叫元件方法前,使用 await 
        //Console.WriteLine();
        //Console.WriteLine($"觸發按鈕事件 ({System.Threading.Thread.CurrentThread.ManagedThreadId})");
        //counter++;
        //Console.WriteLine($"計數器 + 1 ({System.Threading.Thread.CurrentThread.ManagedThreadId})");
        //await Task.Yield();
        //await Task.Delay(100);
        //component.ShowSomething();
        #endregion

        #region 將呼叫元件方法改成非同步方式呼叫
        //Console.WriteLine();
        //Console.WriteLine($"觸發按鈕事件 ({System.Threading.Thread.CurrentThread.ManagedThreadId})");
        //counter++;
        //Console.WriteLine($"計數器 + 1 ({System.Threading.Thread.CurrentThread.ManagedThreadId})");
        //var myTask = System.Threading.Tasks.Task.Run(() =>
        //{
        //    System.Threading.Thread.Sleep(100);
        //    component.ShowSomething();
        //});
        #endregion
    }

}

執行並且測試

  • 首先,在 [Index.razor] 檔案中,找到 #region 使用同步方式來執行 文字
  • 將 #region 使用同步方式來執行 與 #endregion 區段內的程式碼,解除其註解
  • 按下 F5 開始執行這個專案
  • 當專案一啟動執行,必且顯示 Index.razor 頁面後
  • 可以從 Console 視窗中看到底下的輸出內容
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Vulcan\Projects\bzParameterBinding\bzParameterBinding
設定參數綁定 (4)
OnParametersSet (4)
設定參數綁定 (14)
OnParametersSet (14)

從 Console 視窗中看到的輸出結果,顯示了兩次 [設定參數綁定] 的文字,這是因為第一次的文字輸出是因為 Blazor 內部的 Pre-render 機制所造成的,第二次的輸出內容,則是顯示該頁面到網頁上的時候,所造成的。

另外,這裡也看到了,當透過參數綁定把物件傳遞到其他元件內,會先把物件值更新到有標示 [Parameter] 的屬性上,接著便會觸發 Blazor 內建生命週期的 [OnParameterSet] 事件方法。

在瀏覽器上可以看底下的畫面

請點選 [加一] 這個按鈕,此時 Console 螢幕會出現底下內容

觸發按鈕事件 (4)
計數器 + 1 (4)
呼叫方法 [0] (4)
設定參數綁定 (4)
OnParametersSet (4)

觸發按鈕事件 (13)
計數器 + 1 (13)
呼叫方法 [1] (13)
設定參數綁定 (13)
OnParametersSet (13)

不論你點選幾次這個 [加一] 按鈕,你會發現到,當在這個按鈕事件程式碼執行完成之後,會把計數器 conuter 加一,接著執行 component.razor 這個元件的 ShowSomething 方法,也就是呼叫 component.ShowSomething();

最後從 Console 輸出內容看到,當 ShowSomething 方法執行完成之後,參數綁定的動作才會完成,然而,此時對於正在執行 ShowSomething() 方法的時候,所看到的參數綁定物件內容,卻不是父元件所傳送過來的物件值,因此,這樣的執行結果不是當初所要設計出來的結果,也是我們小夥伴所遇到的問題已經透過這樣的範例程式碼重現出來了。

使用暫停一段時間的非同步設計,執行並且測試

  • 停止這個專案執行
  • 首先將剛剛解除註解的程式碼重新註解起來
  • 在 [Index.razor] 檔案中,找到 #region 呼叫元件方法前,使用 await 文字
  • 將 #region 呼叫元件方法前,使用 await 與 #endregion 區段內的程式碼,解除其註解
  • 按下 F5 開始執行這個專案
  • 當專案一啟動執行,必且顯示 Index.razor 頁面後
  • 可以從 Console 視窗中看到底下的輸出內容
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Vulcan\Projects\bzParameterBinding\bzParameterBinding
設定參數綁定 (9)
OnParametersSet (9)
設定參數綁定 (4)
OnParametersSet (4)

從 Console 視窗中看到的輸出結果,顯示了兩次 [設定參數綁定] 的文字,這是因為第一次的文字輸出是因為 Blazor 內部的 Pre-render 機制所造成的,第二次的輸出內容,則是顯示該頁面到網頁上的時候,所造成的。

另外,這裡也看到了,當透過參數綁定把物件傳遞到其他元件內,會先把物件值更新到有標示 [Parameter] 的屬性上,接著便會觸發 Blazor 內建生命週期的 [OnParameterSet] 事件方法。

在瀏覽器上可以看底下的畫面

請點選 [加一] 這個按鈕,此時 Console 螢幕會出現底下內容

觸發按鈕事件 (9)
計數器 + 1 (9)
設定參數綁定 (9)
OnParametersSet (9)
呼叫方法 [1] (16)

觸發按鈕事件 (9)
計數器 + 1 (9)
設定參數綁定 (9)
OnParametersSet (9)
呼叫方法 [2] (4)

觸發按鈕事件 (6)
計數器 + 1 (6)
設定參數綁定 (6)
OnParametersSet (6)
呼叫方法 [3] (11)

現在,你將會發現到,當按下 [加一] 按鈕之後,計數器會加一,之後會執行 [await Task.Yield()] ,當然,也可以執行 [await Task.Delay(100)] 休息 0.1 秒,不論是哪種方式,當程式碼遇到 await 之後,將會立即 return 回去,而此時,將會發現到參數綁定的工作將會正確無誤的執行,一旦作業系統配置原先等待的程式碼可以繼續使用 CPU 來執行的時候,將會正確顯示出剛剛父元件所傳遞過去的物件值。

從輸出內容可以看到,當呼叫 [ShowSomething()] 方法的時候,使用的是另外一個執行緒,而不是原先的執行緒,若你對於 await 這個運算子有充分的了解,就會知道為什麼會是這樣的結果。

將呼叫元件方法改成非同步方式呼叫,執行並且測試

  • 停止這個專案執行
  • 首先將剛剛解除註解的程式碼重新註解起來
  • 在 [Index.razor] 檔案中,找到 #region 將呼叫元件方法改成非同步方式呼叫 文字
  • 將 #region 將呼叫元件方法改成非同步方式呼叫 與 #endregion 區段內的程式碼,解除其註解
  • 按下 F5 開始執行這個專案
  • 當專案一啟動執行,必且顯示 Index.razor 頁面後
  • 可以從 Console 視窗中看到底下的輸出內容
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Vulcan\Projects\bzParameterBinding\bzParameterBinding
設定參數綁定 (9)
OnParametersSet (9)
設定參數綁定 (6)
OnParametersSet (6)

請點選 [加一] 這個按鈕,此時 Console 螢幕會出現底下內容

觸發按鈕事件 (6)
計數器 + 1 (6)
設定參數綁定 (6)
OnParametersSet (6)
呼叫方法 [1] (10)

觸發按鈕事件 (10)
計數器 + 1 (10)
設定參數綁定 (10)
OnParametersSet (10)
呼叫方法 [2] (12)

觸發按鈕事件 (6)
計數器 + 1 (6)
設定參數綁定 (6)
OnParametersSet (6)
呼叫方法 [3] (4)

現在,你將會發現到,當按下 [加一] 按鈕之後,計數器會加一,接著使用一個非同步工作來執行 [component.ShowSomething();] 這個方法,將會得到你想要的執行結果;然而,你可能發現到了,在這個 非同步 工作內,有執行 [System.Threading.Thread.Sleep(100);] 這個敘述,在呼叫 [component.ShowSomething();] 這個方法前,休息了 0.1 秒的時間。

現在,請將 [System.Threading.Thread.Sleep(100);] 這個敘述註解起來,重新執行一次,接著點選三次 [加一] 按鈕,你將會看到決然不同的執行結果,你知道發生了甚麼問題嗎?

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: D:\Vulcan\Projects\bzParameterBinding\bzParameterBinding
設定參數綁定 (5)
OnParametersSet (5)
設定參數綁定 (5)
OnParametersSet (5)

觸發按鈕事件 (5)
計數器 + 1 (5)
設定參數綁定 (5)
OnParametersSet (5)
呼叫方法 [1] (10)

觸發按鈕事件 (11)
計數器 + 1 (11)
呼叫方法 [1] (15)
設定參數綁定 (11)
OnParametersSet (11)

觸發按鈕事件 (9)
計數器 + 1 (9)
呼叫方法 [2] (4)
設定參數綁定 (9) 

OnParametersSet (9) 





2021年4月16日 星期五

Blazor 專案使用 SfDatePicker 選取日期

Blazor 專案使用 SfDatePicker 選取日期

使用 Syncfusion 元件來進行 Blazor 專案開發,可以立即使用 Syncfusion 所帶來的各種豐富的 UI 元件,加入 Blazor 專案的開發,在這篇文章所要探討的就是關於 SfDatePicker 這個元件所提供的功能,該元件可以提供使用者選取一個日期到 Blazor 專案內。

這個說明專案的原始碼位於 bsDatePicker

建立 Blazor Server-Side 的專案

  • 打開 Visual Studio 2019

  • 點選右下方的 [建立新的專案] 按鈕

  • [建立新專案] 對話窗將會顯示在螢幕上

  • 從[建立新專案] 對話窗的中間區域,找到 [Blazor 應用程式] 這個專案樣板選項,並且選擇這個項目

  • 點選右下角的 [下一步] 按鈕

  • 現在 [設定新的專案] 對話窗將會出現

  • 請在這個對話窗內,輸入適當的 [專案名稱] 、 [位置] 、 [解決方案名稱]

    在這裡請輸入 [專案名稱] 為 bzSyncfusion

  • 完成後,請點選 [建立] 按鈕

  • 當出現 [建立新的 Blazor 應用程式] 對話窗的時候

  • 請選擇最新版本的 .NET Core 與 [Blazor 伺服器應用程式]

  • 完成後,請點選 [建立] 按鈕

稍微等會一段時間,Blazor 專案將會建立起來

進行 Syncfusion 元件的安裝

  • 滑鼠右擊 Blazor 專案的 [相依性] 節點
  • 選擇 [管理 NuGet 套件]
  • 切換到 [瀏覽] 標籤頁次
  • 搜尋 Syncfusion.Blazor.Calendars 這個元件名稱
  • 選擇搜尋到的 [Syncfusion.Blazor.Calendars] 元件,並且安裝起來

進行 Syncfusion 元件的設定

  • 打開專案根目錄下的 [Startup.cs] 這個檔案
  • 找到 [ConfigureServices] 這個方法
  • 在這個方法的最後面,加入底下程式碼,已完成 Blazor 元件會用到的服務註冊
#region Syncfusion 元件的服務註冊
services.AddSyncfusionBlazor();
#endregion
  • 在同一個檔案內,找到 [Configure] 這個方法
  • 在這個方法的最前面,加入底下程式碼,宣告合法授權的金鑰 (License Key)
#region 宣告所使用 Syncfusion for Blazor 元件的使用授權碼
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("YOUR LICENSE KEY");
#endregion
  • 打開 [Pages] 資料夾內的 [_Host.cshtml] 檔案

  • 在 <head> 標籤內,加入需要的 CSS 宣告,如底下內容

    若沒有加入底下的宣告,將無法正常看到 Syncfusion 的元件樣貌

<link href="_content/Syncfusion.Blazor/styles/bootstrap4.css" rel="stylesheet" />

使用 SfCalendar 元件

  • 在專案的 [Pages] 資料夾
  • 打開 [Index.razor] 檔案
  • 使用底下程式碼替換到 [Index.razor] 檔案內容
@page "/"
@using Syncfusion.Blazor.Calendars

<h1>Hello, SfDatePicker!</h1>

<div>
    <div>有設定最小日期與最大日期, TValue 宣告為 DateTime? (Date1Value)</div>
    <SfDatePicker TValue="DateTime?" Min='@MinDate' Max='@MaxDate' @bind-Value='@Date1Value'></SfDatePicker>
</div>
<div>
    <div>有設定最小日期與最大日期, TValue 宣告為 DateTime (Date4Value)</div>
    <SfDatePicker TValue="DateTime" Min='@MinDate' Max='@MaxDate' StrictMode=false  @bind-Value='@Date4Value'></SfDatePicker>
</div>
<div>
    <div>有設定最小日期, TValue 宣告為 DateTime (Date2Value)</div>
    <SfDatePicker TValue="DateTime" Min='@MinDate' @bind-Value='@Date2Value'></SfDatePicker>
</div>
<div>
    <div>有設定最大日期, TValue 宣告為 DateTime (Date3Value)</div>
    <SfDatePicker TValue="DateTime" Max='@MaxDate' @bind-Value='@Date3Value'></SfDatePicker>
</div>
<div>
    <div>有設定最小日期, TValue 宣告為 DateTime? (Date5Value)</div>
    <SfDatePicker TValue="DateTime?" Min='@MinDate' @bind-Value='@Date5Value'></SfDatePicker>
</div>
<div>
    <div>有設定最大日期, TValue 宣告為 DateTime? (Date6Value)</div>
    <SfDatePicker TValue="DateTime?" Max='@MaxDate' @bind-Value='@Date6Value'></SfDatePicker>
</div>

<div class="bg-primary">
    <div class="text-white">Date1Value = @Date1Value</div>
    <div class="text-white">Date2Value = @Date2Value</div>
    <div class="text-white">Date3Value = @Date3Value</div>
    <div class="text-white">Date4Value = @Date4Value</div>
    <div class="text-white">Date5Value = @Date5Value</div>
    <div class="text-white">Date6Value = @Date6Value</div>
</div>
<SurveyPrompt Title="How is Blazor working for you?" />

@code {
    public DateTime MinDate { get; set; }
    public DateTime MaxDate { get; set; }
    public DateTime? Date1Value { get; set; }
    public DateTime? Date5Value { get; set; }
    public DateTime? Date6Value { get; set; }
    public DateTime Date2Value { get; set; }
    public DateTime Date3Value { get; set; }
    public DateTime Date4Value { get; set; }

    protected override void OnInitialized()
    {
        MinDate = DateTime.Now;
        MaxDate = DateTime.Now.AddDays(8);
        Date1Value = DateTime.Now.AddDays(2);
        Date2Value = DateTime.Now.AddDays(2);
        Date3Value = DateTime.Now.AddDays(2);
        Date4Value = DateTime.Now.AddDays(2);
        Date5Value = DateTime.Now.AddDays(2);
        Date6Value = DateTime.Now.AddDays(2);
    }
}

執行結果

從這裡看到使用 Syncfusion 元件相當的容易,只需要加入該元件所使用的命名空間在 Razor 元元件前面,接著就加入該元件的HTML標籤,例如在這裡使用的是 <SfDatePicker TValue="DateTime?" Min='@MinDate' Max='@MaxDate' @bind-Value='@Date1Value'></SfDatePicker>

現在可以直接執行這支程式,就會看到如下圖的執行結果。

首先看到這六個日期都會綁定到相對應的 ui 控制項,不論在 .NET 中的物件型別為 DateTime 或者 DateTime? ,只要宣告相對應的 TValue="DateTime" 或者 TValue="DateTime?",便可以正常運作。

當點選開 UI 控制項圖示,便會跳出萬年曆的選單,在上面的範例中,因為有設定最小與最大日期,因此,使用者儘可以在萬年曆畫面中,點選在允許範圍內的日期。

當點選開 UI 控制項圖示,便會跳出萬年曆的選單,在上面的範例中,因為僅有設定最大日期,因此,使用者儘可以在萬年曆畫面中,儘可以點選在最大日期前的允許日期。

 


2021年4月15日 星期四

在 ASP.NET Core Blazor 專案內,使用 MailKit 發送具有 HTML 格式的電子郵件

在 ASP.NET Core Blazor 專案內,使用 MailKit 發送具有 HTML 格式的電子郵件

當在進行 Blazor 專案程式設計的時候,需要能夠背景批次或者即時的來根據資料庫內的內容,發送出 SMTP 電子郵件,在這個文章中,將會說明如何使用 MailKit 這個套件來做到這樣的需求

現在來看看如何做出這樣的範例成程式碼。

這篇文章的原始碼位於 bzSendSMTP

建立Blazor Server 應用程式專案

  • 開啟 Visual Studio 2019
  • 選擇右下方的 [建立新的專案] 按鈕
  • 在 [建立新專案] 對話窗中
  • 從右上方的專案類型下拉按鈕中,找到並選擇 [Web]
  • 從可用專案範本清單內,找到並選擇 [空的 ASP.NET Core]
  • 點選左下方 [下一步] 按鈕
  • 在 [設定新的專案] 對話窗中
  • 在 [專案名稱] 欄位中輸入 bzSendSMTP
  • 點選左下方 [下一步] 按鈕
  • 在 [其他資訊] 對話窗中
  • 在 [目標 Framework] 下拉選單中,選擇 [.NET 5.0 (目前)]
  • 點選左下方 [建立] 按鈕

在 ASP.NET Core 的開發中安全儲存應用程式秘密

在這個專案內需要設定可以發送 SMTP 的相關資訊,這包含了遠端 SMTP 主機位置、使用通訊埠 Port、帳號與密碼,為了要讓這些資訊不會隨著版控一起被儲存起來,因此,這裡需要使用到 安全儲存應用程式秘密

  • 滑鼠右擊專案節點
  • 選擇 [管理使用者密碼] 彈出功能表選項
  • 此時將會看到 [secrets.json] 視窗出現
  • 請根據你的
  • 當建立一個 ASP.NET Core 專案後,會在專案跟目錄下跟目錄下請在 [bzSendSMTP] 專案根目錄下找到並且打開 [appsettings.json] 檔案
  • 使用底下的內容替換原先檔案內容
  • 在這裡新增加了一個 [中文選項1] 設定項目,而該設定項目內有3個子節點,分別為 [Child1] 、 [Child2] 、 [節點中文3]

    這裡刻意的使用中文字型,而不是僅使用英文字型,因此,要來體驗看看,當在 [appsettings.json] 檔案有中文字的時候,是否會有問題產生。

{
  "SMTPInformation": {
    "Server": "smtp.xxx.com",
    "Port": "???",
    "Accout": "user@host.com",
    "Password": "somethine"
  }
}

建立發送郵件的程式碼

  • 打開 [Pages] 資料夾內的 [] 檔案
  • 使用底下內容將其替換
@page "/"
@using MailKit.Net.Smtp
@using MailKit
@using MimeKit
@using Microsoft.Extensions.Configuration
@inject IConfiguration configuration
<h1>Hello, MailKit!</h1>

<div>
    <button class="btn btn-primary"
            @onclick="SendMailAsync">
        發送郵件
    </button>
</div>
<div class="display-1 text-success">
    @message
</div>

<SurveyPrompt Title="How is Blazor working for you?" />

@code
{
    string message;

    void SendMailAsync()
    {
        message = "";
        var sendMessage = new MimeMessage();
        sendMessage.From.Add(new MailboxAddress("vulcan_lee@hotmail.com", "vulcan_lee@hotmail.com"));
        sendMessage.To.Add(new MailboxAddress("Vulcan Lee", "vulcan.lee@gmail.com"));
        sendMessage.Subject = "恭喜你可以使用 Blazor 發送郵件";

        sendMessage.Body = new TextPart("html")
        {
            Text = @"<h1>你好</h1></br>請開始繼續下一個練習</br>你是否願意<b>繼續</b>接受挑戰?"
        };

        string Server = configuration["SMTPInformation:Server"];
        int Port = Convert.ToInt32( configuration["SMTPInformation:Port"]);
        string account = configuration["SMTPInformation:Accout"];
        string password = configuration["SMTPInformation:Password"];
        using (var client = new SmtpClient())
        {
            client.Connect(Server, Port, false);

            // Note: only needed if the SMTP server requires authentication
            client.Authenticate(account, password);

            client.Send(sendMessage);
            client.Disconnect(true);
            message = "郵件已經送出";
        }
    }
}

為了要讓這些程式碼可以正常運作,需要加入適當的參考命名空間,另外,為了要取得 安全儲存應用程式秘密 內容,因此需要注入 IConfiguration 的具體實作物件。

一旦使用者點選了 [發送郵件] 按鈕,將會觸發 SendMailAsync 方法

在這個方法內,首先建立一個 MimeMessage 物件,這裡需要宣告寄件者、收件者的 Email 電子郵件,接著,設定該郵件的主旨,最後,使用 TextPart 類別,指定建立郵件本身內容為一個 HTML 格式的郵件內容,之後將會指定給 MimeMessage.Body 這個屬性

然後,就要透過 IConfiguration 來取得之前設定 安全儲存應用程式秘密 內容。

現在,可以建立一個 SmtpClient 物件,使用這個物件來發送此封電子郵件

進行執行專案

按下 F5 開始執行這個專案,並且點選發送郵件按鈕

現在將會看到底下的畫面

從上面執行結果的螢幕截圖,可以看到 @inject IOptions<中文選項1> option1 敘述所注入 [IOptions<中文選項1>] 物件,是可以成功取得 [appsettings.json] 檔案內的設定內容。

底下將會為收到的電子郵件內容