C# nameof用法

如果在C#中遇到一些重複的String字串變數,是由變數的命名而來,就可以使用nameof直接提取出自串,避免再打一次造成打錯的問題。

nameof

nameof運算式會產生變數、類型或成員的名稱做為字串常數

1
2
3
4
5
6
7
8
9
Console.WriteLine(nameof(System.Collections.Generic));  // output: Generic
Console.WriteLine(nameof(List<int>)); // output: List
Console.WriteLine(nameof(List<int>.Count)); // output: Count
Console.WriteLine(nameof(List<int>.Add)); // output: Add

var numbers = new List<int> { 1, 2, 3 };
Console.WriteLine(nameof(numbers)); // output: numbers
Console.WriteLine(nameof(numbers.Count)); // output: Count
Console.WriteLine(nameof(numbers.Add)); // output: Add

參考資料:

https://docs.microsoft.com/zh-tw/dotnet/csharp/language-reference/operators/nameof#code-try-0

C# LINQ 延遲執行 (Deferred Execution)

前言

在這篇中主要筆記LINQ 延遲執行的特性。
一般我們如果使用純SQL語言去與資料庫查詢通常一下指令就會被馬上執行,而LINQ卻不一樣,是直到某個觸發點他才會開始運作。
即寫Select時並不會馬上運作。

常見的觸發點

1.foreach(var item in items)
2.轉為ToList(), ToArray(), ToDictionary(),因為需巡覽才能轉型,所以會直接執行。
3.取得彙總結果的聚合函数 Count(), Max(), Sum(), First(), Last()。

主要原因

延遲執行的基礎 - Iterator、yield

  1. 您只要提供 Iterator,它會只往返於類別中的資料結構。當編譯器偵測到您的 Iterator 時,它會自動產生 IEnumerable 或 IEnumerable 介面的 Current、MoveNext 和 Dispose 方法。
  2. Iterator 程式碼會使用 yield return 陳述式輪流傳回各元素。yield break 則會結束反覆運算。

關鍵:使用 yield 關鍵字,C# 會自動將該 block 實作為 iterator 方式執行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System;
using System.Collections;

namespace DeferredExec
{
class Program
{
public class Data : IEnumerable
{
public IEnumerator GetEnumerator()
{
Console.WriteLine("yield 1");
yield return 1;
Console.WriteLine("yield 2");
yield return 2;
Console.WriteLine("yield 3");
yield return 3;
}
}
static void Main(string[] args)
{
Data data = new Data();
foreach(var item in data)
{
Console.WriteLine(item);
}
}
}
}

實作IEnumerable介面,並且撰寫方法GetEnumerator,yield會把每個區塊視為iterator(迭代器)。
而foreach中取得每個item在逆向工程可以見到其實是呼叫GetEnumerator取得一個IEnumerator(即iterator)。

有這觀念再拉回來看LINQ的方法,可以發現是IEnumerable。
Where:

1
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

Select:

1
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)

想知道是不是延遲執行可以看返回值是否為以下3種物件:

1
IEnumerable<TSource>
1
IEnumerable<IGrouping<TKey, TSource>>
1
IOrderedEnumerable<TSource>

範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace DeferredExec
{
class Program
{
private static void Main(string[] args)
{
var people = GetPeople();
var names = people
.Where(person => person.Age > 18)
.Select(person => person.Name);

foreach (var name in names)
{
Console.WriteLine(name);
}
}

private static IEnumerable<Person> GetPeople()
{
yield return new Person { Id = 1, Name = "John", Age = 36 };
yield return new Person { Id = 2, Name = "Bill", Age = 6 };
yield return new Person { Id = 3, Name = "Steve", Age = 23 };
}

public class Person
{

private int _id;
private string _name;
private int _age;
public int Id {
get {
return _id;
}
set
{
_id = value;
}
}

public string Name
{
get
{
return _name;
}
set
{
_name = value;
}
}
public int Age
{
get
{
return _age;
}
set
{
_age = value;
}
}
}
}
}

在這範例中使用泛型(Generic)的IEnumerable

1
IEnumerable<T>

因為Select、Where方法是IEnumerable,所以會在foreach觸發GetEnumerator時執行,進而達到延遲執行的效果。

延遲執行好處

1.可以再添加額外的表達式,如主要的查詢表達式後面可以添加子表達式過濾作為最終結果。
2.取得資料是最新的,因為當下才執行

總結

可以得知C#的LINQ為什麼能夠延遲執行是因為採用IEnumerable介面,裡面會有yield區塊形成迭代器,所以直到foreach執行時才會開始觸發。

參考資料

https://dotblogs.com.tw/hatelove/2013/09/10/csharp-linq-deferred-execution-introduction
https://dotblogs.com.tw/hatelove/2012/05/10/introducing-foreach-ienumerable-ienumerator-yield-iterator
https://dotblogs.com.tw/regionbbs/2012/01/12/linq_deferred_execution

C# Properity(屬性) 與 Field(欄位)

在這篇主要介紹Properity(屬性)、Field(欄位)在物件導向class中的差別。

Properity(屬性)

1.以public為主,能夠讓外部能存取。
2.包含get、set訪問器(能要求欄位只能讀或只能寫)。
3.可以增加邏輯處理,在改變一個欄位的值的時候希望改變物件的其它一些狀態,舉例:更改某外部公開屬性值x為1時,另一個內部私有欄位y需要連動更改數值為2。
4.不佔有實際內存,主要為get;set;存取私有欄位資料。

Field(欄位)

1.以private為主(有時會以protected),主要以class內部使用為主。
2.命名會加_在前面。
3.可能為靜態值所以設定為readonly
4.佔有實際內存、位址。

範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class User
{
private string _name;// _name為欄位
public string Name // Name為屬性,它含有程式碼塊
{
get
{
// 可以在此添加額外會影響的私有變數
return _name;// 讀取(返回_name值)
}
set
{
_name = value;// 為_name賦值
// 可以在此添加額外會影響的私有變數
}
}
}

總結

為什麼要使用屬性為什麼不用欄位就好?
因為能夠確保內部私有資料的安全性,並且能夠透過get;set;方式訪問時能夠增加邏輯處理、方便限制可讀、可寫。

參考資料

https://kevintsengtw.blogspot.com/2011/09/property-field.html?m=1#:~:text=Field%EF%BC%9A_FullDescription%E7%82%BA%E7%89%A9%E4%BB%B6%E7%A7%81%E6%9C%89,%E4%BE%86%E5%AD%98%E6%94%BE%E5%B1%AC%E6%80%A7%E7%9A%84%E8%B3%87%E6%96%99%E3%80%82&text=%E7%AF%80%E9%8C%84%E6%95%B4%E7%90%86%EF%BC%9A,%E9%9C%80%E8%A6%81%E5%AE%8C%E5%85%A8%E7%9B%B8%E5%90%8C%E7%9A%84%E8%AA%9E%E6%B3%95%E3%80%82
https://www.itread01.com/content/1548110544.html

C# 同步非同步實作與介紹

前言

在這篇中簡易介紹C#同步與非同步的作法。
一般我們在做一些任務時有時需要:
1.等待上個任務完成
2.同時處理多個任務
這時就會需要用到非同步、同步的作法,可以節省時間(在同個時間內處理多個任務)。
可能用到Thead或是用Task作法asyncawait。在這篇中則以:
https://docs.microsoft.com/zh-tw/dotnet/csharp/programming-guide/concepts/async/
作為範例,簡易的操作一個需要處理同步/非同步的範例。

同步範例實作

簡單來說,就是每個任務都是會等待上面的任務完成才依序執行。
下方以製作一份早餐的同步範例,1~7步驟都要等上面的步驟完成才會繼續。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using System;
using System.Threading.Tasks;

namespace async
{
class Program
{
static void Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");

Egg eggs = FryEggs(2);
Console.WriteLine("eggs are ready");

Bacon bacon = FryBacon(3);
Console.WriteLine("bacon is ready");

Toast toast = ToastBread(2);
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("toast is ready");

Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}

private static Juice PourOJ()
{
Console.WriteLine("Pouring orange juice");
return new Juice();
}

private static void ApplyJam(Toast toast) =>
Console.WriteLine("Putting jam on the toast");

private static void ApplyButter(Toast toast) =>
Console.WriteLine("Putting butter on the toast");

private static Toast ToastBread(int slices)
{
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
Task.Delay(3000).Wait();
Console.WriteLine("Remove toast from toaster");

return new Toast();
}

private static Bacon FryBacon(int slices)
{
Console.WriteLine($"putting {slices} slices of bacon in the pan");
Console.WriteLine("cooking first side of bacon...");
Task.Delay(3000).Wait();
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("flipping a slice of bacon");
}
Console.WriteLine("cooking the second side of bacon...");
Task.Delay(3000).Wait();
Console.WriteLine("Put bacon on plate");

return new Bacon();
}

private static Egg FryEggs(int howMany)
{
Console.WriteLine("Warming the egg pan...");
Task.Delay(3000).Wait();
Console.WriteLine($"cracking {howMany} eggs");
Console.WriteLine("cooking the eggs ...");
Task.Delay(3000).Wait();
Console.WriteLine("Put eggs on plate");

return new Egg();
}

private static Coffee PourCoffee()
{
Console.WriteLine("Pouring coffee");
return new Coffee();
}

private class Coffee
{
}

private class Egg
{
}

private class Toast
{
}

private class Juice
{
}

private class Bacon
{

}
}
}

結果:

可以發現這樣很難節省時間,所以其實能把某些部分拆成異步來做,有助於節省時間。(雖然同時間可能記憶體耗能會加大)

異步範例實作

準備早餐程式碼的簡單非同步版本看起來像下列程式碼片段:
FryEggsAsync、FryBaconAsync、MakeToastWithButterAndJamAsync更新為Task,函式使用Async
這時會變成先做第一步,之後2、3、(4+5)步是併發執行,並使用while做判斷,依序把完成的Task移除List,仍需處理的Task數量為0就跳出,代表完成了!
再由下個任務6繼續。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace async
{
class Program
{
static async Task Main(string[] args)
{
Coffee cup = PourCoffee();
Console.WriteLine("coffee is ready");

var eggsTask = FryEggsAsync(2);
var baconTask = FryBaconAsync(3);
var toastTask = MakeToastWithButterAndJamAsync(2);

var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(breakfastTasks);
if (finishedTask == eggsTask)
{
Console.WriteLine("eggs are ready");
}
else if (finishedTask == baconTask)
{
Console.WriteLine("bacon is ready");
}
else if (finishedTask == toastTask)
{
Console.WriteLine("toast is ready");
}
breakfastTasks.Remove(finishedTask);
}

Juice oj = PourOJ();
Console.WriteLine("oj is ready");
Console.WriteLine("Breakfast is ready!");
}
static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
var toast = await ToastBreadAsync(number);
ApplyButter(toast);
ApplyJam(toast);

return toast;
}

private static Juice PourOJ()
{
Console.WriteLine("Pouring orange juice");
return new Juice();
}

private static void ApplyJam(Toast toast) =>
Console.WriteLine("Putting jam on the toast");

private static void ApplyButter(Toast toast) =>
Console.WriteLine("Putting butter on the toast");

private static Toast ToastBread(int slices)
{
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
Task.Delay(3000).Wait();
Console.WriteLine("Remove toast from toaster");

return new Toast();
}

private static async Task<Bacon> FryBaconAsync(int slices)
{
Console.WriteLine($"putting {slices} slices of bacon in the pan");
Console.WriteLine("cooking first side of bacon...");
await Task.Delay(3000);
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("flipping a slice of bacon");
}
Console.WriteLine("cooking the second side of bacon...");
await Task.Delay(3000);
Console.WriteLine("Put bacon on plate");

return new Bacon();
}


private static async Task<Egg> FryEggsAsync(int howMany)
{
Console.WriteLine("Warming the egg pan...");
await Task.Delay(3000);
Console.WriteLine($"cracking {howMany} eggs");
Console.WriteLine("cooking the eggs ...");
await Task.Delay(3000);
Console.WriteLine("Put eggs on plate");

return new Egg();
}

private static Coffee PourCoffee()
{
Console.WriteLine("Pouring coffee");
return new Coffee();
}
private static async Task<Toast> ToastBreadAsync(int slices)
{
for (int slice = 0; slice < slices; slice++)
{
Console.WriteLine("Putting a slice of bread in the toaster");
}
Console.WriteLine("Start toasting...");
await Task.Delay(3000);
Console.WriteLine("Remove toast from toaster");

return new Toast();
}
private class Coffee
{
}

private class Egg
{
}

private class Toast
{
}

private class Juice
{
}

private class Bacon
{

}
}
}

結果:
可以發現為異步執行!
FryEggsAsync:原本需6秒
FryBaconAsync:原本需6秒
MakeToastWithButterAndJamAsync:原本需3秒
以同步執行的話需要:15秒
在異步執行只需取其中最大的秒數:6秒
大幅度的減省時間花費。

TaskAPI 包含兩種方法, Task.WhenAll 和 Task.WhenAny 撰寫非同步程式碼,能在多個背景工作上執行非封鎖等候。
await Task.WhenAny
await Task.WhenAll
更多可參考:
https://gist.github.com/relyky/52b4abf32b44d5d00a674b9cd34ca3a3

用紅筆畫起來部分,表示其併發執行。

結論

以Async為開頭的函數內步才能夠進行併發的操作。
並且搭配可以搭配await做Task的停頓。
即讓每個可Async的Task充分利用時間。

在工作上可能遇到需要併發、可同時執行時會使用,如有N個迴圈任務且沒有順序性,則可以寫入Task,或是需要用後端呼叫HTTP請求,可以一次併發,增加系統效率。或是有順序性執行的任務就很適合採用Task拆成子項目。

如果要再深入探討可以參考官方:
https://docs.microsoft.com/zh-tw/dotnet/csharp/async

參考資料

https://docs.microsoft.com/zh-tw/dotnet/csharp/programming-guide/concepts/async/
https://gist.github.com/relyky/52b4abf32b44d5d00a674b9cd34ca3a3
https://docs.microsoft.com/zh-tw/dotnet/csharp/async

SQL Server Stored Procedure 介紹&實作

在這篇文章中簡易的介紹Stored Procedure。

Stored Procedure(預存程序)

A stored procedure is a prepared SQL code that you can save, so the code can be reused over and over again.

So if you have an SQL query that you write over and over again, save it as a stored procedure, and then just call it to execute it.

You can also pass parameters to a stored procedure, so that the stored procedure can act based on the parameter value(s) that is passed.

簡單來說就是一種預存程序,寫在資料庫裡面,可以藉由外部傳入參數去呼叫數值,而得到結果。

優點

1.能夠讓不同應用程式直接呼叫,核心邏輯不用分散。
(但目前有微服務架構能改用API了,因此除非是特別有必要,否則還是很少碰到)

缺點

1.撰寫因為沒辦法中斷點Debug所以除錯困難。

簡易寫一個Stored Procedure

1.建立一段searchProduct的Stored Procedure。用來查詢特定Id的商品。

1
2
3
4
5
CREATE PROCEDURE searchProduct 
@productId int
AS
SELECT * from Products
where Id = @productId

2.執行後可以在資料庫的「資料庫>Programmability>Stored Procedures」裡面找到

3.執行

1
exec searchProduct 6

即可得到結果:

4.這樣就完成了一段最簡單的Stored Procedure。
但在實作上主要維護時會很花時間,如果是千行T-SQL,無法使用中斷點會像是天書那樣。

總結

以目前寫過系統來說都是在後端程式裡面寫,而Stored Procedure算是一個新的體驗。
除非,真的有必要需求,否則還是用API式輕量化,因為比較好DEBUG。

SQL Server 常見SQL語法筆記

前言

在這裡整理一些專案常用的SQL語法。

語法

1.新增一筆資料

1
insert into Todos (Name) Values ('123')

2.更新一筆資料

1
update Todos set Name = '4123',IsComplete = 1 where Name = '123'

3.刪除一筆資料

1
delete from Todos where id = 17

4.查詢該表所有資料

1
select * from Todos;

5.使用查詢結果新增資料

1
insert into Todos (Name) select Name from Todos where Name = '4123'

6.INNER JOIN 主表跟次要表都有相同的值才撈出

1
2
select * from ShipmentOrderDetails as a 
join Products as b on a.ProductId = b.Id

7.LEFT JOIN 主表值為主嘗試撈取次要表值,次要表有值就撈,沒值就NULL

1
2
select * from ShipmentOrderDetails as a 
left join Products as b on a.ProductId = b.Id

8.GROUP BY 取得以哪個欄位作為群,並且操作該群的集合資料

1
2
select OrderId,sum(ProductPrice * ProductUnit) sum from ShipmentOrderDetails 
group by OrderId

9.多對多資料處理(即使用left join/join的組合技)

1
2
3
select a.Id,a.Name,c.Id as ProductCategoryId,c.Name as ProductCategoryName from Products as a
left join ProductProductCategoryRelationships as b on a.Id = b.ProductId
left join ProductCategory as c on c.Id = b.ProductCategoryId

總結

這裡整理SQL常見用法。
因為在寫C#時其實內部會使用LINQ,所以就會對不常用的原生SQL生疏,因此在此整理在專案上常見的使用方法,有需要時就能節省再熟悉的時間。

[Day30] C# MVC 實際撰寫後之我的想法 - C#&AspNetCore

在此先說明一下,大概花了一個月製作了了一套商品出貨訂單系統的Side Project。
儲存庫:https://github.com/yuhsiang237/Order-System
影片網址:https://www.youtube.com/watch?v=nSk6OZjhDMY

所以這文章內容會包含很多我與之前的製作系統經驗的分享。

首先,MVC現在已經算是很主流的網站架構了,我在之前有使用過Laravel、ASP.NET Core MVC這兩套框架來設計系統,因此在整體的觀感上因為設計模式是有共同的出發點,所以上手難度是差不多,甚至說是無痛換上的感覺。因此,依照這個推論,如果仍有下個世代的框架出來,也是標榜MVC那也夠無痛接手,頂多就是學一些基礎語法糖而已。

而專案內通常有少部分會寫到演算法,我目前處理過最複雜演算法就是BFS,主要是要產生訂單的子代、父代,並且將畫圖產生出來,就是一張圖論的圖。這算是我在專案裡面處理過最複雜的結構。
這是我的演算法筆記用的存庫,遇到一些算法需求會整理上去。
https://github.com/yuhsiang237/AlgorithmStudy

而MVC基本上跟演算法無關。他只是給你一套共同認知的地基,讓你能夠跟其他人有共同基礎去打造事物,這有什麼好處呢?能夠增加維護性,因為在沒基礎下,需要知道整體脈絡怎麼寫必須去翻舊的程式碼,算是一部分技術債,而MVC則把這技術債降低,讓你能夠比較無痛的降低學習成本,有點像是一套彼此知道的內功,學會就能打天下。

再來我說說Laravel與ASP.NET Core MVC的差異。

在除錯上,C#有著地表最強IDE Visual Studio撐腰,所以除錯非常容易,只要中斷點下去跑一次八成就知道要改哪裡了。Laravel則需要肉眼DEBUG,或是自己插Return返回點。

在撰寫速度上,C#需要編譯時間,Laravel是直譯語言則不需要所以會快一些。但如果考量到C#便利的中斷點,那最後把時間拉長我認為是C#會是優勝。

在型別上,C#屬於強型別,每屬性有特定的類型,能夠讓自己很清楚知道要塞什麼數值。而Laravel則是弱型別,宣告是用$var所以大專案下來可能會不知道哪個是哪個變數,可能會造成數值塞錯報錯。

在直譯與編譯上,C#需要編譯才能知道撰寫成功與否,Laravel則是要當場跑才知道。如果C#可以成功編譯,那可能上線只會有邏輯錯誤;Laravel則是需要當場去試錯才比較可能知道問題。

在開發速度上,C# MVC有一套框架給你但並不給你便利的內建系統,如Seed塞值、資料庫遷移、內建Redius、等等。而Laravel則是全都給你了,所以你就只要寫邏輯就好。

在版本升級上,C# MVC基礎就View Model Controller所以升級版本只要考量套件問題,相容就沒問題,而現在ASP.NET Core仍然是用基礎寫法寫就能無痛升級。Laravel則是要考量目錄變動,因為它幫你內建很多功能,每個版本的寫法也不太相同。

在資料結構上,C#內建超多的資料結構,如List、Array、Directory、Hashset…幾乎是C++的STL演算法有的他都全包還擴增,而Laravel則是沒有,需要自己找套件包或自己寫,在處理複雜資料時就會稍微辛苦一點。

在其他領域應用上,C#能夠開發網頁、桌面程式、library、windows、linux(asp.net core)。而Laravel則是只能開發網頁。

總結

這是我對於這兩套MVC的差異理解,但總體來說還是看自己需求,以目前來看未來我會選擇C# MVC,因為在維護上方便許多、在台灣工作環境相對友善(?)。而更重要的是需求分析、設計模式、演算法方面,因此目前可能會往這邊走XD。
總之,哪種方法能夠讓我花最少成本大概就是它了,畢竟寫程式某種意義就是避免浪費時間。

[Day29] C# MVC 被使用在哪些地方 - C#&AspNetCore

先以C#語言實際用在那些地方開始講好了,可以製作遊戲Unity、命令列程式、跨平台(ASP.NET Core)、手機(但很少)、桌面應用、資料分析。唯一缺點大概就是嵌入式,因為通常嵌入式有記憶體限制,會壓的很低,會用純C,但是C#基本上語法跟C也很相像。

而ASP.NET的MVC則是在處理網頁為多數,如現在普遍在台灣看到的政府機構網頁、大型服務網頁都可以用這套MVC去實現,算是處理大量資料已經有認證過可以實踐的框架了。

簡言之,就是沒什麼框架能與之抗衡,光是Visual Studio的除錯能力就能減少很多開發者的時間。以及背後的支持微軟,是自己家目前產品。而Linux則是偏向雜湊派,東加西加拼湊,整合度比較低。因此,目前首選還是微軟。

結論

總而言之,除了嵌入式、手機外,一些後端API、網頁程式基本上都可以看到它的蹤影。

[Day28] C# MVC FluentValidation 好用的驗證格式套件 - C#&AspNetCore

這回介紹一個必用的驗證神器FluentValidation,在處理驗證時會比原本Model驗證好上很多。因為他能適應不同的條件變化。

首先先看一張DEMO圖,就是類似表單驗證什麼都可以使用。

安裝

1.開啟NuGet管理畫面

2.因為我是.net core專案所以安裝FluentValidation.AspNetCore,如果不是就裝第一個

撰寫一個簡單驗證

在此以帳密登入為範例

1.先建立一個Model
Models/LoginViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace FluentValidationExample.Models
{
public class LoginViewModel
{
public string Account { get; set; }
public string Password { get; set; }
}
}

2.撰寫該Model驗證
Models/Validator/LoginViewModel.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FluentValidation;

namespace FluentValidationExample.Models.Validator
{
public class LoginValidator : AbstractValidator<LoginViewModel>
{
public LoginValidator()
{
RuleFor(x => x.Account).NotNull().WithMessage("名稱不可為空");
RuleFor(x => x).Custom((x, c) =>
{
if (x.Password == "" || x.Password == null)
{
c.AddFailure("Password", "密碼不可為空");
}
});
}

}
}

最主要是繼承抽象的AbstractValidator,而裡面傳進去的多型是LoginViewModel,即要驗證的Model。
再來就是用RuleFor去驗證。
我寫了一個內建、一個客製的。
此外,如果有連結資料庫也能從外面把Context傳進來。

3.再來撰寫登入的Post事件
Controllers/HomeController.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[HttpPost]
public IActionResult Index(LoginViewModel model)
{
LoginValidator validator = new LoginValidator();
ValidationResult result = validator.Validate(model);
if (!result.IsValid)
{
foreach (ValidationFailure failer in result.Errors)
{
ModelState.AddModelError(failer.PropertyName, failer.ErrorMessage);
}
}
return View();
}

基本上就是套用剛剛完成的validator。
當!result.IsValid時將錯誤訊息塞給ModelState,在前台就能顯示。

4.前台畫面cshtml撰寫。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@model FluentValidationExample.Models.LoginViewModel;

<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">building Web apps with ASP.NET Core</a>.</p>
</div>
<div asp-validation-summary="ModelOnly"></div>

<form asp-action="Index">
Account <input asp-for="Account" /> <span asp-validation-for="Account"></span>
<br />
Password <input asp-for="Password" /> <span asp-validation-for="Password"></span>
<br />
<input type="submit" value="submtit" />
</form>

主要就塞入asp-validation-for,他會與ModelState匹配,剛剛增加的錯誤訊息就能收到並顯示在畫面上了。

6.完成,然後嘗試用Debug跑一次

總結

為什麼不用Model驗證呢?
因為不同情況如某些時候欄位必填,有時不是,這時就會有問題。
而FluentValidation則完整破除這問題,並且能以邏輯方式條列撰寫,在釐清數據正確與否非常有幫助。
因此建議還再使用Model驗證的人可以快點換成FluentValidation,複雜的條件也能迎刃而解。

在此附上專案連結:
https://github.com/yuhsiang237/FluentValidation-Project

更多範例可以參考官方:
https://docs.fluentvalidation.net/en/latest/aspnet.html