# MVC 學習筆記 ## MVC 基本介紹 * Model:負責定義資料格式與資料存取的介面,包括商業邏輯與資料驗證。 * View:負責使用者介面相關呈現,包括輸入與輸出。 * Controller:負責控制系統運行的流程、跟瀏覽器如何互動、決定網頁操作的流程與動線、回應用戶端的各種要求、錯誤處理等。 ## Hello World [微軟 開始使用 ASP.NET MVC 5](https://docs.microsoft.com/zh-tw/aspnet/mvc/overview/getting-started/introduction/getting-started) [偵錯、追蹤和程式碼剖析](https://docs.microsoft.com/zh-tw/dotnet/framework/debug-trace-profile/) [Entity Framework Core](https://docs.microsoft.com/zh-tw/ef/core/) ## 論壇 [ASP.NET MVC 論壇](https://social.msdn.microsoft.com/Forums/windowsdesktop/zh-TW/home?forum=aspnetmvces&filter=alllanguages) [Channel9](https://channel9.msdn.com/) [Apache](https://blogs.apache.org/) ## 目錄介紹 * 首頁 * /Controller/HomeController.cs(程式碼) * /Views/Home/Index.aspx(頁面) * 關於 * /Controller/HomeController.cs(程式碼) * /Views/Home/About.aspx(頁面) 目錄介紹 * App_Start * BundleConfig:定義CSS/JS打包壓縮的規則 * FilterConfig:全域 Action Filter 定義的地方 * RouteConfig:定義網址路由 (Routing) 的地方 * Content:放置網站內所有靜態內容 * Controllers:放置`ASP.NET MVC`控制器 * Models:所有`ASP.NET MVC`與Models有關的程式碼,例如:EDMX、DBML * Scripts:所有`Javascript`、`VBScript`指令檔存放位址 * Views:放置`ASP.NET MVC`檢視 * Home:放置HomeController所屬檢視的目錄 * Shared:放置全站共用檢視的目錄,可能放式Layout頁面或Partial View或自訂預設錯誤頁面等 ## Config * 主板設定:/Views/_Layout.cshtml [ASP.NET Configuration Files](https://docs.microsoft.com/zh-tw/previous-versions/aspnet/ms178684(v=vs.100)) ## 常用類別 如果有使用心得會另外加入 * [System.Data.Entity](https://docs.microsoft.com/zh-tw/dotnet/api/system.data.entity?view=entity-framework-6.2.0) * [DbSet`<TEntity>` Class](https://docs.microsoft.com/zh-tw/dotnet/api/system.data.entity.dbset-1?view=entity-framework-6.2.0) ### Controller * [System.Web.Mvc](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc?view=aspnet-mvc-5.2) * [ActionResult](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.actionresult?view=aspnet-mvc-5.2):表示動作方法的結果 * [WebViewPage](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.webviewpage?view=aspnet-mvc-5.2):表示為了呈現使用 ASP.NET Razor 語法的檢視而必須使用的屬性和方法 * [ActionNameAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.actionnameattribute?view=aspnet-mvc-5.2) * ActionName:表示用於動作名稱的屬性 * [NonActionAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.nonactionattribute?view=aspnet-mvc-5.2) * NonAction:不對外公開 #### HttpAttribute * System.Web.Mvc * [HttpGetAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.httpgetattribute?view=aspnet-mvc-5.2): * HttpGet:表示用於限制動作方法只處理 HTTP GET 要求的屬性 * [HttpPostAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.httppostattribute?view=aspnet-mvc-5.2) * HttpPost:表示用於限制動作方法只處理 HTTP POST 要求的屬性 * 依序還有Delete、Put、Head、Options、Patch #### ActionResult * System.Web.Mvc * [ActionResult](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.actionresult?view=aspnet-mvc-5.2) * [ContentResult](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.contentresult?view=aspnet-mvc-5.2):回傳一個使用者自訂的文字內容 * [EmptyResult](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.emptyresult?view=aspnet-mvc-5.2):不回應任何資料到客戶端 * [FileResult](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.fileresult?view=aspnet-mvc-5.2):以二進位串流的方式回傳一個檔案資料 * [FileContentResult](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.filecontentresult?view=aspnet-mvc-5.2):直接輸出`byte[]`內容 * [FilePathResult](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.filepathresult?view=aspnet-mvc-5.2):指定檔案路徑輸出檔案內容 * [FileStreamResult](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.filestreamresult?view=aspnet-mvc-5.2&viewFallbackFrom=aspnet-webpages-3.2):指定Stream物件回傳其內容 * [HttpStatusCodeResult](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.httpstatuscoderesult?view=aspnet-mvc-5.2):回傳自訂的HTTP狀態碼與訊息 * [HttpNotFoundResult](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.httpnotfoundresult?view=aspnet-mvc-5.2):回傳`HTTP 404`狀態碼 * [HttpUnauthorizedResult](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.httpunauthorizedresult?view=aspnet-mvc-5.2&viewFallbackFrom=aspnet-webpages-3.2):回傳`HTTP 401`狀態碼 * [JavaScriptResult](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.javascriptresult?view=aspnet-mvc-5.2):將資料序列化成JSON格式回傳 * [RedirectResult](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.redirectresult?view=aspnet-mvc-5.2):重新導向到指定的URL * [RedirectToRouteResult](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.redirecttorouteresult?view=aspnet-mvc-5.2):與RedirectResult類似,但是它是重導向到一個Action或Route * [ViewResultBase](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.viewresultbase?view=aspnet-mvc-5.2):回傳一個View頁面 * [ViewResult](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.viewresult?view=aspnet-mvc-5.2):回檢視頁面(View Page) * [PartialViewResult](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.partialviewresult?view=aspnet-mvc-5.2):回傳部分檢視頁面(Partial View) #### 傳遞物件 * System.Web.Mvc * [ControllerBase](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.controllerbase?view=aspnet-mvc-5.2) * [TempData](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.controllerbase.tempdata?view=aspnet-mvc-5.2#System_Web_Mvc_ControllerBase_TempData):`TempDataDictionary`型別,內部用Session來儲存資料,只會存在一次網頁要求 * [ViewData](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.controllerbase.viewdata?view=aspnet-mvc-5.2#System_Web_Mvc_ControllerBase_ViewData):`ViewDataDictionary<string,object>`物件,只會存在這次的HTTP要求中 * [ViewBag](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.controllerbase.viewbag?view=aspnet-mvc-5.2#System_Web_Mvc_ControllerBase_ViewBag) ### View * [System.Web.Mvc.Html](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.html?view=aspnet-mvc-5.2) * [DisplayExtensions](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.html.displayextensions?view=aspnet-mvc-5.2):表示支援以 HTML 呈現物件值 * Display * [LinkExtensions](https://docs.microsoft.com/zh-tw/dotnet/api/system.web.mvc.html.linkextensions?view=aspnet-mvc-5.2):表示應用程式中的 HTML 連結支援 * ActionLink * RouteLink * [FormExtensions](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.html.formextensions?view=aspnet-mvc-5.2):表示應用程式中的 HTML 支援 * BeginForm:將會輸出`<form>`標籤,必須用using包起來 * BeginRouteForm * EndForm * [LabelExtensions](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.html.labelextensions?view=aspnet-mvc-5.2):表示 ASP.NET MVC 檢視中的 HTML label 項目支援 * Label * LabelFor * LabelForModel * [EditorExtensions](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.html.editorextensions?view=aspnet-mvc-5.2):表示應用程式中的 HTML input 項目支援 * Editor * EditorFor * EditorForModel * [ValidationExtensions](https://docs.microsoft.com/en-us/dotnet/api/system.web.mvc.html.validationextensions?view=aspnet-mvc-5.2):提供支援,以驗證 HTML 表單的輸入內容 * Validate * ValidateFor * ValidationMessage * ValidationMessageFor * ValidationSummary:當表單欄位發生驗證失敗時,顯示的錯誤訊息 ### Model 允許Null:在型別宣告後面加? `RegukarExpression`可用`Metadata`替代 * [System.ComponentModel](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel?view=netcore-3.1):命名空間提供類別,用來實作元件和控制項的執行階段和設計階段行為。 此命名空間包含基底類別和介面,以便實作屬性和類型轉換器、繫結至資料來源,以及授權元件 * [DisplayNameAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.displaynameattribute?view=netcore-3.1):指定不使用引數之屬性、事件或 public void 方法的顯示名稱 * [DisplayName](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.displaynameattribute.displayname?view=netcore-3.1#System_ComponentModel_DisplayNameAttribute_DisplayName):取得不使用這個屬性 (Attribute) 中所儲存引數之屬性 (Property)、事件或 public void 方法的顯示名稱 * [DataAnnotations](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations?view=netcore-3.1) * [Schema](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations.schema?view=netcore-3.1) * [DatabaseGeneratedAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations.schema.databasegeneratedattribute?view=netcore-3.1) * DatabaseGenerated:預設值 * [RequiredAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations.requiredattribute?view=netcore-3.1) * Required:用戶端與伺服器驗證設定,指定資料欄位值為必要 [ex.](https://github.com/dotnet/AspNetDocs/blob/master/aspnet/mvc/overview/getting-started/introduction/sample/MvcMovie/MvcMovie/Models/AccountViewModels.cs#L8) * [KeyAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations.keyattribute?view=netcore-3.1) * Key:primary key * [MaxLengthAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations.maxlengthattribute?view=netcore-3.1) * MaxLength(n):設定欄位的字串長度 * [NotMappedAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations.schema.notmappedattribute?view=netcore-3.1) * NotMapped:排除在Entity Framework自對對應的欄位 * [StringLengthAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations.stringlengthattribute?view=netcore-3.1) * StringLength:字串欄位所允許的最大長度 * [RegularExpressionAttribute ](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations.regularexpressionattribute?view=netcore-3.1) * RegularExpression:欄位內容必須符合所指定的規則運算式 * [RangeAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations.rangeattribute?view=netcore-3.1) * Range:數字欄位必須符合的範圍 * [CustomValidationAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations.customvalidationattribute?view=netcore-3.1) * CustomValidation:自訂欄位驗證規則 * [MetadataTypeAttribute](https://docs.microsoft.com/zh-tw/dotnet/api/system.componentmodel.dataannotations.metadatatypeattribute?view=netcore-3.1) * MetadataType:指定與資料模型類別產生關聯的中繼資料類別 #### 資料庫關聯 virtual: 使用`ICollection`進行一對多或多對多關聯 ``` public class Dept { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<staff> Staffs { get; set; } } public class Staff { public int Id { get; set; } public string Name { get; set; } public virtual staff Staff { get; set; } } ``` 使用一對一關聯 ``` public class Staff { public int Id { get; set; } public string Name { get; set; } public virtual StaffDetail StaffDetail { get; set; } } public class StaffDetail { public int Id { get; set; } public string Name { get; set; } public Staff Staff { get; set; } } // 主從架構 protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Entity<Staff>().HasRequired(a => a.StaffDetail) .WithOptional(b => b.Staff); } ``` [ASP.NET MVC - Code First - Model層設計(一對一、一對多、多對多)](https://dotblogs.com.tw/kevin_blog/2018/01/31/143252) ## 注意事項 1. 專安類型最好使用`Web應用程式專案(Web Application Project)` 2. 每當程式修改後要先手動建置專案才會將修正的結果編譯進bin目錄下的組件 3. 使用資料`模型繫結(Model Binder)`來使用Request和Response物件 4. 不要在`View`撰寫過多程式邏輯 ## 使用EF資料庫移轉 [在 ASP.NET MVC 應用程式中使用 EF 遷移並部署至 Azure](https://docs.microsoft.com/zh-tw/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/migrations-and-deployment-with-the-entity-framework-in-an-asp-net-mvc-application) ``` enable-migrations add-migration InitialCreate update-database // 回滾 update-database -TargetMigration:{add-migration名稱} // 產生最近一次差異的SQL Update-Database // 產生差異Sql(1)到(2)版的差異SQL Update-Database -Script -SourceMigration:(1) -TargetMigration:(2) // 回復到初始狀態 Update-Database -TargetMigration: $InitialDatabase -force ``` ### multiple Context #### Enable-Migrations ``` Enable-Migrations -EnableAutomaticMigrations -ContextTypeName NamespaceOfContext.ContextA -ProjectName ProjectContextIsInIfNotMainOne -StartupProjectName NameOfMainProject -ConnectionStringName ContextA 建立完成後將Configuration.cs重命名。 再執行下一個Enable-Migrations add-migration InitialBSchema -IgnoreChanges -ConfigurationTypeName ConfigurationA -ProjectName ProjectContextIsInIfNotMainOne -StartupProjectName NameOfMainProject -ConnectionStringName ContextA Update-Database -ConfigurationTypeName ConfigurationA -ProjectName ProjectContextIsInIfNotMainOne -StartupProjectName NameOfMainProject -ConnectionStringName ContextA ``` #### add-migration ``` Add-Migration MYMIGRATION -ConfigurationTypeName ConfigurationA -ProjectName ProjectContextIsInIfNotMainOne -StartupProjectName NameOfMainProject -ConnectionStringName ContextA ``` #### update-database ``` Update-Database -ConfigurationTypeName ConfigurationA -ProjectName ProjectContextIsInIfNotMainOne -StartupProjectName NameOfMainProject -ConnectionStringName ContextA ``` [How do I enable EF migrations for multiple contexts to separate databases? ](https://stackoverflow.com/questions/13469881/how-do-i-enable-ef-migrations-for-multiple-contexts-to-separate-databases) ## 流程圖 ### 用戶端對`ASP.NET`網站進行要求的流程圖 ```mermaid graph TD; 用戶要求-->UrlRouting; UrlRouting-->Route; Route-->RouteHandler; RouteHandler-->HttpHandler; ``` ### `ASP.NET MVC`的執行生命週期 ```mermaid graph TD; Request-->UrlRouting; UrlRouting-->Route; Route-->RouteHandler; RouteHandler-->HttpHandler; HttpHandler-->ControllerFactory; ControllerFactory-->Controller; Controller-->ViewFactory; ViewFactory-->View; View-->Response; ``` ## 測試架構 1. Unit Test 2. NUnit 3. Visual Studio ## 開發順序 我認為`MVC`開發順序應該是 Model>Controller>View 這是我參考Will保哥的想法 > `M(Model)`是MVC架構的中心,先有`Model`之後,就可以讓`Controller`與`View`參考這些`Model`,先定義出所有計畫開發的`Controller`與`Action`,然後再建立所有`Action`對應的`View`(無內容的`View`),之後就可以將不同單元的`Controller`的`View`分工開發,最後再進行整合即可。 > > (引用文獻 `ASP.NET MVC 4 開發實戰 第35頁`) ## `ASP.NET` vs `ASP.NET MVC` MVC 的優點 1. 清楚的關注點分離,強迫你寫出比`Web Forms`更容易維護的程式。 2. 開放特行(Open Source)。 3. 社群支持。 4. 優秀的開發效率。 5. 易於測試的架構。 6. 易於分工的架構。 MVC 的缺點 1. 開發人員比須面對`HTML`、`CSS`與`JavaScript`在`View`頁面上的配置。 ## 使用的`ASP.NET`架構 ```mermaid graph TD; MVC-->Sites; WebPages-->Sites; WebForms-->Sites; SinglePages-->Sites; WebAPI-->Servers; SignalR-->Servers; Sites-->ASP.NET; Servers-->ASP.NET; ``` ## 框架 1. [ORM框架](https://zh.wikipedia.org/wiki/%E5%AF%B9%E8%B1%A1%E5%85%B3%E7%B3%BB%E6%98%A0%E5%B0%84) ## 技術 1. Membership 2. Profile 3. SiteMap 4. Web Caching 5. Session 6. Authentication 7. [Unobtrusive](https://zh.wikipedia.org/wiki/%E9%9D%9E%E4%BE%B5%E5%85%A5%E5%BC%8FJavaScript) 8. [JQuery](https://api.jquery.com/) ## OpenSource [Code Plex : ASP.NET](https://archive.codeplex.com/?p=aspnet) [AspNetDocs Code](https://github.com/dotnet/AspNetDocs/tree/master/aspnet/mvc/overview/getting-started/introduction/sample/MvcMovie/MvcMovie) ## 工具 1. Reflector 2. [Appcmd](https://zh.wikipedia.org/wiki/%E7%B6%B2%E9%9A%9B%E7%B6%B2%E8%B7%AF%E8%B3%87%E8%A8%8A%E6%9C%8D%E5%8B%99#IIS_Express) 3. [CodeMaid](https://marketplace.visualstudio.com/items?itemName=SteveCadwallader.CodeMaid) 清理程式碼 4. [Fortify](https://en.wikipedia.org/wiki/Fortify_Software) 5. wget 6. Aspnet_regiis ### Aspnet_regiis [組態檔案加密](https://ithelp.ithome.com.tw/articles/10188072) ### wget ``` wget -c -nH -np -m -P 儲存地址 url ``` ### Appcmd #### 顯示已經註冊到 `IIS Express` 的站台列表 ``` PS C:\Users\user\source\repos> appcmd list site SITE "WebSite1" (id:1,bindings:http/:8080:localhost,state:Unknown) ``` #### 查看 `IIS Express` 的 `Log` 訊息 ``` PS C:\Users\user\source\repos> iisexpress /siteid:1 Starting IIS Express ... Successfully registered URL "http://localhost:8080/" for site "WebSite1" application "/" Registration completed for site "WebSite1" IIS Express is running. Enter 'Q' to stop IIS Express ``` 從VS2015 新增的網站專案都會多一個.vs 目錄,裡面會存放此專案的 applicationHost.config 組態檔。 可以用以下語法替代。 ``` iisexpress /config:{你的專案}\.vs\config\applicationhost.config /siteid:{id} ``` [VS2015的IISEXPRESS 10的APPLICATIONHOST.CONFIG置叨位](https://blog.kkbruce.net/2015/07/where-vs2015-iisexpress-10-applicationhostconfig.html#.Vsm6HZx96M8) #### IIS Express Config `C:\users\user\Documents\IISExpress\config` #### 讓 IIS Express 支援外部連結 1. 開啟`config`裡的`applicationhost.config` 2. 找到`<site>`標籤,找到想支援外部連結的`Application` 3. 將後阜號後方的`localhost`修改為`*`星號 ``` 開始指令 netsh http add urlacl url=http://*:{阜號}/ user=everyone 結束指令 netsh http delete urlacl url=http://*:{阜號}/ ``` ## 建議擴充套件 1. NuGet Package Manager 2. Web Essentials 3. VSCommands for Visual Studio 4. Sample Browser Visual Studio Extension 5. JQuery 6. JQuery UI 7. [NLog](https://github.com/NLog/NLog) 8. `Json.NET` 9. Entity Framework 10. ELMAH 11. [X.PagedList](https://github.com/dncuug/X.PagedList) 12. [Spire.Doc (Word)](https://www.e-iceblue.com/Introduce/word-for-net-introduce.html) 13. [NPOI (Excel)](https://github.com/nissl-lab/npoi) ## SQL SERVER ### 版本升級 [如何下載與升級 SQL Server Express LocalDB 到最新版](https://blog.miniasp.com/post/2020/02/16/install-and-upgrade-sql-server-express-localdb)