---
# System prepended metadata

title: '[112]天方科技 ASP.net core 教育訓練 1120328(C#成員、Get Action 創建、Linq查詢語法)'

---

# [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)
