2023年1月26日 星期四

使用 Vulcan.Maui.Template 專案範本來進行 MAUI for Prism 專案開發

使用 Vulcan.Maui.Template 專案範本來進行 MAUI for Prism 專案開發


早期在進行 Xamarin.Forms 專案開發的時候,通常會搭配 Prism 這套開發框架來進行 MVVM 設計模式的程式碼開發,由於 Prism 是一個 Framework 開發框架,因此,當要第一次建立專案的時候,若採用 Xamarin.Forms 所提供的預設專案範本所建立的專案,將會需要做許多的修正,才能夠在專案內使用 Prism 開發框架所提供的功能;然而,Prism 團隊也提供了一個 Prism Template Pack 擴充工具,可以安裝在 Visual Studio IDE 內,一旦想要進行 Xamarin.Forms 專案開發的時候,便可以透過 Prism Template Pack 所提供的專案範本來進行專案開發。

隨著 MAUI 開發工具的到來,Prism Template Pack 並沒有隨之提供最新版本的 MAUI 支援,想要在 MAUI 專案內使用 Prism 所提供的功能,需要安裝 Prism.Maui 這個套件,當然,同樣的也需要將使用原生 MAUI 專案範本所建立的專案,進行調整。

Prism 開發團隊內的 Dan Siegel 提供了一個暫時性的替代方案,那就是 Prism.Templates ,安裝好這個套件之後,就會在 Visual Studio 2022 開發工具內,出現可以使用 Prism 來開發 MAUI 的專案範本,可是, [Prism.Maui] 這個套件有升級,且使用方式有所不同,而且是會造成 Break Change ,可是,使用 [Prism.Templates] 所建立的專案範本卻沒有隨之升級,造成每次建立新的 MAUI 專案的時候,為了使用最新的 [Prism.Maui] 套件所提供的最新功能,又需要手動進行變更專案內的原始碼,使用起來相當的不方便。

有鑑於此,我自己進行開發出的可以採用最新 [Prism.Maui] 套件的專案範本套件,也就是 Vulcan.Maui.Template ,透過這個套件所產生出來的專案原始碼,已經預設安裝好 CommunityToolkit.Mvvm 這個套件來進行 MVVM 的程式碼設計 (之前都是使用 PropertyChanged.Fody 這個套件來支援 MVVM 的開發),使用起來便會更加方便了。

安裝

  • 打開命令提示字元視窗
  • 輸入 dotnet new install Vulcan.Maui.Template
  • 底下將會是解除安裝過程出現的內容
C:\>dotnet new install Vulcan.Maui.Template
將安裝下列範本套件:
   Vulcan.Maui.Template

成功: Vulcan.Maui.Template::0.2.2 已安裝下列範本:
範本名稱                                 簡短名稱     語言  標記
---------------------------------------  -----------  ----  -------------------------------------------
Vulcan Custom Full Prism .NET MAUI App   Full-Maui    [C#]  MAUI/Android/iOS/macOS/Mac Catalyst/Windows
Vulcan Custom Prism .NET MAUI App        Vulcan-Maui  [C#]  MAUI/Android/iOS/macOS/Mac Catalyst/Windows
Vulcan Custom Prism View and View Model  MVVMItem     [C#]  MAUI/Android/iOS/macOS/Mac Catalyst/Windows

解除安裝

  • 打開命令提示字元視窗
  • 輸入 dotnet new uninstall Vulcan.Maui.Template
  • 底下將會是解除安裝過程出現的內容
C:\>dotnet new uninstall Vulcan.Maui.Template
成功: Vulcan.Maui.Template::0.2.2 已解除安裝。

建立採用 Prism 開發框架的 MAUI 專案

  • 打開 Visual Studio 2022 IDE 應用程式

  • 從 [Visual Studio 2022] 對話窗中,點選右下方的 [建立新的專案] 按鈕

  • 在 [建立新專案] 對話窗右半部

    • 切換 [所有語言 (L)] 下拉選單控制項為 [C#]
    • 切換 [所有專案類型 (T)] 下拉選單控制項為 [MAUI]
  • 在中間的專案範本清單中,找到並且點選 [Vulcan Custom Prism .NET MAUI App] 專案範本選項

    Visual Studio 2022 建立新專案

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

  • 在 [設定新的專案] 對話窗

    Visual Studio 2022 設定新的專案

  • 請點選右下角的 [建立] 按鈕

  • 此時,將會建立一個可以用於 MAUI 開發的專案

專案結構內容說明

  • 底下是建立好的專案結構螢幕截圖

  • 在此專案內,已經預先建立好 [Views] 與 [ViewModels] 這兩個資料夾

  • 底下將會是 [MauiProgram.cs] 的檔案內容

using Prism.Ioc;
using Project5.ViewModels;
using Project5.Views;

namespace Project5;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .UsePrism(prism =>
            {

                prism.RegisterTypes(container =>
                      {
                          container.RegisterForNavigation<MainPage, MainPageViewModel>();
                      })
                     .OnInitialized(() =>
                      {
                          // Do some initializations here
                      })
                     .OnAppStart(async navigationService =>
                     {
                         // Navigate to First page of this App
                         var result = await navigationService
                         .NavigateAsync("NavigationPage/MainPage");
                         if (!result.Success)
                         {
                             System.Diagnostics.Debugger.Break();
                         }
                     });
            })
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

        return builder.Build();
    }
}
  • 這個專案將會使用 [Prism.DryIoc.Maui] 8.1.273-pre 這個現在最新的本版

  • 在 [CreateMauiApp] 方法內,將會使用 [UsePrism] 這個擴充方法來進行對 Prism 開發框架的宣告

  • 從 [RegisterTypes] 這個委派方法內,將會進行此專案會用到的各種服務、頁面的 IoC 容器註冊動作

  • 從 [OnAppStart] 這個委派方法內,將會看到在此設定需要第一個開啟的頁面

  • 底下將會是 [MainPage.xaml] 的檔案內容

  • 在這裡加入一個 [viewModel] 命名空間宣告與使用 [x:DataType] 宣告需要使用編譯時期的資料綁定行為

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             Title="{Binding Title}"
             x:Class="Project5.Views.MainPage"
             xmlns:viewModel="clr-namespace:Project5.ViewModels"
             x:DataType="viewModel:MainPageViewModel">

  <ScrollView>
    <VerticalStackLayout
            Spacing="25"
            Padding="30,0"
            VerticalOptions="Center">

      <Image Source="prism.png"
             SemanticProperties.Description="Cute dot net bot waving hi to you!"
             HeightRequest="150"
             HorizontalOptions="Center" />

      <Label Text="Hello, World!"
             SemanticProperties.HeadingLevel="Level1"
             FontSize="32"
             HorizontalOptions="Center" />

      <Label Text="Welcome to Prism for .NET MAUI"
             SemanticProperties.HeadingLevel="Level2"
             SemanticProperties.Description="Welcome to Prism for dot net Multi platform App U I"
             FontSize="18"
             HorizontalOptions="Center" />

      <Button Text="{Binding Text}"
              SemanticProperties.Hint="Counts the number of times you click"
              Command="{Binding CountCommand}"
              HorizontalOptions="Center" />

    </VerticalStackLayout>
  </ScrollView>

</ContentPage>
  • 在這裡加入一個 [viewModel] 命名空間宣告與使用 [x:DataType] 宣告需要使用編譯時期的資料綁定行為

  • 底下將會是 [MainPage.xaml] 的檔案內容

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace Project5.ViewModels;

public partial class MainPageViewModel : ObservableObject, INavigatedAware
{
    private int _count;

    public MainPageViewModel()
    {
    }

    [ObservableProperty]
    string title = "Main Page";

    [ObservableProperty]
    string text = "Click me";



    [RelayCommand]
    private void Count()
    {
        _count++;
        if (_count == 1)
            Text = "Clicked 1 time";
        else if (_count > 1)
            Text = $"Clicked {_count} times";
    }

    public void OnNavigatedFrom(INavigationParameters parameters)
    {
    }

    public void OnNavigatedTo(INavigationParameters parameters)
    {
    }
}
  • 從最上方看出,這個 ViewModel 類別內,已經加入 Microsoft MVVM Toolkit 的命名空間宣告
  • 這個 [MainPageViewModel] 類別也繼承了 [ObservableObject] 類別,並且該類別也宣告了 [partial],表示了在這個類別內,可以開始使用 Microsoft MVVM Toolkit 所提供的功能
  • 若想要設計一個資料綁定 Data Binding 的屬性,在這個類別內僅需要以 欄位 Field 的方式來設計此一成員,並且此欄位的名稱,需要符合欄位名稱命名慣例,也就是第一個字需要為小寫,最後,在該欄位上方加入 [ObservableProperty] 這個屬性宣告,如此,編譯器所觸發的原始碼產生器,將會自動產生出一個 屬性 Property 成員出來,並且也會實作出 [INotifyPropertyChanged] 會用到的各種方法
  • 對於想要設計一個可用於命令綁定的物件,在此僅需要設計一個方法(並不一定需要 public),接著,在此方法上方加入 [RelayCommand] 這個屬性宣告,如此,編譯器所觸發的原始碼產生器,將會自動產生出一個以這個方法名稱加入 [Command] 的 RelayCommand 物件,有了這個物件,便可以在相對應的頁面上,使用這個物件來進行命令的綁定。

執行結果

  • 切換到 [Windows Machine] 模式,開始執行此專案,將會看到底下結果

  • 切換到 [Android Emulator] 模式,選擇一個適合的模擬器,開始執行此專案,將會看到底下結果

 




2023年1月25日 星期三

動手練習 ASP.NET Core7 相依性注入 Dependency Injection 的使用方式

動手練習 ASP.NET Core7 相依性注入 Dependency Injection 的使用方式

在現今專案開發過程中,相依性注入 Dependency Injection 這個設計模式 Design Pattern 扮演著相當重要的角色,因為,一旦在專案中使用了相依性注入這個設計模式,將會透過 DI / IoC Container 容器來進行所需要使用到的服務的 註冊 Registration,而在各個相關類別內,可以透過經常使用的 建構式注入 Constructor Injection 的方式,宣告這個類別需要使用到其他的服務,這個過程稱之為 解析 Resolve;當在這個類別取得相依物件之後,便可以進行此相依物件的操作與使用,一旦此物件使用完畢之後,當初所注入的服務物件,便會根據當初所注入時期宣告的生命週期來進行管理,由 DI / IoC 容器決定何時要 釋放 Release 當初所注入的服務物件。

因此,整個相依性注入這個設計模式,將會圍繞著 RRR (Registration , Resolve , Release)這三種操作來進行,透過這個設計模式將會獲得到整個專案具有鬆散耦合的特性,這也代表了這個專案具有好維護的特性,因為,一旦當初所規劃與設計的實作服務物件需要進行變更或者更換的時候,此時,便可以設計另外一個新服務類別,無需使用原有的服務類別來進行修改,緊接著透過對相依性服務容器的註冊階段,宣告此專案將會注入此一新設計的服務物件,不再使用原有的服務物件。

對於第一次接觸這個相依性注入設計模式的程式設計師,絕大多數很難接受這樣的開發做法,猛一看,整個開發過程似乎變得相當複雜,而且產生出更多的程式碼,不過,這一切的辛苦與努力,只是為了要能夠達成一件事情,讓整體專案變得好維護、好擴充與延伸,因此,一旦學會與使用這個設計模式之後,所得到的效益與成果,是沒有使用這個設計模式之前很難想像到的。

那麼,相依性注入 Dependency Injection 這個設計模式 Design Pattern 究竟好不好學習與實做呢?接下來就來看看

在這個練習中,將會設計一個服務類別 [MyService] ,此類別內僅會有一個 [Hi] 方法,並且該方法會回傳一個字串。

首先先來建立一個 ASP.NET Core Web 應用程式 (Model-View-Controller) 專案,請依照底下說明來建立這個專案

  • 打開 Visual Studio 2022 IDE 應用程式
  • 從 [Visual Studio 2022] 對話窗中,點選右下方的 [建立新的專案] 按鈕
  • 在 [建立新專案] 對話窗右半部
    • 切換 [所有語言 (L)] 下拉選單控制項為 [C#]
    • 切換 [所有專案類型 (T)] 下拉選單控制項為 [Web]
  • 在中間的專案範本清單中,找到並且點選 [ASP.NET Core Web 應用程式 (Model-View-Controller)] 專案範本選項
  • 點選右下角的 [下一步] 按鈕
  • 在 [設定新的專案] 對話窗
  • 找到 [專案名稱] 欄位,輸入 AN005 作為專案名稱
  • 點選右下角的 [下一步] 按鈕
  • 現在將會看到 [其他資訊] 對話窗
  • 請點選右下角的 [建立] 按鈕

建立需要使用到的服務類別

在這裡將會按照一般常用的設計方式來進行操作

  • 使用滑鼠右擊該專案節點

  • 在彈出功能表上,選取 [加入] > [新增資料夾] 選項

  • 在新產生的資料節點上,輸入 [Services] 這個名稱作為資料夾的名稱

    日後,若有其他相關服務要進行新增,請在此資料夾下來進行新增新的類別來進行設計,如此的好處將會是可以把所有的服務都存在同一個地方,方便區隔與管理,當然,一旦該資料夾的服務隨著時間延續,需求不斷的增加,也會造成該資料夾下的類別檔案數量越來越多,此時有一種選擇方式,那就是在此 [Services] 資料夾下,建立出更多不同分類的資料夾,江河是的服務類別檔案,拖拉到合適的資料夾內,如此將會達到更加清爽的檢視與管理目的。

  • 使用滑鼠右擊 [Services] 資料夾節點

  • 在彈出功能表上,選取 [加入] > [類別] 選項

  • 此時,將會出現 [新增項目] 對話窗

  • 在此對話窗最下方的 [名稱] 欄位內,輸入 MyService 這個文字,作為此新建立類別的名稱與檔案名稱

  • 底下將會是剛剛建立的 [MyService] 類別的程式碼

namespace AN005
{
    public class MyService
    {
    }
}
  • 請依據底下程式碼重新設計此新的類別程式碼
public class MyService
{
    public string Hi(string name)
    {
        return $"Hi {name}";
    }
}

在此設計一個 [Hi] 方法,將會接收一個字串型別的參數,也就是要傳入一個名字,接著將會對這個名字文字說 Hi,因此透過 $"Hi {name}" 表示式將其組合起需要的新文字內容,最後此文字回傳回去。

為新建立的服務類別,建立新的介面

原則上來說,當要設計一個新的服務類別,應該會要設計該類別的 介面 Interface ,也就是該類別對外溝通的合約,接著,才會依據這個新建立的 介面 來實作出新的服務類別出來;不過,在這裡將會簡化這個過程,而是先設計一個服務類別,然後,透過 Visual Studio 2022 所提供的 重構 Refactor 工具,為這個類別自動產生對外溝通會用到的合約介面出來。

  • 將滑鼠移動到 [MyService] 這個文字任何地方

  • 使用滑鼠右擊此文字,將會出現如下圖的彈跳功能表清單出來

  • 請選取 [快速動作與重構] 這個選項

  • 接下來將會看到另外一個彈跳功能表清單

  • 在這個彈跳功能表清單中,看到需要使用到的重構功能,請選取 [擷取介面] 這個項目

    這個功能選項將可讓您使用類別、結構或介面的現有成員來建立一個新介面,如此,便會自動產生出一個對外會用到溝通合約介面了,想要使用這個類別的任何功能或者屬性,僅能夠透過此介面來存取,對於參考與使用這個類別所建立的物件而言,使用者並不需要知道是哪個類別來提供這些服務,使用者僅需要參考所用到的介面,而真正的服務類別也僅需要實作所指定的介面即可。

  • 緊接著將會看到新的對話窗出現,在這個 [擷取介面] 對話窗內,將可以調整需要產生介面的內容

    在此為了簡化練習過程,將會使用預設值來操作,更多關於這方面的資訊,可以參考 擷取介面重構

  • 在 [擷取介面] 對話窗內,點選右下角的 [確定] 按鈕,以便產生出這個新的介面

  • 底下將會是新產生出的來的介面檔案內容

    這是一個 C# Interface 介面宣告,這可以從 public interface ... 看的出來。

    這個介面內僅宣告一個成員,那就是一個名稱為 Hi 的方法,從該 方法簽章 Method Signatures 可以看出,這個介面需要一個方法,其需要傳入一個型別為字串的參數,而該方法將會回傳一個型別為字串的物件

    對於任何要實作 Implementation 這個介面的類別,在該類別內都需要也剛剛提到的函式簽章存在

namespace AN005.Services
{
    public interface IMyService
    {
        string Hi(string name);
    }
}
  • 原有的 [MyService] 類別,將會被重構為如下程式碼

    在這個類別名稱之後,自動加入了  : IMyService 這個介面宣告,表示這個類別有實作這個 [IMyService] 介面合約內容。

namespace AN005.Services
{
    public class MyService : IMyService
    {
        public string Hi(string name)
        {
            return $"Hi {name}";
        }
    }
}

開始用使用相依性注入容器來進行新服務註冊

一般來說,當要進行與使用相依性服務容器來進行註冊的時候,通常會在 Composition Root 組合根 地方來進行,也就是通常的程式進入點位置,在這裡 ASP.NET Core7 相關專案,程式進入點的位置就是在 [Program.cs] 這個檔案內來宣告,所以,接下來的工作就要進行這個服務的註冊程式設計。

  • 在專案根目錄下,找到並且打開 [Program.cs] 檔案

  • 找到 builder.Services.AddRazorPages(); 敘述

  • 在其下方加入底下程式碼

    型別為 WebApplicationBuilder 的 [builder] 物件,裡面有個 [Services] 屬性,他的型別為 IServiceCollection ,其目的將會是指定服務描述項集合的合約,講白話點,那就是要在這裡進行對相依性服務容器來進行 註冊 宣告

    對於 [AddTransient] 這個方法,將會是指定此注入行為所產生的物件,其物件生命週期為何,也就是該物件何時會產生、何時會消滅掉,這個 [AddTransient] 方法將會宣告當要注入此物件的時候,將會新建立與產生新的物件,而當參考使用的物件歸還記憶體之後,這個注入的物件,也會消滅並把所占有的記憶體歸還給作業系統。

    這個 [AddTransient] 方法提供泛型方法覆載,在這裡的範例中,先傳入一個介面型別,緊接著傳入一個有實作該介面的類別,一旦想要透過相依性注入容器來注入或者取得(正式的名稱為 解析 Resolve),僅會告知需要一個 [IMyServer] 型別的物件,而究竟是哪個有實作該介面的物件會產生出來,就會依據這裡的宣告來建立新的物件。

    之前有提到,若因為有新的需求需要變更這個 [MyServer] 類別,程式設計師可以依據新的需求,重新設計一個新的 [MyService2] 類別,接著在此修改宣告,任何未來想要使用 [IMyService] 這個介面的時候,都會注入 [MyService2] 物件,也就是說,相依性服務容器的註冊程式碼將會改成 builder.Services.AddTransient<IMyService, MyService2>(); ,最後,不用再修改任何程式碼,重新啟動這個專案,此時,該專案便可以滿足新變更的需求。

builder.Services.AddTransient<IMyService, MyService>();

注入與使用 MyService 物件

  • 在 [Program.cs] 檔案內找到 var app = builder.Build(); 敘述
  • 在該敘述之後加入底下程式碼

在型別為 [WebApplication] 的 [app] 物件內,將會有個型別為 IServiceProvider 的 [Services] 屬性,其目的為定義機制來擷取服務物件,也就是為其他物件提供自訂支援的物件,講白話來說,就是可以透過這個屬性來進行手動注入需求

因此,使用 [GetService] 這個泛型方法,提供一個 [IMyService] 型別,代表說要透過相依性注入容器,取得一個有實作 [IMyService] 介面的物件。

根據剛剛的程式碼,在相依性注入容器內對於 [IMyService] 介面的註冊內容,將會對應到 [MyService] 這個型別類別,因此,當呼叫了 app.Services.GetService<IMyService>() 這個方法呼叫,相依性注入容器將會產生一個新的 [MyService] 物件,並且回傳到 [myService] 區域變數內。

var myService = app.Services.GetService<IMyService>();
var logger = app.Services.GetService<ILogger<Program>>();
var hi = myService.Hi("Lee");
logger.LogInformation($"In WebApplication, Call Hi Method : {hi}");

最後整個專案將會如下結構

 




2023年1月18日 星期三

由 ASP.NET Core MVC (Model-View-Controller) 專案範本來理解與學習 ASP.NET Core

由 ASP.NET Core MVC (Model-View-Controller) 專案範本來理解與學習 ASP.NET Core

在這個系列文章 由專案範本來理解與學習 ASP.NET Core 架構 中,將會透過 Visual Studio 2022 內建的幾個專案範本所產生的程式碼,了解 ASP.NET Core 7 的運作方式與在這些專案中的差異在哪裡?

  1. 由 ASP.NET Core 空白專案範本來理解與學習 ASP.NET Core
  2. 由 ASP.NET Core Web API 專案範本來理解與學習 ASP.NET Core
  3. 由 ASP.NET Core 應用程式 (Model-View-Controller) 專案範本來理解與學習 ASP.NET Core

對於更多關於 ASP.NET Core 7 的說明內容,可以參考 ASP.NET Core 基本概念的概觀

在上一篇文章中,使用 ASP.NET Core Web API 範本建立一個專案,並且從這個專案原始碼中,了解到 ASP.NET Core 的運作方式,並且,了解到 Web API 專案範本與 空白 專案範本的差異在哪裡,不過,透過 Web API 專案範本,卻可以提供這個 Web 網站具有 Web API 服務功能。

在這篇文章中,將會建立同樣名稱的專案,不過將會採用 ASP.NET Core Web 應用程式 (Model-View-Controller) 類型的專案範本,並且來比較這個 Web API 類型的專案與空白類型的專案有何不同。

首先先來建立一個 ASP.NET Core Web 應用程式 (Model-View-Controller) 專案,請依照底下說明來建立這個專案

  • 打開 Visual Studio 2022 IDE 應用程式

  • 從 [Visual Studio 2022] 對話窗中,點選右下方的 [建立新的專案] 按鈕

  • 在 [建立新專案] 對話窗右半部

    • 切換 [所有語言 (L)] 下拉選單控制項為 [C#]
    • 切換 [所有專案類型 (T)] 下拉選單控制項為 [Web]
  • 在中間的專案範本清單中,找到並且點選 [ASP.NET Core Web 應用程式 (Model-View-Controller)] 專案範本選項

    Visual Studio 2022 建立新專案

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

  • 在 [設定新的專案] 對話窗

  • 找到 [專案名稱] 欄位,輸入 ASPNETCore7 作為專案名稱 Visual Studio 2022 設定新的專案

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

  • 現在將會看到 [其他資訊] 對話窗

  • 找到 [使用控制器 (取消勾選已使用最低 API)] 檢查盒,注意,一定需要勾選這個選項,因為,在此先來觀察傳統的使用 API Controller 建立的專案長成甚麼樣子。 Visual Studio 2022 其他資訊

  • 請點選右下角的 [建立] 按鈕

完成專案創建之後,將會看到 Visual Studio 2022 將這個新專案開啟,從 [方案總管] 視窗內,可以看到這個專案內所建立的檔案

首先,按下 F5 看看這個專案的執行結果會呈現甚麼樣貌

在 Visual Studio 2022 應用程式的上方,將會看到一個綠色三角形,請點選該綠色三角形來執行這個專案。

一旦專案編譯、建置完成後,瀏覽器將會出現這個網頁

在網頁上看出,並沒有像空白專案會顯示出 Hello World! 這個文字 或者 Web API 專案顯示出 Swagger 的網頁,這裡將會出現一個採用 MVC 開發框架所設計出來的網頁內容,接下來透過程式碼內容,來理解為什麼可以生成這樣的網頁內容。

從專案內容來理解為什麼會有這樣的執行結果

先來看一下 ASP.NET Core Web 應用程式 (Model-View-Controller) 專案的專案定義宣告檔案,也就是 [.csproj]。

可以透過直接點擊方案總管內的專案名稱節點,也就是 [ASPNETCore7] 或者 使用檔案總管找到 [ASPNETCore7.csproj] 這個檔案,便可以看到關於這個專案的定義宣告檔案。

底下的將會是空白專案的 [.csproj] 檔案內容

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

</Project>

這裡可以看到此專案定義宣告檔案,將會與 空白 專案所用到相同,都是採用 [Microsoft.NET.Sdk.Web] 這個 SDK,並且將會採用 .NET7 作為目的框架。既然都是相同,在這裡的專案可以使用 MVC 方式進行開發與顯示出更加豐富的網頁,這樣是怎麼做到的呢?

對於 [Properties] 資料夾內的 [launchSettings.json] 檔案 與 [appsettings.json] 這兩個檔案,其實與空白專案內的用法與意義是相同的。

接下來要來比較程式進入點差異,也就是這個 Web 網站的核心程式碼,這些程式碼將會在 [Program.cs] 檔案內,其內容如下:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

從 Web 應用程式 (Model-View-Controller) 專案內產生的 [Program.cs] 檔案,同樣的使用 var builder = WebApplication.CreateBuilder(args); 敘述來建立一個 [WebApplicationBuilder] 型別物件到 [builder] 變數內。

在 Web 應用程式 (Model-View-Controller) 專案內,對於這個 builder 並沒有做其他的呼叫,僅呼叫 builder.Build() 方法,取得一個型別為 [WebApplication] 物件到 [app] 變數內`,也就是說,這裡沒有明確地進行要進行各種服務註冊到相依性注入容器內。

接下來就是要進行管道與中介軟體 Middleware 的宣告

使用 app.Environment.IsDevelopment() 來判斷此專案此時是否在開發模式下運行,若不是在開發模式下執行,則會加入這兩個中介軟體

app.UseExceptionHandler : 新增例外狀況處理中介軟體

app.UseHsts() : 新增強制執行 HTTPS

接著將會宣告

app.UseHttpsRedirection() : 將 HTTP 要求重新導向至 HTTPS

app.UseStaticFiles() : 可以取得靜態檔案內容

app.UseRouting() : 將路由比對新增至中介軟體管線

app.UseAuthorization() : 授權中介軟可授權使用者存取安全資源

app.MapControllerRoute(...) : 建立單一路由

app.Run() : 執行應用程式並封鎖呼叫執行緒,直到主機關閉為止

可以透過上面的程式碼與 空白 和 Web API 專案來比較,其實就是加入幾行程式碼,瞬間就可以將專案宣告成為採用 MVC 框架方式來設計與執行,其實,這一切功能都包含在 Microsoft.NET.Sdk.Web 內了。

現在來看看在 [Controllers] 資料夾內,有 [HomeController.cs] 檔案,這個檔案內容如下:

using ASPNETCore7.Models;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;

namespace ASPNETCore7.Controllers
{
    public class HomeController : Controller
    {
        private readonly ILogger<HomeController> _logger;

        public HomeController(ILogger<HomeController> logger)
        {
            _logger = logger;
        }

        public IActionResult Index()
        {
            return View();
        }

        public IActionResult Privacy()
        {
            return View();
        }

        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

這個控制器繼承 [Controller] 類別,而這個類別又繼承了 [ControllerBase],也就是,如同 Web API 專案一樣,這些控制器都是繼承於 [ControllerBase] 這個類別

依據 ASP.NET Core MVC 開發框架的設計方式,將會在這個 [HomeController] 類別內設計許多 [Action] 方法,這樣就可以在網頁中使用 URL 來呼叫了。

對於 [Views] & [Models] 這兩個資料夾,也是依據 MVC 開發框架的命名慣例所存在的,用來設計相關檢視頁面內容和資料模型類別。

另外,因為這是 MVC 網頁設計專案,因此,將會額外增加一個 [wwwroot] 目錄,這裡將會儲存網站會用到的靜態資源檔案,例如: .js JavaScript 檔案、 .css 、圖片等檔案