包含標籤 CSharp 的文章

菜雞新訓記 (6): 使用 依賴注入 (Dependency Injection) 來解除強耦合吧

Image

這是俺整理公司新訓內容的第六篇文章,目標是紀錄什麼是依賴注入(Dependency Injection)。包含:

並用 .net Core 實際跑一次依賴注入藉由將控制權轉移給注入容器,解除分層與分層間、類別與類別間的依賴和耦合關係,達到以介面分離實作的目標

前言

西元前的某一天,憂心的皇帝在朝堂內繞著柱子走,正巧被路過的廷尉看見。

廷尉:「敢問陛下在煩惱什麼呢?」

皇帝:『朕這是在想封賞的事兒哪。前朝之所以覆滅,根本的原因就在於大肆封賞臣下,四處分封土地給他們做諸侯。

這些諸侯呢,肆意起用自己喜歡的人擔任要職、結黨營私,心情好就 new 將軍("我ㄉ朋友");
十天就封了十個將軍。這些人若犯了錯,要處理他們還得看諸侯面子;而諸侯一聲令下,這些人便群起造反。

並且,這些諸侯之間彼此喜歡直接往來,動不動就在自家裡下命令給 隔壁諸侯.借糧草(100),哪天就變成 隔壁諸侯.揪團造反()。彼此之間偷來暗去,實在難以掌握。

最後呢,一個逆賊起來造反,若要將他給辦了,附近諸侯就一起響應,每個都一齊報錯,Exception 成千上百,國家也就這樣滅了,想到這朕就頭痛得很,不知愛卿可有法子?』

廷尉想了一想,便說:「陛下,此事要點還是在於諸侯之間相互依賴、彼此耦合,致生禍端。

臣有一計,先收回諸侯的人事任命權,使其不可私自 new 自己人,所有人事異動,須由中央進行管理與派遣。這樣即使諸侯要造反,也不知道下面這群打工仔是不是自己人。大家各司其職,諸侯做好自己的行政作業,打工仔派到崗位就做好自己的工作,彼此不直接依賴,這樣出事的機率就少了。

其次,明令禁止諸侯私自往來,對諸侯們進行隔離,若是有公務上的需要,一律藉由中央提供的接口來溝通,彼此之間明訂契約,由中央進行隔離與調派,諸侯間就只需要按照協議好的合約下去合作,這樣勾結的機會也就少了,耦合也就降低了。陛下覺得如何?」

皇帝大喜:『如此甚好!治眾如治寡,在於分而治之。此計可有名字?』

「此乃--依賴注入之計!」

……

閱讀全文


在 Swagger UI 加上驗證按鈕,讓 Request Header 傳遞 Authorize Token

Image

在先前的 菜雞新訓記 (4): 使用 Swagger 來自動產生簡單好看可測試的 API 文件吧 中,我們介紹了在 .net Core 環境使用 Swashbuckle 套件來產生 Swagger 文檔,並且直接在 Swagger UI 中呼叫 API 來進行測試。

但很多時候,我們的 API 會需要先驗證才能使用,例如在 Header 傳遞 Token 來驗證身分等等。這時候 Swagger UI 就會整個廢掉,打了都會出錯,很不方便。

因此這篇文章就紀錄一下如何在 Swagger UI 上加入 Authorize Token 的傳遞,讓 Swagger UI 在需要身分驗證的環境也能直接呼叫使用。

……

閱讀全文


菜雞抓蟲: DateTime.ToString() 之我們不一樣 & CultureInfo 文化特性小筆記

Image

事發緣由

咱們內部套件中有個方法,會將各個參數組合為 QueryString 去打指定的 Api。就是這麼稀鬆平常的場景,神奇的事情就發生了。

同樣的套件、同樣的語法,在團隊中兩個人的電腦上安裝執行,卻是一個成功一個失敗。

原來該方法的參數中,包含一欄型別為 DateTime 的資料,並且會把該欄位的值拿來 ToString() 再做為參數傳遞給目標 Api。

而呼叫失敗的人就是在這個 DateTime.ToString() 的過程中產生了中文字,使得目標 Api 接到參數後,無法將中文字轉換回 DateTime 而發生了錯誤。

問題就浮現了:同一行 DateTime.ToString() 在不同電腦執行的結果竟然不一樣?!

為了讓我們更快了解狀況,現在就簡單地使用 Linqpad 進行測試:

void Main()
{
    DateTime.Now.ToString().Dump();
}

首先,在我的 Windows 時間設定中,完整時間的格式為 09:40:07 ,也就是 24 小時制。

現在讓我們先執行上面這段語法看看:

// 2021/10/04 21:00:00

接著讓我們變更時間格式看看。

以我的 Win10 為例,在 Windows 工具列,也就是畫面的右下角右鍵,選擇 調整日期時間 → 日期時間格式設定 → 變更資料格式,將時間格式變更為 上午 09:40:07

重新啟動 Linqpad 再執行如下:

// 2021/10/04 下午 09:00:00

可以看到 下午 兩個字就蹦出來了!

之所以會有這樣的差異,是因為 DateTime.ToString() 預設轉換的目標格式會是抓取目前執行緒的文化特性

……

閱讀全文


菜雞新訓記 (5): 使用 三層式架構 來切分服務的關注點和職責吧

img

這是俺整理公司新訓內容的第五篇文章,目標是使用三層式架構 (3-Layer Architecture) 來切分服務的關注點和職責

什麼是分層?分層可以吃嗎?

天地混沌如雞子,商業邏輯生其中。

萬八千歲,天地開闢。表現層為天。資料層為地。商業邏輯層在其中……

    --民明書坊《盤古與他的CRUD之旅》

根據民明書坊的文獻記載,我們常聽到的「天地玄黃,宇宙洪荒」云云,其實指的就是上古時期的開發狀況。當時世界還是一片混沌,所有的程式碼都混雜成一坨,不是所有東西寫在一起你儂我儂,一言不合就三千行;就是依賴關係交錯複雜,改了北極壞南極。

要說有多亂呢,大概就算前人嘗試引入了 MVC,也只是改成把所有程式都塞在 Controller 而已,其絕望程度可見一斑。

這時候隔壁課的老盤調過來接刀,一看不得了,便決定先對這屎山整頓一番。他大喝一聲,那些靠近使用者的便上浮起來化作了天,親近資料庫的便沉澱下去變成了地,而所有的商業邏輯就連接著兩者,支撐起了整個專案。這也就是分層架構的由來。

三層式架構

分層架構是運用最為廣泛的架構模式,幾乎每個軟體系統都需要通過層(Layer)來隔離不同的關注點(Concern Point),以此應對不同需求的變化,使得這種變化可以獨立進行;此外,分層架構模式還是隔離業務複雜度與技術複雜度的利器。 -- Ray’s Notes

一般來說,最常見的分層架構就是三層式架構了。

三層式架構顧名思義就是把應用程式分成三層,通常會分成「展示層、商業邏輯層、資料存取層」。

分層架構01

……

閱讀全文


菜雞抓蟲: 使用 FromUri 的複雜型別在有傳遞 QueryString 的情況下會先建立再賦值

Image

事發緣由

在 .net Framework 4.6.2 MVC 的 ApiController 中,某個查詢資料列表的方法除了提供查詢條件的參數以外,還有提供選擇性的分頁參數。也就是像這樣子:

[HttpGet]
public IEnumerable<Boo> GetBoos(
    [FromUri] SearchBooParameter parameter,
    [FromUri] PagingParameter paging = null)
{
    // 呼叫 Service 查資料...
}

由於需要調整該功能的預設排序,改為由大到小,又不想背負更改大量共用的 PagingParameter 去影響到其他使用到的地方,決定在 Controller 這裡簡單用預測值加上判斷處理一下就好

相信著「若使用者沒有傳遞 paging 相關的參數,應該就會是給定的預設值 null 吧!」的我,用了 if (paging is null) 進行判斷:若是 null 的情況就將其中用來標示排序方向的成員 isDesc 設定為 true,開開心心交差。

[HttpGet]
public IEnumerable<Boo> GetBoos(
    [FromUri] SearchBooParameter parameter,
    [FromUri] PagingParameter paging = null)
{
    if (paging is null)
    {
        paging = new PagingParameter();
        paging.isDesc = true; // 預設由大到小
    }
    // 呼叫 Service 查資料...
}

但實際使用之後發現:即使只有傳入查詢條件參數、未傳遞 paging 時,資料仍然由小到大顯示,且 paging.isDesc 竟然是 false,並未被更改到。也就是說,即使未傳遞 paging,它也並不是 null!

實測之後發現:若在呼叫該 API 的時候,給定一個完全無關的參數,例如 ?a=1,則 paging 還是會被建立一個實體出來,並無視 = null 這個預設值。因此就導致了非預期(=跟我想的不一樣啊!)的行為。

這邊直接先講結論:如果有傳遞 QueryString 的任何參數時,不管這些參數跟指定的類別有沒有關係,放在 [FromUri] 的複雜型別都會先建立出實體,再嘗試和 QueryString 的內容進行比對與設值

設定在 [FromUri] 的複雜型別身上的預設值,像是 [FromUri] PagingParameter paging = null 只有完全沒給任何 QueryString 的時候才會吃到。(不過因為預設值只能是常數的關係,基本上就是指 defualt 的 null)

因此如果遇到要給定預設值的場合,還是得乖乖地針對型別中的成員做設定比較保險,例如 bool isDesc { get; set; } = true。另外,因為完全沒給 QueryString 的時候還是會是 null,故該有的參數檢查仍然不能漏了。

……

閱讀全文


C#: BenchmarkDotnet —— 效能測試好簡單

「你寫那什麼鬼東西?這個OOO寫法比較好啦!」
『聽你在屁!明明是這個XXX寫法快= =』

哇喔!等等!想戰效能嗎?那你一定需要這款 BenchmarkDotnet

介紹與安裝

我們在 Coding 的時候,或多或少都會有「不知道這兩個寫法哪個比較好?」、「聽說A寫法比B寫法快,真的嗎?」這類關於效能的疑問。

在遠古時期,當我們需要驗證這種想法,可能就要用記錄秒數的方式,或是搭配迴圈、然後再印在畫面上等等這類土法煉鋼的方式。

然而這種單純計秒數的 Print 流測試,可能比較到了時間成本,卻忽略了吃掉的記憶體這些空間成本;又或是每次都要插一堆列印文字的語句,因為麻煩就萌生退意等等…

這時候就是 BenchmarkDotnet 出場的時候啦!

BenchmarkDotnet 是一款簡單好用的效能比較工具,可以幫助我們比對多組程式碼,並告訴我們平均的執行時間、耗用的記憶體等等。

只要使用 BenchmarkDotnet 這個神奇妙妙幫手,它就能幫我們搞定這些麻煩的事情,讓我們可以專注在要測試的程式碼內容囉。

……

閱讀全文


菜雞新訓記 (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,於是我們到了提供公車動態的運輸資料服務 TDX (Transport Data eXchange) 去找我們想要的 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
……

閱讀全文


系列文

轉貼文

最近文章

分類

友鏈

標籤

統計資訊

工商服務

    DDDTaiwan