包含標籤 CSharp 的文章

菜雞新訓記 (4): 使用 Swagger 來自動產生可互動的 API 文件吧

img

這是俺整理公司新訓內容的第四篇文章,目標是簡單地使用 Swagger 工具來自動產生可互動的 API 文件

API 文件與 Swagger

上一篇 我們建立了一個有簡單的 CRUD 的 Web API 服務,這篇我們就接續著 API 服務往下看吧!

之前我們介紹 API 的時候有提過:API 是為了讓兩個服務之間可以溝通、互動所產生的接口。而所有的溝通要有效,都一定要先有共識,隨著溝通的人數越來越多,或是內容的理解要越來越細,就會用文件或契約的方式來達成共識。

回到我們的 API 服務開發來說,就是你除了把服務生出來了,可以跑了以外,還有一個重要的點是:必須讓所有的使用者(包含幾個月後的你自己)知道怎麼使用這組 API 服務

也就來說,就是要寫 API 規格文件 啦!

……

閱讀全文


菜雞新訓記 (3): 使用 Dapper 來連線到資料庫 CRUD 吧

Image

這是俺整理公司新訓內容的第三篇文章,目標是在 .NET Core 簡單地使用 Dapper 連線到資料庫並完成 CRUD 的功能

接續 上一篇 的進度,我們接著要來連線到資料庫中完成我們的 Web Api 的 CRUD 範例。因為從新訓時期到現在工作團隊作業上主要都是使用 Dapper 來做連線資料庫的工作,這邊就直接用 Dapper 來推進吧!

Dapper 有多好用呢?它輕量、它簡單、它快速。總之先把大神們的介紹文直接拿來鎮樓:

那麼按照慣例,我們先來 吹捧今天的主角 說明一點簡單的前因後果吧。想直接實作的朋友,可以跳到正式開工的小節呦。

……

閱讀全文


菜雞新訓記 (2): 認識 Api & 使用 .net Core 來建立簡單的 Web Api 服務吧

Image

這是俺整理公司新訓內容的第二篇文章,目標是對 Api, Restful Api, HTTP 等相關的知識點做個筆記,並用 .net Core 建立一個簡易的 Web Api 專案

前言、基本觀念

我們在 上一篇 記錄了新訓第一天的 Git 操作筆記。接著在這篇,我們終於要進入 .net Core 啦!

目前的規劃是先從建立一個可以使用的、最簡單版本的 Web Api 服務開始,再將各個工具擴增進來。所以後續的文章應該都會以這篇的簡易 API 為基底繼續延伸下去(如果順利的話啦)

這篇文章的前半段會用來記錄一些使用或開發 API 常用到的相關知識,如果對 HTTP 的部分已經有點頭緒,或是迫不及待想直接動手用 .net Core 開 Api 服務的朋友們,可以直接跳到 正式開工 的部份。那麼,我們開始吧~


什麼是 API

我們在物件導向的 介面 時有稍微聊過所謂介面(Interface)的概念:「在兩個系統,或是兩個分層之間要介接的時候,只需要提供我這個功能的接口/介面給對方,就能讓對方知道如何使用」

API(Application Programming Interface)也是同樣的道理:

在不同的應用程式或服務(Application)之間,使用程式碼(Programming)的方式提供一組 介面(Interface),讓提供方和使用方可以藉由這組介面銜接起來。

API 最貼切的比喻就是我們在 封裝篇 也用過的販賣機:販賣機會提供不同飲料的按鈕,當我們選擇了其中一個按鈕按下、投了錢之後,對應的飲料就會掉下來。

對應回來就是:我們到了某個服務(販賣機),去拿我們想要的資料(飲料),所以呼叫了該服務的某支 API(按鈕)並且提供了一些該 API 要求的資料(投錢),最後 API 就會把我們想要的資料交給我們(飲料)

再用更實際的例子來說就像是:假設我們想要做一款可以查詢台北市的公車動態的 APP,於是我們到了提供公車動態的服務 MOTC Transport API v2 去找我們想要的 API,過程中我們可能需要告訴服務我們要查的是台北市,最後服務就會將公車動態的資料交給我們。

關於 API 的部份,推薦可以先閱讀過 Huli 大大的這兩篇,將基本觀念說明的相當好懂且透徹:

另外,也推一下我在 CodingBar 看到的這篇 API 到底是什麼? 用白話文帶你認識 和它所引用的影片:

……

閱讀全文


菜雞抓蟲: 在 Amazon Linux AMI 安裝 .net Core 時卡在 Requires: openssl-libs

最近遇到在 Amazon Linux AMI 要安裝 .net Core 3.1 環境的時候,會一直跳出
Requires: openssl-libs 而無法安裝的問題,儘管明明已經有 openssl 了,但還是解析失敗找不到依賴,過程一直碰壁,因此在這邊紀錄一下。

過程中嘗試了安裝 openssl-libs(會找不到該套件)、下載 Dotnet 的 tar.gz ,再直接對執行檔下 Dotnet 指令起站台(雖然網站起得來,但執行者會是當下的登入身分,也就是 ‘’@連線進來的IP-伺服器位置,而非由本機執行。後續如果有連線資料庫等檢查權限的地方就很容易出錯)

最後在 Dotnet Core 的 issue 翻到這篇 Cannot install .NET Core 2.0 on Amazon Linux AMI 才成功解決。

首先先將 openssl-libs 的 SPEC 抓下來,然後給 RPM 建置一下。這兩句可以參考一下這篇 RPM 打包︰由一竅不通到動手濫用 (二) 的說明。

wget https://github.com/dotnet/core/files/2186067/openssl-libs-ami.spec.txt
rpmbuild --bb openssl-libs-ami.spec.txt
……

閱讀全文


C#: 元組 (Tuple)

因為隔壁介紹原則的部分有點卡住了,所以這週來紀錄一下挺常用到的方便東西:Tuple

這篇的 Tuple 指的是 C# 7.0 後提供的 ValueTuple 和相關語法,舊版得用 Tuple.Create 建立,成員的名稱也只能使用 Item1, Item2…,實用性並不是很高。但新 Tuple 出現後,方便程度大大提升,這邊就稍作紀錄一下。

註:此處使用的 Dump 是 Linqpad 提供的輸出方法,把它當成 Print 就行了。

var student = (1, "王小明");
student.Item1.Dump(); // 1
student.Item2.Dump(); // 王小明

student.Dump();

可以看到 Tuple 的建立相當簡單,只需要用小括號 () 括選起來即可。建立後的內容就會像這樣:

但這樣使用就和之前一樣,取出來時只能拿 Item1, Item2,放個幾天根本就不記得 Item1 裡面是啥東西了。這時我們就可以替成員們取名字

(int ID, string Name) student = (1, "王小明");
student.ID.Dump(); // 1
student.Name.Dump(); // 王小明

如此使用的時候就和一般操作物件的習慣沒有差別,也增加了可讀性。

……

閱讀全文


C#: 時區轉換、民國西元、國曆農曆、中文月份週期

聊到將時間從 UTC 轉到台灣時間,居然還是聽到朋友表示使用 +8 小時的做法,驚為天人。這種做法可能會造成後續的問題,例如時區並不會跟著變動,或是遇到日光節約等特殊狀況就容易出事。和西元民國轉換直接 -1911 一樣不穩定。

這篇就用來記錄一下之前看過比較優雅的時區轉換方式,順便將先前存著的時間處理相關資料整理一下,方便之後需要時可以馬上回來查詢。

TimeZoneInfo: 時區資訊

轉換方式主要參考自 [食譜好菜] DateTime 具有文化特性的格式化及時區的轉換在各時區間轉換時間,感謝前人的指引。

關於文化特性,也可以參考本站的 菜雞抓蟲: DateTime.ToString() 之我們不一樣 & CultureInfo 文化特性小筆記 呦。

// 假設現在是要從標準時區 +00:00 轉換到台灣時區,故這邊使用 UtcNow 先取標準世界協調時間
var nowDateTime = DateTime.UtcNow;

nowDateTime.ToString("yyyy/MM/dd H:mm:ss zzz").Dump();
// 2020/08/30 15:56:05 +00:00

// ==================================================

// 傳統的 直接對時間做計算的方式…
var addedDateTime = nowDateTime.AddHours(8);

addedDateTime.ToString("yyyy/MM/dd H:mm:ss zzz").Dump();
// 2020/08/30 23:56:05 +00:00
// 可以看到儘管時間變動了,時區仍然還在 +00:00

// ==================================================

// 使用 TimeZoneInfo 先取得台北時區
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Taipei Standard Time");

// 再使用 TimeZoneInfo 來變更時間
var convertedDateTime = TimeZoneInfo.ConvertTime(nowDateTime, timeZone);

convertedDateTime.ToString("yyyy/MM/dd H:mm:ss zzz").Dump();
// 2020/08/30 23:56:05 +08:00
// 可以看到除了時間變更以外,時區也切換到 +08:00 了!

上面取得台北時區的步驟,可以參照 Time Zone IDs 來查詢想要的時區。這樣的時區切換方式,不僅副作用少,不會因為時區沒轉雷到後續接手的人,也省卻了擔心日光節約等等問題,這種事就交給微軟去煩惱吧!

……

閱讀全文


C#: 字串插值 (String interpolation) 的格式化

自從 C# 有了 字串插值 這東西之後,我就一直是愛用者。畢竟比起 string.format 這東西可是看起來優雅多了。例如:

var message = $"哈囉,{userName} 您的點數將於 {cutoffTime} 到期。";

簡潔又明瞭,一眼就能理解字串內容。實在是挺方便,後來發現這東西還有一些延伸用法,這邊就稍加紀錄一下:

字串插值中能夠做簡易計算,例如:

var message = $"您輸入的數值為:{a}、{b}。他們相加為:{a + b}";

同時,在字串插值時可以針對內容作格式化,只需要用 : 來區隔,妥善運用可以省下一堆 ToString() 的空間。

例如當我們要將時間格式化的時候,就可以:

var date = new DateTime(2020, 8, 9);
var message = $"您的商品已於 {date:yyyy/MM/dd} 抵達。";
// 您的商品已於 2020/08/09 抵達。

另外,數值當然也可以格式化,不過數值的應用比較複雜,主要是用來定下小數點、百分比等符號的位置。 可以參見 自訂數值格式字串 - Microsoft Docs

var cost = 2100;
var message1 = $"您的商品一共是 {cost:#,###} 元";
// 用 # 可以替數字預留位置
// 您的商品一共是 2,100 元

var message2 = $"您的商品一共是 {cost:#,###.00} 元";
// 也可以用 0 來預留位置,若該數字有值就會顯示該數字,沒有就會自動補 0
// 您的商品一共是 2,100.00 元
……

閱讀全文


C#: 使用 System.Environment 取得環境資訊、特殊資料夾路徑

有時候我們會需要取得一些系統資訊,例如說取得設備和當前使用者等資料來寫 Log,或是取得特殊資料夾路徑、讀取環境變數等等。這些時候就可以使用 System.Environment ,這邊就稍微紀錄一下用法。

先列出幾個常用的環境資訊,詳細可查詢的內容可以到 Environment Class 查詢:

資料夾路徑則需要用 Environment.GetFolderPath 搭配 Environment.SpecialFolder 列舉使用,該列舉包含資料夾可以到 Environment.SpecialFolder Enum 查詢。

……

閱讀全文


C#: 程式碼風格備忘

前言:本篇是整理公司規範和網路文章後,方便我自己在各個場所也能回來查閱使用的,故仍會不定時修改(畢竟我這人挺三心二意的)。另外本篇有重新調整過行距,發現有點跑版的朋友可以先 Ctrl+F5 一下,感謝閱讀。

如果你想知道的是如何寫出更優雅、更乾淨、品質更高的程式碼,那並不是該看這篇我個人的備忘錄,我會建議可以閱讀《無暇的程式碼》。或是可以參考這幾篇,我覺得都寫得很好:可不可以不要寫糙 codeClean Code 無瑕的程式碼閱讀筆記易讀程式之美學,共勉之。

如果你是正巧路過並且也寫 C#,希望這篇能讓你做為參考。但請記得,程式碼風格沒有絕對,最終還是回歸到團隊能否接受和將來的可維護性去考慮,畢竟教條是死的,人是活的。了解這樣做背後的原因,以及為自己寫的程式碼負責,這些都比對著隻字片語斤斤計較更加重要。

Nothing is true, everything is permitted. —— Assassin’s creed.

命名原則

想像下一個接手你程式碼的人是個暴力傾向的重度精神病患者

而且他知道你住在哪。  —— 《無瑕的程式碼》

  • 使用有意義的命名,請重視描述性。除了迴圈計數器例外
  • 盡量不要超過五個單字
  • 業界和慣例中有對應縮寫時可以使用縮寫
  • 承上,縮寫兩個字母時全大寫,三個字母以上時只第一字大寫

命名空間

  • 基礎類別庫:{組織} . {大類/應用範圍} . {小類/專案名稱}
    • MyStudio.Libs.Basic
    • MyStudio.Libs.Web.BaseTools
  • 專案類別庫:{專案名稱} . {子專案/類別/用途}
    • MyProject.Permiss.Proxy
    • MyProject.Permiss.Repository
……

閱讀全文


菜雞與物件導向 (2): 建構式、多載

我就直接說了,有用前面的程式碼區塊的人,絕對執行不了。因為我們建立哥布林和戰士這兩張卡片的時候,根本就沒有給他們數值呀!

雖然可以先呼叫出來再賦值…

var goblin = new Card();
goblin.Name = "哥布林";
goblin.Attack = 3;
goblin.Health = 2;
/* ...其他賦值 */

這實在相當占空間,也有點奇怪。畢竟如果是阿福(狗),一出生的時候應該就確定了一些特徵才對,例如品種、血型、眼睛顏色這種。並不會出生後過一陣子,才突然決定這些東西,既然如此,我們在產生物件的時候,當然也會希望在建立的同時就先決定好一部份內容

這時候我們就可以藉由建構式的方式,在建立物件時就進行一些我們想要的操作。

建構式

事實上,當我們呼叫 new Card() 的時候(不覺得這個 () 很有呼叫方法的感覺嗎?)我們就是正在調用 Card 的建構式。而當我們沒有特別去定義建構式的時候,就會直接使用內建的建構式去幫我們產生物件。

現在我們替 Card 新增一個建構式:

public class Card
{
    public Card (string name, int attack, int health)
    {
        this.Name = name;
        this.Attack = attack;
        this.Health = health;
    }
    /* ... 其他屬性和方法 */
}

在 C# 的時候,建構式必須和類別同名,且不需要定義回傳類型。當我們有了建構式,剛剛的例子就可以改寫成:

var goblin = new Card(name: "哥布林", attack: 3, health: 2);
var warrior = new Card(name: "戰士" , attack: 4, health: 3);
warrior.Hit(goblin);

建構式也就是建立這個物件時執行的函式,通常會用來進行初始化,也就是做一些建立物件必要的準備。例如傳遞必要屬性或是建構需要的其他物件、或是給予私有屬性初始值等等,例如說我們的卡牌一建立,就會需要知道它的名字和戰鬥力,這樣才有卡牌的感覺,而不該像一些 壞決鬥者 邊打牌邊偷偷印卡。

……

閱讀全文