分類 CSharp-and-Dotnet 中的文章

C#: 使用 DinkToPdf 把 HTML 轉成 PDF 吧

Image

最近做的一項需求是要把 HTML 轉換成 PDF,過程中決定使用 DinkToPdf 來處理這一段。

考慮到現在拿到的一些文件模板都是 HTML 檔了,感覺以後會蠻常碰到這個場景,決定來筆記一篇,給未來的我複製貼上。

環境準備

首先,當然是要先到 Nuget 安裝這篇的主角:DinkToPdf

Image

由於 DinkToPdf 只負責包裝給 C# 這段,實際上要產出 PDF 還得用到 wkhtmltopdf 這個工具,因此還需要先弄到 wkhtmltopdf 的檔案。

不過作者也知道大家不是很想另外跑去找,所以 wkhtmltopdf 的組件檔案可以直接從 DinkToPdf 的 Repo 拿。但要記得要按照作業系統下載對應的 wkhtmltopdf 檔案:

  • Windows => libwkhtmltox.dll
  • Linux => libwkhtmltox.so
  • macOS => libwkhtmltox.dylib

但全部都載也不會有人阻止你就是了。

最後把 libwkhtmltox 放到專案的根目錄底下,DinkToPdf 就呼叫得到囉!
(問就是魔法,不服的自己去啃 P/Invoke

……

閱讀全文


使用 AddTypeMap 調整型別映射,讓 Dapper 乖乖寫入 0001-01-01 到 datetime2

Image

這週忙著打黑悟空,簡單記一下前陣子同事遇到的一個場景:

由於曆法的關係,SQL Server 的 datetime 範圍只能從 1753-01-01 開始。
如果我們要寫入 0001-01-01 進去的話就會報錯

SqlDateTime overflow. Must be between 1/1/1753 12:00:00 AM and 12/31/9999 11:59:59 PM.

補充:關於曆法的部份,有興趣的朋友可以參考這篇:
淺談:消失的日期,以關聯式資料庫的日期資料類型為例 - 德瑞克:SQL Server 學習筆記

這時候通常去把資料表欄位改成香香的 datetime2 就可以搞定,爽爽寫入 0001-01-01。

但如果我們正在使用 Dapper,預設會把 C# 的 DateTime 映射到 SQL Server 的 datetime,所以還是會寫入失敗。

這時候我們就需要明確地請 Dapper 幫我們使用 datetime2 來進行處理。


如果傳遞的只是某個參數,使用 DbType 就行了:

var parameters = new DynamicParameters();
parameters.Add("@Birthday", birthday, DbType.DateTime2);

但如果傳遞的是某個 Model 裡的特定欄位呢?例如:

public class HistoricalEvent
{
    public Guid EventId { get; set; }       // 歷史事件的唯一識別碼
    public string Description { get; set; } // 歷史事件的描述
    public DateTime EventDate { get; set; } // 歷史事件發生的日期, 可能早於 1753 年!
}

這種時候就可以使用 AddTypeMap 來調整一下 Dapper 的映射型別。

……

閱讀全文


.Net: 使用 FeatureManagement 套件來實作功能切換(Feature Toggle)吧

Image

在做上一篇 IOptions 的筆記時,剛好看到 FeatureManagement 這香東西。
馬上來收錄一篇。順便也簡單整理一下 Feature Flag (≒Feature Toggle) 的介紹。

認識一下 Feature Flag

本部落格秉持著「簡單、友善、我好菜」的精神,按照慣例先簡單介紹一下
已經知道的朋友就可以跳過這個小節,直接前往 #環境準備 囉。

假設我們原本有 Old 邏輯:

Old();

天庭傳來諭令,要我們改成 New 邏輯。這簡單,我們就把 Old 砍掉,換成 New。非常自然,改完就佈版

New();

隔天,天庭又傳來諭令,New 需要調整一下,先不要了
現在我們又需要把 New 邏輯砍掉,讓 Old 邏輯回來。
簡單,但看來我們得再上一版

Old();
// New();

再隔天,大家可能猜到天庭又要幹嘛了,總之又上了一版

// Old();
New();

如此往復三萬八千次,工程師終於受不了了:「俺老孫每天在這切換 Old 跟 New,改完還得佈版,每天搞這些就飽了,我滴媽呀,不幹了」

就在老孫關燈走人的那一瞬間,突然靈光一現:等等,俺加個開關還不行嗎?

……

閱讀全文


.Net: 使用 IOptions 取得 appsettings.json 的設定值吧

Image

因為手邊的 .Net Core API 專案越來越多,蠻常會需要讀 appsetting.json 的 Config,每次都要重找文章有點麻煩,這邊就來筆記一篇。

認識 Appsettings.json

首先簡單介紹一下 appsettings.json 是在幹嘛的:

我們開發的時候,常常會需要先設定好一些服務參數、組態設定之類的設定值,再用這些設定值用來控制我們程式的某些行為。例如:

  • 「某功能的上限值是 10
  • 「某項開關在測試環境是 false
  • 「某服務信件的發送者要用 noreply9527

另外常見的還有連線字串、寫 Log 時的 logging level 等等。

這些設定值會和程式碼拆開,放在設定檔集中管理,再讓程式碼從設定檔讀取相關的設定值來用就好,藉此來把設定值的管理和使用做個關注點分離。

有了設定檔,要調整修改也比較方便:要增加或是修改設定值,都只要先往設定檔衝就行。並且因為兩邊拆開了,我們也就可以簡單地替換這些設定值來應對不同狀況(例如正式環境和測試環境套用兩組不同的設定檔,或是在自己電腦測試的時候快速改個值之類的),彈性可說是 UPUP!

更重要的是,這樣我們就不需要把一大堆東西寫死在程式碼的各個地方,也就不會要改個值還要先搜尋整個專案再一個一個挖出來改了。我按 Shift Ctrl F 已經按到哭

把設定值抽出去丟到設定檔之後,我們就得到了:集中管理設定值、方便修改和替換、減少程式碼中又重複又寫死的臭東西等等好處。

在 .Net Core 的世界裡,這個設定檔就是 appsettings.json
前面提到的像是日誌等級、功能旗標之類的這些設定值,就會放在 appsettings.json 裡面。

而當我們想要從 appsettings.json 把這些設定值給讀出來的時候,
就可以使用我們的 IOptions 啦!

使用 IOptions 來註冊 & 注入

提醒:這篇的示範會用到一些些 .Net 依賴注入(DI)相關的操作。沒接觸過的朋友可以考慮先閱讀菜雞新訓記:依賴注入

假設我們有個專案,叫做大漢防禦管理系統。專案內的 appsettings.json 有以下內容:

{
  "StrongholdInfo": {
    "Index": 49,
    "Name": "劍閣",
    "Enabled": true, 
    "General": [
      "姜維",
      "廖化",
      "張翼",
      "董厥"
    ]
  }
}

我們正好在開發一個新功能,需要抓到這段設定值。現在就來示範一下:

……

閱讀全文


使用 .Net 的 System.Drawing 產生簡單的文字 Banner 初體驗

Image

原本我都是用產生 FB 封面的「康熙字典體產生器」來做簡單的文字 Banner,就拿來當作文章的封面照

用了好一陣子也沒啥問題。結果某天文章寫好,吃著火鍋唱著歌,產生器打開一看,服務竟然就沒了!

當下是一個震驚啊,一氣之下決定直接打開 Linqpad 寫一個。

註:現在搜尋康熙字典體產生器,還查得到介面截圖,還真的蠻簡單方便的 Q_Q

以前面的菜雞與物件導向系列 Banner 為例,我們大概需要:

  • 產生一張圖
  • 在圖上面放主標題和副標題
  • 關鍵字可以上色

Image

稍微搜尋一下發現 .Net 已經有 System.Drawing 這個工具可以幫我們完成這些簡單的圖片任務,事不宜遲馬上就來嘗試!

……

閱讀全文


Polyglot Notebooks -- 在 VSCode 簡單迅速地撰寫 C# 腳本吧

Image

像我這樣的菜鳥工程師,在 Coding 的時候時常會需要簡單試一些想法。像是「我這邊下了這串 Linq 出來的資料,到底是不是我想要的內容啊 = =?」

或是在進行開發任務時:「這個步驟能不能這樣做啊?先拆個簡單的 Method 試試看好了」之類的

以往遇到這種時候,我都會打開香香的 Linqpad 直接無情開寫,快速地作個小小的概念驗證。

畢竟 Linqpad 可以迅速地開始撰寫簡單的 C# 腳本,並且有良好的語法提示、Nuget 支援和方便的資料庫連線,省去我還要開一個 Console 專案自己弄這些東西,更可以存著之後備查,所以一直以來我都愛不釋手。

但在因緣際會下(其實就是閒逛論壇的時候),發現了微軟把拔出的 Polyglot Notebooks 這款 VSCode 擴充套件。當下驚為天人!Linqpad 在我心中的地位就這麼動搖了

Polyglot Notebooks 可以讓我們直接在 VSCode 上面撰寫簡單的 C#、F#、JavaScirpt 等腳本、迅速驗證想法。甚至可以對程式碼分段、加入 Markdown 文檔,讓我們能更有邏輯、有步驟地撰寫我們的腳本、處理我們的資料。

它的特色是這麼對我的胃口,看來我以後鐵定是會用到的。現在就來簡單記錄 Polyglot Notebooks 的使用方式吧!

……

閱讀全文


C#: 使用 AngleSharp 爬蟲工具來抓取網頁內容吧

Image

前一次用到 AngleSharp 已經是去年抓網路小說的時候,想不到最近又用上了,乾脆就來筆記一下。

AngleSharp 是一款簡單方便的 C# 爬蟲套件,撈網頁時支援 QuerySelector 的語法來篩選網頁元素,並且撈回來的資料集合也都能用 Linq 操作,讓我們能對爬取的網頁內容快速進行篩選和處理,只需要短短的語法就可以開心抓想要的內容。

說到要示範爬蟲,果然還是要用爬蟲界默認的經典範例 PTT 表特版 來操作(?),接著就讓我們來寫一個簡單的腳本來抓取文章吧!

……

閱讀全文


菜雞新訓記 (7): 使用 Fluent Validation 來驗證參數吧

Image

這是俺整理公司新訓內容的第七篇文章,目標是紀錄 Fluent Validation 這個好用套件。

FluentValidation 可以幫我們將 Api 傳入的參數的檢查用更口語、更乾淨的方式去處理,除了可以將檢查邏輯拆分成單獨的 Validator 類別,更提供了許多內建的檢查規則和自訂的彈性,相當方便。

並且因為將參數的檢查邏輯整理出去,就可以和 Controller 本身的工作做簡單的拆分,達到關注點分離的目標。

現在就讓我們來認識一下這個好用工具吧!首先要從很久很久以前開始說起…

前言

西元前的某一天,憂心的皇帝在朝堂內繞著柱子走,突然大臣奪門而入。

大臣:「陛下!敵軍已經攻到國境內啦!」

皇帝大驚:『邊境的那些檢查站和關口難道都陷落了嗎?不可能!』

大臣:「陛下,有內奸和敵國勾結,檢查站完全沒檢查!髒資料已經闖進來了!」

皇帝喊了一聲:『怎麼可能!讓朕看看!』就打開 Controller 和前一個版本的 Git Log,這一看差點就昏了過去。

原來 Controller 的舊程式碼就已經很亂了,檢查參數的條件 if/else 和其他呼叫的方法、組裝資料都雜在一起。結果這次專案改動時,某一行就被內奸改壞了,關鍵的參數竟然沒檢查到!

『可,可惡!來人啊,把工程師推出午門斬首!』

「皇上!他已經離職啦!」

皇帝跌坐在地,懊悔地說:『如果當初有好好把檢查參數跟實際組資料的部份都拆開的話,也許就不會這樣了…』

「是啊,如果我們有用 Fluent Validation…!」

專案現況

大臣提到的 FluentValidation 是一套能幫我們把傳入參數的分離出去、用更口語化的方式去撰寫的工具。

……如果當時他們有使用 Fluent Validation 來把驗證的邏輯和規則跟原本很亂的 Controller 切分的話,說不定就能及時發現問題吧,大概。

為了不要步上他們的後塵,就讓我們直接回到本系列的卡牌管理 API 服務來加上這個好用工具吧!

……

閱讀全文


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 自己人,所有人事異動,須由中央進行管理與派遣。這樣即使諸侯要造反,也不知道下面這群打工仔是不是自己人。大家各司其職,諸侯做好自己的行政作業,打工仔派到崗位就做好自己的工作,彼此不直接依賴,這樣出事的機率就少了。

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

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

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

……

閱讀全文


系列文

轉貼文

最近文章

分類

友鏈

標籤

統計資訊

工商服務

    DDDTaiwan