# [112]天方科技 ASP.net core 教育訓練 1120328(C#成員、Get Action 創建、Linq查詢語法) ## 類別成員 類別和結構的成員可表示其資料與行為。 類別的成員包含在所有類別中宣告的成員,以及在其繼承階層架構之所有類別中宣告的所有成員 ![](https://i.imgur.com/g2d1ApC.png) **C#教材 20483B-ENU-TrainerHandbook1─Lesson 3 page1-14** 參考: [**C#成員**](https://learn.microsoft.com/zh-tw/dotnet/csharp/programming-guide/classes-and-structs/members) ## Static 靜態修飾詞 ### 靜態方法成員 ```csharp= public static void Main(string[] args) { Program.xx("Hello World"); //透過型別名稱呼叫方法成員 xx("Hello World"); //如果處於該型別,可直接呼叫 //例 string s = "123"; int i = int.Parse(s); //int(型別).Parse(靜態方法) //Parse方法定義帶有static修飾詞 public static int Parse(string s) s = i.ToString(); //i(變數).ToString(實體方法) //ToString方法定義沒有static修飾詞 public override string ToString() } static void xx(string s) { Console.WriteLine(s); } ``` ### 實體方法成員 ```csharp= public static void Main(string[] args) { Program p = new Program(); //靜態成員使用實體成員時,必須先實體化 p.xx("Hello World"); //透過實體化後的變數呼叫方法 } void xx(string s) { Console.WriteLine(s); } ``` ### 不同型別調用方法 ```csharp= public class program { void xx(string s) { //靜態成員直接使用型別呼叫 Test.Name = s; //非靜態成員需實體化 Test t = new Test(); t.Age = 20; Console.WriteLine(s); } } public class Test { public static string Name{ get; set; } public int Age{ get; set; } } ``` ## Get Action 創建 ### Get id 語法 `[HttpGet("{id}")]` `Route:API url + id` `https://localhost:7019/api/s30_student/1` ```csharp= [HttpGet("{id}")] public async Task<ActionResult<s30_student>> Gets30_student(decimal id) { if (_context.s30_student == null) { return NotFound(); } var s30_student = await _context.s30_student.FindAsync(id); //FindAsync()會搜尋符合key值的資料,產生where語法 if (s30_student == null) { return NotFound(); } return s30_student; } ``` ![](https://i.imgur.com/VK7W5DM.png) ### Visual Studio 語法自動完成功能 `Alt + →`:可以使用自動完成功能 ![](https://i.imgur.com/c9MD5vU.png) ### 編輯→進階 可以查看快捷鍵 ![](https://i.imgur.com/Fh0pWTB.png) ### Http Action HTTP 定義了一組能令給定資源,執行特定操作的請求方法(request methods),以下是常用方法: **GET** `GET` 方法請求展示指定資源。使用 `GET` 的請求只應用於取得資料。 **POST** `POST` 方法用於提交指定資源的實體,通常會改變伺服器的狀態或副作用(side effect)。 **PUT** `PUT` 方法會取代指定資源所酬載請求(request payload)的所有表現。 **DELETE** `DELETE` 方法會刪除指定資源. **:warning:PATCH**(在 .net core無法直接呼叫使用,需要另外實現) PATCH 方法套用指定資源的部份修改。 一個Controller可以允許許多Http Action,但是每個Action的Route必需不一樣 ```csharp= [HttpGet] //https://localhost:7019/api/s30_student [HttpGet("{year}")] //https://localhost:7019/api/s30_student/110 ``` 參考: [**HTTP 請求方法**](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Methods) ### 自行創建HttpGet語法 首先從同步語法開始,url `id` 為學年,因為有指定回傳型別`s30_student`,所以先回傳相同型別的結果, `[HttpGet("{year}")]`以及`(int year)`的`year`,**名稱必須一樣,大小寫可不同**,如果使用Swagger測試,會強制要大小寫相同才能執行 ```csharp= [HttpGet("{year}")] public s30_student GetStudent(int year) { return _context.s30_student.Find(year); } //url https://localhost:7019/api/s30_student/110 ``` 如果使用兩層url,這裡測試時將回傳型別改為`IActionResult`,可回復Http狀態碼,`return Ok();` ```csharp= [HttpGet("{year}/{_}")] public IActionResult GetStudent(int year, int _) { return Ok(); } //url https://localhost:7019/api/s30_student/110/555 ``` 相同Route下,在方法新增`int aaa, int bbb`兩個參數 ```csharp= [HttpGet("{year}/{abc}")] public IActionResult GetStudent([FromQuery]int year, int abc, int aaa, int bbb) //資料繫結來源使用[FromQuery] { return Ok(); } //https://localhost:7019/api/s30_student/110/11?aaa=456&bbb=789 ``` HTTP GET 要求的模型繫結來源 **[FromQuery]** - 從查詢字串取得值。 **[FromRoute]** - 從路由資料取得值。 **[FromForm]** - 從張貼的表單欄位取得值。 **[FromBody]** - 從要求本文取得值。 **[FromHeader]** - 從 HTTP 標頭取得值。 參考: [**ASP.NET Core 中的資料繫結**](https://learn.microsoft.com/zh-tw/aspnet/core/mvc/models/model-binding?view=aspnetcore-7.0) [**ASP.NET Core Web API 入門教學 - From之常用來源標籤功能用法介紹**](https://blog.talllkai.com/ASPNETCore/2021/04/28/FromWhere) `[FromQuery]int year` `year`被設定為參數,所以url內的`year`會有Route以及Query的值 ```csharp= [HttpGet("{year}/{abc}")] public IActionResult GetStudent([FromQuery]int year, int abc, int aaa, int bbb) //資料繫結來源使用[FromQuery] { return Ok(); } //{year} = 110, {abc} = 123, year = 120, aaa = 111, bbb = 222 //https://localhost:7019/api/s30_student/110/123?year=120&aaa=111&bbb=222 ``` ## Linq查詢語法 ### Lambda Expression Loading all data ```csharp= using (var context = new BloggingContext()) //等同相依性注入 { var blogs = context.Blogs.ToList(); //透過DbContext讀取table } ``` Loading a single entity 回傳一筆資料的方法: `Find()`:參數需為`Key`值 `Single()`:條件下完只能有一筆符合 `First()`:符合條件的第一筆資料 ```csharp= using (var context = new BloggingContext()) { var blog = context.Blogs .Single(b => b.BlogId == 1); //取得符合條件的一筆資料,如果符合條件超過一筆會出現error } ``` Filtering ```csharp= using (var context = new BloggingContext()) { var blogs = context.Blogs .Where(b => b.Url.Contains("dotnet")) //取得包含"dotnet"的資料 .ToList(); //過濾完將資料包成清單集合物件 } ``` 參考 [**查詢資料**](https://learn.microsoft.com/zh-tw/ef/core/querying/) ### Query Expression ```csharp= //queryAllCustomers is an IEnumerable<Customer> var queryAllCustomers = from cust in customers //類似SQL語法的使用 select cust; ``` Filtering ```csharp= var queryLondonCustomers = from cust in customers where cust.City == "London" select cust; ``` Ordering ```csharp= var queryLondonCustomers3 = from cust in customers where cust.City == "London" orderby cust.Name ascending //ascending為預設值,可寫可不寫 select cust; ``` Grouping ```csharp= // queryCustomersByCity is an IEnumerable<IGrouping<string, Customer>> var queryCustomersByCity = from cust in customers group cust by cust.City; //group後不用select,出來的結果就是一個集合 // customerGroup is an IGrouping<string, Customer> foreach (var customerGroup in queryCustomersByCity) { Console.WriteLine(customerGroup.Key); foreach (Customer customer in customerGroup) { Console.WriteLine(" {0}", customer.Name); } } ``` ```csharp= // custQuery is an IEnumerable<IGrouping<string, Customer>> var custQuery = from cust in customers group cust by cust.City into custGroup //將group結果放入變數,之後再透過變數整理資料 where custGroup.Count() > 2 orderby custGroup.Key select custGroup; ``` Joining Linq只有`inner join`,必須使用對等式 ```csharp= // custQuery is an IEnumerable<IGrouping<string, Customer>> var innerJoinQuery = from cust in customers join dist in distributors on cust.City equals dist.City //使用"equals",不使用"==",也不使用其他運算式 select new { CustomerName = cust.Name, DistributorName = dist.Name }; //"new"出來的型別為匿名型別,所以宣告變數時使用"var" ``` 參考: [**基本 LINQ 查詢作業 (C#)**](https://learn.microsoft.com/zh-tw/dotnet/csharp/programming-guide/concepts/linq/basic-linq-query-operations) ## ASP .Net Core語法轉SQL指令 ### 1.Visual Studio執行時的主控台可看到SQL指令 ![](https://i.imgur.com/9D6Ls4K.png) ### 2.Visual Studio的輸出視窗可以可看到SQL指令 ![](https://i.imgur.com/qKmibHc.png) ### 3.SQL Server Profiler SQL Server 工具→SQL Server Profiler→新增追蹤→登入→執行 ![](https://i.imgur.com/UzMGweh.png) ![](https://i.imgur.com/wwko4UL.png) ![](https://i.imgur.com/XxO5O2v.png) ![](https://i.imgur.com/n5wlYPA.png) ### 進入SQL Server追蹤頁面 ![](https://i.imgur.com/9zzXHgL.png) ### 可以看到SQL指令,比在Visual Studio更為詳細,例如可看到傳入參數的值 ![](https://i.imgur.com/a5630e1.png) ### Linq語法產生的SQL指令,執行帶參數的指令,可預防SQL Injection ```sql= exec sp_executesql N'SELECT TOP(2) [s].[std_key], [s].[cls_id], [s].[std_id], [s].[std_name], [s].[std_name_eng], [s].[std_sex], [s].[std_year], [s].[upd_dt], [s].[upd_name] FROM [s30_student] AS [s] WHERE [s].[std_year] = @__year_0',N'@__year_0 int',@__year_0=110 ``` ## Linq語法測試 ### `Single()`只能回傳一筆資料,超出一筆會出現error ```csharp= [HttpGet("year/{year}")] public s30_student GetStudentByYear(int year) //回傳型別是"s30_student",並且回傳一筆資料 { var linq = from s in _context.s30_student select s; return linq.Single(); //"Single()"只能回傳一筆資料,超出一筆會出現error } ``` ![](https://i.imgur.com/9oKoSQT.png) ### `First()`會抓取第一筆資料 ```csharp= [HttpGet("year/{year}")] public ActionResult<s30_student> GetStudentByYear(int year) //使用ActionResult可以 { var linq = from s in _context.s30_student select s; return linq.FirstOrDefault(); //"First()"會抓取第一筆資料,可以使用"FirstOrDefault()"避免讀取後為空值,產生error } ``` ### 如果要讀取多筆資料,需要使用`IEnumerable<>`作為回傳型別 ```csharp= [HttpGet("year/{year}")] public ActionResult<IEnumerable<s30_student>> GetStudentByYear(int year) //可列舉的多筆資料使用IEnumerable<>型別 { var linq = from s in _context.s30_student where s.std_year == year select s; return linq.ToList(); //將多筆資料列為清單 } ``` ### 增加參數條件 ```csharp= [HttpGet("year/{year}")] public ActionResult<IEnumerable<s30_student>> GetStudentByYear(int year, string name) { var linq = from s in _context.s30_student where s.std_year == year && (name.Length > 0 && s.std_name.StartsWith(name)) //確認name內至少有一個字,且搜尋以這個字串開頭的資料 select s; return linq.ToList(); } ``` ### 如果`name`為`null`或是空字串,回報狀態碼400 ![](https://i.imgur.com/ghnurA1.png)