AutoMapper 使用 ConvertUsing 自定義類型轉換,將包含串列成員的物件映射為一組串列

從朋友那兒聽到了用 AutoMapper 把串列成員物件攤平成一組串列的問題,發現了 ConvertUsing 的好用,這邊就紀錄一下。

事情是這樣的,首先有一個 Parent 類別,其中包含著兩個成員:Id 和串列的 Child 類別,而 Child 類別則只有一個成員 Val,如下:

public class Parent
{
    public int Id { get; set; }
    public IEnumerable<Child> Children { get; set; }
}

public class Child
{
    public double Val { get; set; }
}

另外還有一個 Target 類別,包含 IdVal 兩個成員:

public class Target
{
    public int Id { get; set; }
    public double Val { get; set; }
}

現在的目標是:將一個有著 Child 串列的 Parent 映射成 Target 串列

也就是說,假設我們的來源是這樣子:

var boo = new Parent
{
    Id = 1,
    Children = new List<Child>
    {
        new Child { Val = 1 },
        new Child { Val = 2 },
    }
};

希望可以變成這樣子:

var expect = new List<Target>
{
    new Target { Id = 1, Val = 1 },
    new Target { Id = 1, Val = 2 },
};

我之前遇到的時候,會直覺地將 Child 直接 Map 到 Target,再對 Target 做個 Foreach 來補上 Parent 的 Id。

這次和朋友討論時,提到了另一個角度:雖然這樣的做法相當直覺快速,但其實並不能保證後續維護的人使用這組 Mappings 時,都知道這裡要補資料;況且此處的對應關係的確是 ParentList<Target>,並非 ChildTarget 而已,直覺上就怪怪的。若要解決這個問題,可能就要再包裝一層,把 Mapper 隔離出去做個轉換器之類的。

但想想又覺得 AutoMapper 不可能沒提供這個場景能使用的方法才對,最後餵狗發現 AutoMapper 確實有提供 ConvertUsing 來讓我們客製化轉換過程,這邊就紀錄一下。

……

閱讀全文



菜雞新訓記 (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,故該有的參數檢查仍然不能漏了。

……

閱讀全文



菜雞抓蟲: Url 變得怪怪的?你可能是零寬空格(ZWSP)的受害者!

這週遇到個想不到的坑,特別來記錄一下。故事是這樣的--

在需要呼叫其他 API 服務時,發生了以下怪事:

  • 打某支查詢 API,突然查不到任何東西,或是跳出參數錯誤
  • 有些需要用參數組成 URL 的 API 跑出 Not Found
    • 第一組資料呼叫成功,第二組突然路徑錯誤
  • 寫入的時候,資料莫名其妙多了個 ?
    • 例如原先的資料是 ABC,不知怎地變成了 ABC?

由於這些操作都涉及到同一個參數,直覺上就是我們這邊給的參數出了點問題,馬上進入找犯人的環節。直接中斷點標記下去,反覆觀察該字串,但它就是一個普通的字串 "ABC",完全看不出什麼端倪。

正要覺得參數沒有問題的時候,赫然發現組出來的 Url 相當不對勁:在該參數的後方,多出了 %E2%80%8B 這串神秘東西!

當下我驚呆了,我們傳出去的 Url 裡,並不是預想的 /api/product/ABC,而是 /api/product/ABC%e2%80%8b!真是赤裸裸的背叛!這串鬼東西到底是什麼來頭?!

……

閱讀全文



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 有多好用呢?它輕量、它簡單、它快速。總之先把大神們的介紹文直接拿來鎮樓:

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

……

閱讀全文



系列文

轉貼文

最近文章

分類

標籤

友鏈

統計資訊

工商服務

    DDDTaiwan