# [112]天方科技 ASP.net core 教育訓練 1120328(C#成員、Get Action 創建、Linq查詢語法)
## 類別成員
類別和結構的成員可表示其資料與行為。
類別的成員包含在所有類別中宣告的成員,以及在其繼承階層架構之所有類別中宣告的所有成員

**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;
}
```

### Visual Studio 語法自動完成功能
`Alt + →`:可以使用自動完成功能

### 編輯→進階 可以查看快捷鍵

### 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指令

### 2.Visual Studio的輸出視窗可以可看到SQL指令

### 3.SQL Server Profiler
SQL Server 工具→SQL Server Profiler→新增追蹤→登入→執行




### 進入SQL Server追蹤頁面

### 可以看到SQL指令,比在Visual Studio更為詳細,例如可看到傳入參數的值

### 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
}
```

### `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
