--- tags: ASP․NET Core 3 開發實戰:從入門到進階 (台北) --- # Day03 20191201 ASP․NET Core 3 開發實戰:從入門到進階 (台北) # EntityFramework Core :::warning EF 效能會有問題嗎? ::: 只要你的語法沒有寫錯,效能可以很好 但資料超級多的情況,還是不要都用 .net處理資料,該放sql處理的還是要寫寫store procedure ## 什麼是模型 - 描述資料用的方式 - 英文叫做model(抽象的概念) ex:csv,class,xml ## 什麼是Entity - 英文翻譯是:實體、公司 (抽象的概念) - 概念有點像是描述資料的定義變成一個實體的一個資料 ## 什麼是EntityFramework - EntityFramework 是ORM的一個實作 - 底層就是ADO .NET - 拿到資料後會把資料實體化(materialization)變成object - 資料模型就是Class型別,以強型別對資料操作 優點: - 可以在開發時期減少對程式碼的撰寫 - 可以透過Linq語法 ## 什麼是EntityFramework Core - 全新的框架,EntityFramework6功能還是比較強 - 可以去[github](https://github.com/aspnet/EntityFrameworkCore/issues)追蹤issue,目前就有幾百個希望追加功能的請求 - EF6支援.net core但只支援跑在windows平台,沒有跨平台 ## 關於ORM與DDD - ORM管理資料庫的關聯,只能管理基本的關聯,因為資料庫的關聯可以非常複雜 - DDD試圖做到將程式碼與DB正規化脫鉤不被綁架,貼近真實需求 - 如果依照正規化的資料結構去寫程式,可能你無法直接看出需求是什麼 - 以DDD的方式開發,程式會貼近需求,易懂&維護 ## 新手上路 - EF Core 3.0 可以有無pk的table/view,相比2.X版還修正了很多pk誤判的問題 - 用EF Core Power Tools 可以幫你下powershell命令,以db first方式產model&db context(windows限定) - 而且是partial class,可以另外搭設定擴充 - 寫appsetting.json的連線字串跟DI注入ef core時可以切到vscode用code snippet比較快 - json裡面打`conn` - windows用visual studio的話可以選EF環境的控制器/API控制器新增,就會自動給CRUD,若MAC環境的話就用CLI,參考講義內的語法 - `dotnet tool list -g` 列出所有安裝好的dotnet工具 - Visual Studio 2019 - 建立 ASP․NET Core Web API 專案 - 使用 [EF Core Power Tools](https://marketplace.visualstudio.com/items?itemName=ErikEJ.EFCorePowerTools) 產生必要的實體模型類別 - 新增資料連線 - 勾選 EF Core 3.0 - 勾選 Tables - 設定輸出選項 - 設定 `Startup.cs` 的 `ConfigureService` 完成 `ContosoUniversityContext` 註冊 DI - 設定 `appsetting.json` - 新增控制器 - **使用 Entity Framework 執行動作的 API 控制器** - macOS / bash ```shell= dotnet new webapi -n api1 cd api1 code . dotnet add package Microsoft.EntityFrameworkCore.Design dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design dotnet ef dbcontext scaffold "Server=(localdb)\MSSQLLocalDB;Database=ContosoUniversity;Trusted_Connection=True;MultipleActiveResultSets=true" Microsoft.EntityFrameworkCore.SqlServer -o Models dotnet aspnet-codegenerator controller -name DepartmentsController -async -api -dc ContosoUniversityContext -m Department -outDir Controllers ``` 在建立好所有 API 控制器之後,還需要手動調整 `Startup.cs` 與 `appsettings.json` 才能執行! ## Migrations WorkFlow :::info 情境: 公司有兩個人,在個別的電腦各做一件事情(通常用EF不會共用資料庫,每個人有自己的DB),A做建一格欄位,建一個Migrations,B做建一格欄位,建一個Migrations,彼此不知道,,這時A、B的Migrations各有兩筆紀錄,接者a把b的code合併回來Migrations會為3筆紀錄,合併code完第一步先執行,database update ::: ## 資料庫移轉與初始化 - `dotnet ef migrations add <MigrationName>` - **`-v`** 加上後可以看CLI背後的LOG - `<MigrationName>`是會寫到`__EFMigrationsHistory資料表`內的識別名稱,可以寫下這次變更的名稱如AddCreateDate之類的 - 前綴的日期時間是DateTime.UtcNow,不怕時區問題 - `dotnet ef database update 0`的0版號是特殊字,可以從無產生 - Migrations資料夾內會產出每次更新的內容差異檔,有這個檔案就可以將DB版控與程式碼關連 ## 認識__EFMigrationsHistory資料表 - 當多人協作場景時,各自有自己的資料庫做開發,所以各自有各自的__EFMigrationsHistory資料表,在版控合併後做update時,會根據這個紀錄自動產出差異的TSQL去更新DB - product version是ef的版本 ## :memo:練習 EF Core 的 Database First 開發流程 - 這裡是練習DB first轉向code first,除了第一次要動DB以外,之後都以程式為主,所以實務上最好控制人員不要直接動DB,都以程式去修改DB - 第一版migration時,以DB的schema為主,建立好Migrations/....差異檔後要砍掉up & down內容,因為migration是不跟db關聯,而是跟自己model比,所以up 會產出要建立table的程式,但因為db已經有table,不需要這些,而down也要移除不然降板時會把資料砍掉 - ![](https://i.imgur.com/7hwv2pX.png) - 異動資料欄位:調整model - 異動完更新:再次按migration tool,產差異檔,更新DB ## 基礎EF Core 資料操作 ## :memo:練習用contoso建立course CRUD API&用postman測試 - 建立WEB API專案 - 建立contosouniversity dbcontext - 對course建CRUD - 另外建一個課程查詢資料API篩選課程名稱有"git" - 建完API用postman建collection將每個API都丟上去 - postman SSL驗證問題 - asp .net core web應用有HTTPS,加入信任後在瀏覽器就有安全鎖頭 - 但POSTMAN預設會自己驗證SSL,要在設定內 (File>Settings) 關掉不然local的SSL他不會信任 - (記得打api要用https&5001 port) ![](https://i.imgur.com/IWqWLCY.png) - EF Core AddAsync: ![](https://i.imgur.com/q9dvtaq.png) - 一個Get範例 ![](https://i.imgur.com/QRCiP7g.png) - 一個Post範例 (Raw with JSON) ![](https://i.imgur.com/ONKQzG0.png) ## 與實體關聯有關的名詞 - 如果db那邊不拉關聯,scafold出來的model不會自動加入關聯導覽屬性,但是可以自己加入關聯在欄位上,在查詢時跟DB沒有關係,最終產出SQL執行時才有關係,所以自己加導覽後去查詢,產出的SQL跟有拉關聯的應該會一樣,但DB上面最好還是建關聯保持資料正確性 - 改debug的log level,讓ef底下產出的sql可見: - 調整appsettings.Development.json中的`Microsoft`值為`Debug` - ![](https://i.imgur.com/3UeZK8J.png) - ![](https://i.imgur.com/UAF2sPF.png) - 使用 Include 時,遇到循環參考 ( JsonException ) 怎麼辦? ![](https://i.imgur.com/ucQaOyR.png) - 可以在 "一對多" 的主體實體 - 相依導覽屬性上方 加上 `[JsonIgnore]` 避免其循環往下序列化 - 注意 `[JsonIgnore]` 必須引入 using System.Text.Json.Serialization; 而非 using Newtonsoft.Json; ## EF執行生命週期 - 可以直接搜尋`entity.HasNoKey();`看到沒有key的entity mapping ## 效能調教技巧 - ef core 2.2時有一個特性,where條件若是provider看不懂的,會先忽略,然後以排除該條件的查詢去抓資料回來後再去做查詢,而效能很差被罵翻 - 3.0後就移除該特性了 - 效能調教基本上是你寫道發現慢了再去調就好了 EntityFramework Reverse POCO Code First Generator https://www.reversepoco.co.uk/ ## 20191207&1208回家作業 - EF CORE執行SP ```CSharp await _context.Database.ExecuteSqlRawAsync(@"dbo.Department_Delete @DepartmentID,@RowVersion_Original", new SqlParameter { ParameterName = "DepartmentID", Value = department.DepartmentId }, new SqlParameter { ParameterName = "RowVersion_Original", Value = department.RowVersion }); ``` - EF CORE POWER TOOL - 當你的Domain model跟db context不同資料夾時:https://github.com/ErikEJ/EFCorePowerTools/issues/290#issuecomment-543131112 - 當你的dbcontext 跟你的startup專案不同專案時,例如開了一個Entity層&Service層&WebAPI層,要做migration(用EF CORE POWER TOOL或者手動下指令)會跳錯說找不到dbcontext,這時在Entity層下migration時帶`-s`參數指定有startup的那層專案路徑 (找不到可以在EF POWER TOOL帶這個指令的地方) ```cmd dotnet ef migrations add Initial -s {path} //ex:dotnet ef migrations add Initial -s ../DroneStore.Web/ ```