---
###### tags: `web111a` `程式筆記` `ASP.NET` `C#`
---
# ASP.NET C# MVC
* 老師:許志強
* 時間:2022/04/27
## 淵源
### .NET Support Policy
* 早期是 Webform,後來改 [MVC](#補充:MVC) 架構,
* 所以 .Net Framework (目前 4.8 版)問世了,他可以寫Webform 和 .Net Framework,但是沒有跨平台支援
* 於是有 .Net Core (目前 3.1 版)
* 後來又有 .Net 6
* 之後預計還會有 .NET 7, .Net 8 ...
![.NET Support Policy](https://i.imgur.com/VRewGSo.png)
:::success
本課以 .Net Framework 為主,時間充足會帶到 .Net Core、.Net 6
:::
~~補充:MVC~~ 2022/5/24 更新:拉到[下方章節](#MVC)
### 那手機呢?
* 以前除了寫 C#,手機端還要再學 Android 以及 iOS 開發,
* 於是有了 Xamarin,讓你能用 C# 開發手機 APP
* 但微軟又双叒叕宣布之後 MAUI 將會取代 Xamarin
### 開發工具
* Visual Studio
* 整合性較高,可以寫 .Net Framework、.Net Core
* 比較肥大,畢竟還要能寫舊東西
* 只能用在 Windows 平台
* Visual Studio Core
* 特別針對跨平台,MacOS、Unix 也可以用
* 業界趨勢,故新技術支援叫豐富
:::success
結論:小孩才做選擇。本課以 Visual Studio 為主,時間充足會帶到 Visual Studio Core
:::
#### 版本選擇
* 年份
* 2019
* 2022支援 .Net 6 :ballot_box_with_check:
* 版本
* 社群版(微軟會員免費):ballot_box_with_check:
* 專業版
* 企業版
## C\#
### Visual Studio 基操
### `try/catch`
```csvpreview=
try
{
// 有可能出錯的程式碼
}
catch (Exception e)
{
// 錯誤訊息在這邊撈
Console.WriteLine("Error occurred from {0}. Message = {1}", path, e.Message);
}
finally
{
// 無論如何執行這邊
}
```
### `TryParse`
比起 `try/catch` 不會讓程式中斷,可以再用迴圈讓使用者重新輸入
```csharp=
double.TryParse(str_height, out db1_height)
```
---
* 時間:2022/5/12
## 物件導向程式設計 OOP
:::info
請反覆看老師影片,有些快捷鍵的地方,反覆看反覆看反覆看
:::
預計花一兩天說明以下:
![成員](https://i.imgur.com/7dfdcmq.png)
## 類別(class)
類別 :arrow_right: new(實作化) :arrow_right: 物件
要 new 之前需要有翻譯的動作:高階語言(人在寫的):arrow_right:低階語言(機器看的)
![class](https://i.imgur.com/a3hy02p.png)
* 屬性(property):把物件的特性做歸納(越來越細分)
* 方法(method()):物件可以做的動作
* 函數:有回傳值
* 事件:沒有回傳值
### 實作看看吧
#### 寫程式從 class 開始
![寫程式從 class 開始](https://i.imgur.com/IwT4uWw.png)
namespace 其實就是分類,可以自己取,系統的(program.cs)就不要改
物件導向class裡面的註解是 `///`
:::spoiler 註解很重要,之後實體化時都會看得到
![註解](https://i.imgur.com/LLnNolh.png)
:::
怎麼插入屬性? :a: 輸入 `prop` + 按兩次`tab`
![prop](https://i.imgur.com/QAyxVqE.png)
![prop 完整版](https://i.imgur.com/lwWwEXM.png)
接著把方法、事件補齊
![方法](https://i.imgur.com/5j4N2zM.png)
#### 實作化
從 Program.cs 去 new
![](https://i.imgur.com/TzzpHXw.png)
![實作化](https://i.imgur.com/ti6aiFi.png)
你也許會發現錯誤,因為不同分類(namespace),Program.cs 是 oopdemo 但是我自己寫的 Student.cs 是 mitour,有以下解法:
* 放在同一分類
* 明確表示哪個分類,但程式碼會變得很長
```csharp=
mitour.Student Tony = new mitour.Student();
```
* :ballot_box_with_check:使用 using
`using`是什麼?
程式搜尋的路徑,擺得越上面,搜得越快,例如你的類別放在 mitour,如果 `using mitour;` 放第一位搜尋速度會比較快,就不用搜尋 A 沒找到、搜尋 B 沒找到、搜尋 mitour 找到了
```csharp=
using mitour;
using A;
using B;
```
visual basic 很好心的會幫你尋求解決方案,把滑鼠移到錯誤上看看
![一盞光明燈](https://i.imgur.com/RYX8p1z.png)
:::info
小撇步:using 沒用到會比較暗,有用到會亮起來
:::
## 枚舉類型 (Enum)
用來限制**程式設計師**輸入,就像下拉式選單
![枚舉類型](https://i.imgur.com/uVvuvvR.png)
### 實作看看吧
Enum 本質上還是 class,新增 class
![enum](https://i.imgur.com/lGgBik8.png)
建議不要分類,卡好用
自己手動 class 改 enum
![手動class改enum](https://i.imgur.com/hfyRjaG.png)
不編號預設就從 0 開始編號,所以下列不寫0、1、2是一樣的
![enum](https://i.imgur.com/cIcMSGD.png)
記得加上註解
然後你會發現 Student.cs 中 Gender 就可以改成自己建立的 enGender
![enGender](https://i.imgur.com/RpZszw4.png)
從 Program.cs 去使用
```csharp=
Tony.Gender = enGender.Man;
```
:::spoiler 快捷鍵
1. `=` 完加上 `空白`
![enGender快捷鍵](https://i.imgur.com/CsItvXt.png)
1. `enGender` 完加上 `.`
![enGender快捷鍵](https://i.imgur.com/6PixAcz.png)
:::
## 建構子
在 new 的瞬間就會呼叫建構子
![建構子](https://i.imgur.com/z6qfw2p.png)
* 建構子:在記憶體產生之後馬上執行,可以做的事例如建立預設屬性
* 解構子:在清出記憶體之前可以做的事,譬如斷開資料庫連結
* 多型:原則上同一 class 只能有一個同名建構子,但是如果參數不同就可以,可以讓new的同時把屬性參數帶入,程式碼變簡潔
### 使用注意事項
![建構子注意事項](https://i.imgur.com/h0iUc2g.png)
### 實作看看吧
public 之後直接接 class 名(此例 Student)
![建構子](https://i.imgur.com/EOB5Sbm.png)
多型
![多型建構子](https://i.imgur.com/5NBrrwU.png)
快速建立建構子? :a: `ctor` + 按兩次 `tab`
![快速建立建構子](https://i.imgur.com/ufYwyzy.png)
#### 實作化
從 Program.cs 去 new
然後你會發現即便沒有用性別、生日,也能帶出預設值
![測試建構子](https://i.imgur.com/SbClk5u.png)
測試多型
![測試多型](https://i.imgur.com/I10MEIF.png)
```csharp=
Student Mary = new Student("002", "Mary", enGender.Woman, DateTime.Now);
```
## 繼承
寫之前先想好階層
![繼承](https://i.imgur.com/Jt6cnqP.png)
### 實作看看吧
針對父階層「車」建立 class
![car class](https://i.imgur.com/LM4Rrea.png)
再加入子階層「引擎驅動車」
* 注意分類(namespace)應該跟「車」同一類
* 其中需要枚舉類別的記得建立
* 繼承的寫法:在子類別後方加上 `: <父類別>`
```csharp=
public class Engine : Car
{
}
```
![引擎驅動車](https://i.imgur.com/HWy6XFW.png)
visual studio 檢視類別圖,可能需要額外安裝元件,mac 沒有 GG
![檢視類別圖](https://i.imgur.com/Sm10Ppb.png)
## 抽象(Abstract)
不接受實作化,一般作為父階被繼承用。本例 Car 就是
視為一種防呆機制
![Abstract](https://i.imgur.com/zq5xDgA.png)
## 虛擬(Virtual)、覆寫(Override)
* Virtual 寫在父層
* Override 寫在子層
代表子階可以覆寫父階的事件或函數
![開放複寫](https://i.imgur.com/E5JA6nf.png)
![覆寫](https://i.imgur.com/FMO2xyt.png)
### 實作看看吧
![實作覆寫](https://i.imgur.com/bU2swDv.png)
## 存取修飾子(Access modifier)
* private:只能用在該類別(class)裡面,繼承也不行
* public:任何類別(class)都能用
* internal:同分類(namespace)才能用
* protected:不管分類,有繼承關係才能共用
* protected internal:合集組合技,同分類可以、有繼承關係也可以
### 注意事項
`erum` 只能是 `public`
## 修飾詞 (static)
[為甚麼要宣告static](https://ithelp.ithome.com.tw/articles/10184803)
> 當程式中出現new的時候,會跟記憶體申請記憶體空間,當記憶體擁有對應的大小的記憶時就會將其提供給該程式使用,而該程式也能針對該區域的記憶體進行讀寫。
>
> 而static變數就是在載入程式後會主動配給記憶體給程式(僅一次),後續無論實例化多少次,記憶體位置都一樣。
>
> 實務上,通常會將公共變數全部設定為靜態變數。這樣可以共享記憶體,相對較節省記憶體。
>
> 但事情總是有兩面性的,也因為共享記憶體,所以要特別注意存取權限以及存取時機,例如:通常不會在建構式中設定靜態變數,以免造成其他程式誤取。
---
* 時間:2022/5/24
* 預告:時程安排關係先上後面章節 Web 設計,物件導向剩下的部分之後在提
## 好奇寶寶提問專區
* 怎麼在程式碼裡連接資料庫並抓到欄位
* Ans: 會寫在 web config 中,連線的語法、抓欄位的語法下午會講
* 資料庫密碼怎麼隱藏?版控時避免誤傳
1. 資料庫端不能使用 SA 因為權限太高,安全考量會為了每一個資料庫建立一至多位使用者
1. 在 web config 裡不以明碼寫資料庫密碼,而是自行建立加密、解密動作
* 但不建議,如果你離職要確保後人看得懂,較容易造成管理不易、維護困難
* html 裡怎麼寫 ASP.NET
* 利用 [Razor 語言](#Razor-語言)
## MVC
* 目的
* 重複使用已寫好的程式碼,簡化應用程式的開發
* 增強程式的可維護性
* 方便團隊合作
* 缺點:耗時
![MVC model](https://i.imgur.com/P2DPXIH.jpg)
### Model 常譯為「模型」,負責和資料庫溝通
* 邏輯層
* 業務邏輯相關的資料以及對資料的處理方法
* 存取資料
* 伺服端程式語言
範例:
* 會員購物有九折、訂單超過一定的金額免運費
* 檢查登入帳號的類型,並依此開放不同權限
* 判斷使用者彼此之間的友好程度
* 過了期但沒被執行的 to-dos 不能被刪除
### View 常譯為「視圖」,HTML template
* 表現層
* 圖像製作
* 前端程式語言
### Controller 常譯為「控制器」
* 注重 OOP 觀念
* 收發 request/response 的核心
* 主要負責將視覺的靜態頁面轉換成嵌有程式的動態頁面
* 將資料利用表單發送到 Model
* 接收並轉換 Model 所回傳的資料並呈現在 View 之上
範例:
* 使用者是否需要先登入 (認證) 才可以看到網頁內容?
* 使用者是否只能閱讀資料,但不能修改或刪除?
* 使用者新增了資料之後,會重新導向至哪個頁面?
![MVC model](https://i.imgur.com/tpz9Zh2.png)
### ref
* [[Day 01] 什麼是MVC?能吃嗎?](https://ithelp.ithome.com.tw/articles/10191216)
* [MVC架構是什麼?認識 Model-View-Controller 軟體設計模式](https://tw.alphacamp.co/blog/mvc-model-view-controller)
## 系統開發工作區分
![full stack](https://i.imgur.com/24C05f5.png)
:::spoiler 補充:技能樹
### 前端技能樹
![front-end](https://i.imgur.com/QdqWDNm.jpg)
### 後端技能樹
![back-end](https://i.imgur.com/TsgT1zT.jpg)
:::
## 實作 MVC
:::warning
Windows(.NET framework) 跟 Mac(.Net core 5.0) 介面可能不太一樣,我是用 Mac,但有些截圖可能會用老師的 (Windows)
:::
建立 MVC 專案
:::info
如果你沒有 .NET framework,可能是沒安裝到(預設未安裝),在 Visual Studio Installer 去安裝就可以了
:::
![建立 MVC 專案](https://i.imgur.com/nXZ4sar.png)
直接執行看看,你會發現什麼都沒設定但是首頁出來了,你甚至不用設定router、port
![index](https://i.imgur.com/0Wz3t5M.png)
### 來看看他幫我們做了什麼吧
在 `Startup.cs` 裡面可以看到 router 設定,Windows 應該在 `App_start` 裡,會有類似下方語法:
![router](https://i.imgur.com/rtLjDCO.png)
`HomeController.cs`
![controller](https://i.imgur.com/J0UcOby.png)
`index.cshtml`
![view](https://i.imgur.com/6t2UNMO.png)
### layout
每一頁共用樣式,預設檔名 `_layout.cshtml`
![layout](https://i.imgur.com/e9xzXe7.png)
### 指定樣式
左圖:主版頁面、右圖子:版頁面
![customize stylesheet](https://i.imgur.com/wF1zHvc.png)
### Razor 語言
* 可以在網頁中撰寫 C#
* 副檔名是 `.cshtml` 而不是 `.html`,兩者的關係就像是先前學到的 `.php` 跟 `.html` 喔
* [使用 Razor 語法 (C#) ASP.NET Web 程式設計簡介](https://docs.microsoft.com/zh-tw/aspnet/web-pages/overview/getting-started/introducing-razor-syntax-c)
起手式
```htmlembedded=
...
@{ /* C#程式碼 */ }
...
```
### 動手做:更改 Bootstrap 版本
解決方案 :arrow_right: 管理 NuGet 套件 :arrow_right: 新增或更新 Bootstrap
![更新 NuGet 套件](https://i.imgur.com/vDvez4y.png)
更新 `_layout.cshtml`
![Layout](https://i.imgur.com/KMMA8no.png)
再回到首頁有可能版面會跑掉,因為版本不同,用法也不同,Bootstrap 5 改動滿多的,所以專案剛開始就建議確定使用的版本
:::info
Windows(.NET framework) 上預設 Bootstrap 是 3.4.1,更新到 5.1.3 差很多,會跑版的比較明顯
| Version | 3.4.1 | 5.1.3 |
| -------- | -------- | -------- |
| index | ![3.4.1](https://i.imgur.com/0vLIiHu.png) | ![5.1.3](https://i.imgur.com/fmoU0Va.png) |
:::
依據需要更新版面設定
* [Bootstrap 5.1.3 文件](https://getbootstrap.com/docs/5.1/getting-started/introduction/)
如果樣式一直沒出來,有可能是吃到快取的 css 檔,你可以暴力 `ctrl` + `F5`,或是理性清除快取,如圖:
![清除快取](https://i.imgur.com/4JsaIjw.png)
![清除快取](https://i.imgur.com/pmCq5TK.png)
### 動手做:加入後台 AdminLTE 模板
:::success
感謝老師提供魔法小卡
:::
將 AdminLTE 加入專案中,不必要的檔案可以刪掉
![不必要的檔案可以刪掉](https://i.imgur.com/aWazD11.png)
* views :arrow_right: share :arrow_right: 新增 `_LayoutAdmin.cshtml`
* 把 AdminLTE 中重複的部分抓進去,navbar、sidebar、footer 等等
* 新增 controller
* 注意命名規則 xxxController,後方 Controller 不能改
* 新增 View
* 從 Controller 中 Index() 類別右鍵 :arrow_right: 新增介面
* 悲哀地告訴你 .NET Core 可能要自己建
* 把 AdminLTE 中內容(main content)的部分抓進去
* 接著就是一步步處例相容性問題,你也可以都換成 NuGet 套件
* `_LayoutAdmin.cshtml` 更新
* bootstrap CSS
* bootstrap JS
* FontAwesome
* JQuery
* JQuery UI
* `_LayoutAdmin.cshtml` 檔案路徑,例如:
* plugins/xxx :arrow_right: ~/AdminLTE/plugins/xxx
* dist/xxx :arrow_right: ~/AdminLTE/dist/xxx
:::info
Bootstrap 5 已經不需要兼容 JQuery
:::
接下來實際執行看看,應該還會有一些兼容問題,因為 AdminTLE 是用 bootstrap 4 寫的,請自行取代掉
將不必要的 js (demo.js)拿掉
* alert
* preload
#### 切出靜態、動態(抓資料庫)區域
![靜態、動態(抓資料庫)區域](https://i.imgur.com/PcOKPBC.png)
靜態
* 例如:應用程式名稱、版本
* Web.config :arrow_right: appSetting 中定義 app 靜態參數
![WEb.config](https://i.imgur.com/gu2zAVt.png)
寫一隻類別 appService 專門用來抓 Web.config,之後只要呼叫 AppService 的屬性就能抓到
記得引入 `System.Web.Configuration`
```csharp=
using System.Web.Configuration
```
屬性:
* AppName:應用程式名稱
* AppVer:應用程式版本
* AppInfor:名稱+版本
```csharp=
/// <summary>
/// 應用程式專用類別
/// </summary>
public static class AppService{
/// <summary>
/// 應用程式名稱
/// </summary>
public static string AppName {
get {
object obj_value = WebConfigurationManager.AppSettings["AppName"];
return (obj_Value == null)? "" : obj_value.ToString();
}
}
/// <summary>
/// 應用程式版本
/// </summary>
public static string AppVer {
get {
object obj_value = WebConfigurationManager.AppSettings["AppVer"];
return (obj_Value == null)? "" : obj_value.ToString();
}
}
/// <summary>
/// 應用程式資訊
/// </summary>
public static string AppInfo {
get {
return $"{AppName} {AppVer}";
}
}
}
```
注意:
* 建議不要用 namespace,別的專案有需要也可以直接 copy 去用
* 建議用 static 靜態類別,不用實作化(new),比較方便
* 設定唯讀,只能 get 不能 set
* 利用 `object` 去抓而不是 `string` 比較好判斷 `null` 狀況
* c# 中字串縮寫的方法 `$"名字:{name}"`
再套到 layout(.cshtml) 的 navbar 上
```htmlembedded=
@AppService.AppInfo
```
還有 footer company info、copyright 等等資訊可以用 Web.config 設定,減少寫死在網頁(html)中
動態
先建立資料庫
:::spoiler 欄位建議
使用者
| 資料行名稱 | 型別 | 允許 Null | 欄位說明 |
| ------------- | ------------- | --------- | -------------------- |
| rowid | int | X | |
| is_valid | bit | V | 帳戶是否驗證過(T/F) |
| mno | nvarchar(50) | V | 員工編號 |
| mname | nvarchar(50) | V | 員工姓名 |
| pwd_user | nvarchar(50) | V | |
| role_no | nvarchar(50) | V | 使用者/管理者/廠商 |
| code_gender | nvarchar(50) | V | 性別 |
| department_no | nvarchar(50) | V | 部門編號 |
| title_no | nvarchar(50) | V | 職稱 |
| date_onboard | date | V | 到職日 |
| date_leave | date | V | 離職日 |
| email_contact | nvarchar(50) | V | 聯絡信箱 |
| tel_contact | nvarchar(50) | V | 聯絡電話 |
| addr_contact | nvarchar(250) | V | 聯絡地址 |
| code_valid | nvarchar(50) | V | 驗證碼 |
| remark | nvarchar(250) | V | 備註 |
部門
| 資料行名稱 | 型別 | 允許 Null |
| ---------- | ------------ | --------- |
| rowid | int | X |
| mno | nvarchar(50) | V |
| mname | nvarchar(50) | V |
| remark | nvarchar(50) | V |
職稱
| 資料行名稱 | 型別 | 允許 Null |
| ---------- | ------------ | --------- |
| rowid | int | X |
| mno | nvarchar(50) | V |
| mname | nvarchar(50) | V |
| remark | nvarchar(50) | V |
角色
| 資料行名稱 | 型別 | 允許 Null |
| ---------- | ------------ | --------- |
| rowid | int | X |
| mno | nvarchar(50) | V |
| mname | nvarchar(50) | V |
| remark | nvarchar(50) | V |
:::
實務上不會用sa帳號,權限太高,建議額外建立使用者
* 安全性 :arrow_right: 登入 :arrow_right: 新增
1. 一般:使用者名稱,建議就取名跟資料庫一樣,好記XDD
1. 伺服器角色:public
1. 使用者對應:指定資料庫:mvcdemo、權限:db_owner
1. 實體對應:預設
1. 狀態:預設
用 enity framework 將資料庫匯入到專案中,就可以用操作類別的方法操作資料庫
建議命名:
* 資料庫名稱+Model,例如 mvcdemoModel
* 但如果你的資料庫只有一個,也可以直接叫 dbModel,
* 建立時記得 enities、model 時也改成 dbEnities、dbModel 方便管理
:::info
MySQL 記得先下載 MySQL for .NET framework driver
:::
新增類別 UserService,記得引入資料庫的 Model
```csharp=
using mvcdemo.Models;
```
屬性:
* UserNo
* UserName
* UserInfo
* Role
* IsLogin
```csharp=
/// <summary>
/// 使用者類別
/// </summary>
public static class UserScervice{
}
public static string UserNo { get; set; }
public static string UserName { get; set; }
public static enRole Role { get; set; }
public static bool IsLogin { get; set; }
public static string UserInfo
{
get
{
enumClass enclass = new enClass();
string str_role_name = enclass.getRoleName(Role);
return $"{UserNo} {UserName} {str_role_name}";
}
}
/// <summary>
/// 登入系統
/// <param name="userNo">帳號</param>
/// <param name="userPwd">密碼</param>
/// </summary>
public static bool Login(string userNo, string userPwd)
{
using(dbEntities db = new dbEntities())
{
Logout();
// 比對輸入資料與資料庫內資料是否相同
var data = db.Users
.Where(m => m.mno == userNo && m.pwd_user == userPwd)
.FirstOrDefault();
// 帳號或密碼錯誤(亦即資料庫中沒有這筆結果)
if(data == null) return false;
// 登入成功,更改、取得屬性
IsLogin = true;
UserNo = data.mno;
UserName = data.mname;
enumClass enclass = new enumClass(); // 沒有設定解構子所以只能用實體化不能直接用 using
Role = enclass.GetRole(data.enRole);
return true;
}
}
/// <summary>
/// 登出系統
/// </summary>
public static void Logout()
{
IsLogin = false;
UserNo = "";
UserName = "";
Role = enRole.Guest;
}
```
:::info
上述35行
* 有運用到 C# 的 [Lambda](https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/operators/lambda-expressions)
* 簡單可理解為把 Users 這個資料庫丟給 m,有點像變數,讓變數 m 可以抓 Users 的欄位
上述36行
* `.FirstOrDefault()` 表示有符合的話給我第一筆資料,否則抓 default (就是 `null`)
:::
角色枚舉類型(enRole.cs)
```csharp=
/// <summary>
/// 角色枚舉類型
/// </summary>
publin enum enRole {
Guest = 0,
User = 1,
Mis = 2,
Vender = 3
}
/// <summary>
/// 枚舉類型的類別
/// </summary>
public partial class enumClass {
/// <summary>
/// 取得角色名稱
/// <parem name="role">角色</param>
/// </summary>
public string GetRoleName(enRole role){
string str_value = "undifine";
if(role == enRole.Guest) str_vlaue = "訪客";
if(role == enRole.User) str_vlaue = "使用者";
if(role == enRole.Mis) str_vlaue = "管理員";
if(role == enRole.Vender) str_vlaue = "廠商";
return str_value;
}
/// <summary>
/// 取得角色
/// <parem name="role">角色</param>
/// </summary>
public enRole GetRole(string role)
{
enRole en_value = enRole.Guest;
if(role == "Guest") en_vlaue = enRole.Guest;
if(role == "User") en_vlaue = enRole.User;
if(role == "Mis") en_vlaue = enRole.Mis;
if(role == "Vender") en_vlaue = enRole.Vender;
return en_value;
}
}
```
#### 新增使用者的 controller
登入頁通常不會有 header、footer,再自訂一個 layoutLite(.cshtml)
> :arrow_down_small: _layoutLite.cshtml
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>@AppService.AppInfo</title>
<link href="~/Content/bootstrap.min.css" rel="stylesheet" />
<link href="~/Content/Site.css" rel="stylesheet" />
<script src="~/Scripts/modernizr-2.8.3.js"></script>
<script src="~/Scripts/jquery-3.6.0.min.js"></script>
<script src="~/Scripts/bootstrap.min.js"></script>
@RenderSection("styles", required: false)
@RenderSection("scripts", required: false)
</head>
<body>
<div class="container body-content">
@RenderBody()
</div>
</body>
</html>
```
登入事件
![http](https://i.imgur.com/a33RGna.png)
> :arrow_down_small: UserController.cs,再一步一步慢慢改
```csharp=
public class UserController : Controller
{
[HttpGet]
public ActionResult Login
{
return View();
}
[HttpPost]
public ActionResult Login(Users model)
{
return View();
}
}
```
要多少欄位拿多少,在 Model 裡面再建立專門給 view 的 ViewModel (本例叫 vmLogin),建立自己要的:
* 結構(欄位)
* 標題
* Display,要引入 `System.ComponentModel.DataAnnotations`
* 打 Display 讓系統自動載入就好
* ![using](https://i.imgur.com/mbSyTqF.png)
* 驗證方式
* Required
* 其他
* DataType
> :arrow_down_small: Model/ViewModel/vmLogin.cs
```csharp=
public class vmLogin
{
[Display(Name = "帳號")]
[Required(ErrorMessage = "必填")]
public string UserNo { set; get; }
[Display(Name = "密碼")]
[Required(ErrorMessage = "必填")]
[DataType(DataType.Password)]
public string Pwd { set; get; }
}
```
改成以下,特別留意第 11 行,Model 從 User 改成自訂的 vmLogin
> :arrow_down_small: UserController.cs
```csharp=
public class UserController : Controller
{
[HttpGet]
public ActionResult Login
{
vmLogin model = new vmLogin();
return View(model);
}
[HttpPost]
public ActionResult Login(vmLogin model)
{
return View();
}
}
```
#### 產生 view
![產生 view](https://i.imgur.com/Z3CQz0b.png)
選擇 MVC5
![MVC5](https://i.imgur.com/OuIgoyQ.png)
記得用 layoutLite
![layoutLite](https://i.imgur.com/0ZCVFcg.png)
> :arrow_down_small: login.cshtml
```htmlembedded=
@model vmLogin
@{
ViewBag.Title = "Login";
Layout = "~/Views/Shared/_LayoutLite.cshtml";
}
<h2>Login</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>vmLogin</h4>
<hr/>
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="form-group">
@Html.LabelFor(model => model.UserNo, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.UserNo, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserNo, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</div>
</div>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
```
> :arrow_down_small: UserController.cs
```csharp=
public class UserController : Controller
{
[HttpGet]
public ActionResult Login
{
vmLogin model = new vmLogin();
return View(model);
}
[HttpPost]
// model 是使用者輸入的資料
public ActionResult Login(vmLogin model)
{
// 依據 vmLogin.cs 中的驗證,本例中只有 require 兩欄必填,
// 不符合的話 return View(model),你填什麼(View(model) 的 model) 我就給你什麼,白話文:欄位不會清空
if(!ModelState.IsValid) return View(model);
// 去資料庫中驗證資料是否正確,判斷寫在 Model
if(!UserService.Login(model.UserNo, model.Pwd))
{
// 錯誤訊息,參數("顯示欄位", "錯誤訊息")
ModelState.AddModelError("UserNo", "帳號或密碼錯誤");
// 不會清空欄位,原理同上14行
return View(model);
}
// 登入成功,頁面導向後台首頁
return RedirectToAction("Index", "Admin");
}
}
```
改 layoutAdmin 中使用者姓名
```htmlembedded=
@UserService.UserInfo
```
可以執行看看囉
老師有提供幾隻常用的類別,該改的欄位要改,重構
資料夾架構
![資料夾架構](https://i.imgur.com/SdXh2LV.png)
GmailService
擴充 AppService 功能
資料庫連線方式
* Entity
* SQL Server
* Docker
資料庫欄位加減密
base 解構子,讓其他地方都可以直接 using,不用再實體化
運用 [repository pattern](https://ithelp.ithome.com.tw/articles/10157484) 降低耦合度
簡單來說是抽離 controller 對資料庫的依賴性,如果日後資料庫換家,譬如 oracle DB,程式不用重寫
其實本質上就是 CRUD
有幾張資料表,就建立幾個類別
repoDepartments
```csharp=
using mvcdemo.Models;
```
## 實作註冊、登入
流程
註冊 :arrow_right: 驗證 :arrow_right: 註冊頁面 :arrow_right: 登入忘記密碼
加密(加鹽)
* 可逆
* 不可逆
```htmlembedded=
@Html.ActionLink("","","","","")
```
## Deploy
方式
* 本機
* 遠端
* 雲端
### 安裝 IIS(Internet Information Services) 伺服器
控制台 :arrow_right: 程式和功能 :arrow_right: 開啟或關閉 Windows 功能
安裝口訣:
* .NET framework
* IIS
* 除了 FTP Server
確認:
系統管理工具 :arrow_right: Internet Information Services(IIS) 管理員