只有累積,沒有奇蹟

2019年3月31日 星期日

[.NET] ILSpy 程式碼反組譯工具

前言
昨天上保哥非同步課程時有提到反組譯工具 ILSpy,詢問上課的學生發現聽過與使用過的並不多,聽到這點覺得有點訝異,自己曾在某間公司任職時因為 Production 上的重要程式沒有版控,僅能透過反組譯工具 ILSpy 或是 .NET Reflector 得知程式邏輯進而重寫該專案,後來 .NET Reflector 改收費後就都不在使用,這邊就針對 ILSpy 工具做簡單介紹與說明,若有問題歡迎提出一起討論或是給予指導。

ILSpy 安裝
ILSpy 是一套 Open Source 的反組譯套件,目前最新版為 5.0 preview 版本且支援 C# 7.3 與 8.0,可以透過兩種方式進行安裝,如下所示

官網下載
下載安裝檔可以透過 Github 網頁 : 傳送門
Chocolate 安裝
或是也可以透過 Chocolate 進行安裝,沒用過請服用此文 : Windows 套件管理工具 - Chocolatey 初體驗
choco install ilspy --pre
安裝完畢後點選 ILSpy.exe 開啟畫面如下
使用方式
左邊區塊為導覽,可以點選你想查看的 dll,右邊是反組譯的內容,舉例來說想了解 System.Net 底下有 WebRequest 的連線 timeout 時間,就可以發現程式碼定義預設逾時時間為 100000

載入 DLL
點選左上角 File > Open 可以載入 dll,一般開發 dll 都存在於專案目錄 bin 資料夾(project / bin)底下,如果是 .NET Core Console 專案,bin 底下則會有兩個資料夾分別是 net461 以及 netcoreapp2.2    
載入完 dll 後點選加入的 dll 名稱右邊就會出現反組譯後的內容,在與原始代碼相比較發現會有些不同之處,例如加上 private、new TimeSpan 變為 default(TimeSpan) 等編譯後的差異
class Program
{
    static TimeSpan total = new TimeSpan();
    static object tsLock = new object();
    static async Task Main(string[] args)
    {
        // 省略
    }
}
在上面 Toolbar 選項還可以選擇查看 C# 或是 IL Code,以及透過各個 IDE 與 C# 版本編譯後 Code 內容 (預設是最新 C# 8.0 / VS2019),從中可以發現編譯出來的代碼會因為版本不同會有所差異,可以參考下圖
透過以上說明,可以了解使用上十分容易上手,如果想了解更多細節與內容,可以參考官網說明 : ILSpy

參考
ILSpy

2019年3月28日 星期四

[UnitTest] ASP.NET Core 2.2 測試專案中的版本衝突

問題 
這幾天專案某項功能接近尾聲,要替其核心 ASP.NET Core 專案加上單元測試專案,加入後按下建置發現跳出 Error 錯誤訊息  "CS1705 Assembly 'xxxx, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' uses 'Microsoft.Extensions.Logging.Abstractions, Version=2.2.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' which has a higher version than referenced assembly 'Microsoft.Extensions.Logging.Abstractions' with identity 'Microsoft.Extensions.Logging.Abstractions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' "  加一個測試專案就造成參考 dll 版本不同,且測試專案並未加入任何參考類別庫就建置失敗怎麼想都不太對,這篇就針對此案例作簡單紀錄與分享若是有不清楚或是錯誤的地方歡迎討論予糾正

解決方法 
就自己過去經驗這類訊息是因為專案參考版本不同造成,但為了保險還是看一下案發現場錯誤訊息提到的 CS1705 ,提示訊息屬於編譯器錯誤,傳送門 
接著到專案與測試專案的參考進行比對,比對其 dll 的版本與新加入的測試專案 dll 版本號,經過地毯式的比對後發現其錯誤訊息提到的 dll 版號皆為相同並無異常,因此判定此推論不成立
接著,想到錯誤訊息提到 .NET Core 版本不同,因此開啟專案檢查兩個專案版本差異,檢查方式如下
專案按右鍵 > 屬性 > Application > Target Framework,經比對後兩者皆為 ASP.NET Core 2.2 版無誤,猜測再度失敗 !
快要走投無路的時候發現在微軟 github 也有許多人遇到類似的問題,討論傳送門如下
 Version conflicts in test project depending on a Microsoft.AspNetCore.App project #2253
而且在 ASP.NET Core 2.2 此問題似乎尚未解決 (狀態還沒 close),整理一下共通的症狀如下
  • 測試專案
  • 無法編譯的 dll 為 Mircosoft.AspNetCore、Mircosoft.Extension 
如果符合以上疑似症狀,嘗試了各種方法還是沒有辦法建置成功的話,請服用以下步驟
Step 1 : 專案按下右鍵,編輯專案檔 csproj
Step 2 : 在  itemGroup  區段加入下列代碼 (如果在不行可以指定版本)
<PackageReference Include="Microsoft.AspNetCore.App" />
接著在重新將專案建置一次,就可以看到建置成功的畫面 (淚)
擦乾眼淚宣告懸案正式結案 !! 

心得
神奇的是只要建置成功過一次之後,就無法再還原發現場狀況,不指定 Mircosoft.AspNetCore.App 版號也可以正常建置成功 (嚇.jpg),不知道算不算是 .Core 2.2 的bug,如果是希望新版出來時可以修復此問題,不然光看錯誤訊息要修復可能真的不太容易。

參考
https://github.com/dotnet/sdk/issues/2253

2019年3月27日 星期三

[.NETCore] ASP.NET Core 學習資源包 - awesome dotnetcore

前言
最近比較多機會嘗試有興趣的新技術,在學習過程中勢必會遇到很多疑難雜症;像是最近新專案使用 ASP.NET Core 開發,有些觀念如果你 ASP.NET Framework 習慣了轉到新的會需要點時間,這時如果有份清單整理技術領域相關內容是在好不過的了;今天要分享的是 github 上的 awesome dotnet core,集合群眾的力量整理 ASP.NET Core 的學習資源與相關 Library 資訊,另外如果有好康的想要分享給大家也可以發 pull request 加上去,對此有興趣的人可以點進去看看

介紹
Awesome .NET Core 的靈感來自於其他 awesome 系列,上面列的 .NET Core 資源除了 open source 之外也有提供商業版的軟件,其內容包含了官方網站文件、框架類別庫、模板、或是常用到的身分驗證、AI、EMail 等各式各樣的資訊都涵蓋在其中,點擊連結都可以連到相關主題的 github 說明頁面

像是最近在研究 Logging 機制,到 log 區塊就可以發現支援的 log framework 種類清單,像是常見到的 log4Net、NLog、最近常看到的 serilLog 等都包含在介紹頁面中
移到最後很貼心的還有整理出像是 .NET Core 相關討論社群、技術 Blog、電子書跟影片資訊,可以說是十分的豐富,不虧是在 github 上累積 8000多顆星的專案;當然,如果發現有好康的東西想要分享給大家,也可以 fork 下來發 pull request 給建議,自己也有發送 PR 在等待版主大大 approve 中,如果有需求歡迎到網頁逛逛,Enjoy it !。

同場加映
有 Awesome .NET Core 當然也有 Awesome .NET,廢話不多說直接附上 傳送門
參考
awesome dotnet core

2019年3月24日 星期日

[IIS] 程序無法存取檔案,因為檔案正由另一個程序使用。(發生例外狀況於HRESULT:0x80070020)

問題
今天要在公司測試 Server 建立測試站台,在完成設定 Application Pool 與站台指定位置後按下啟動,跳出'程序無法存取檔案,因為檔案正由另一個程序使用。(發生例外狀況於HRESULT:0x80070020) 錯誤訊息,訊息內容看似有檔案被 lock 住造成啟動異常,但追根究底之後會發現其異常原因蠻單純的以下就針對解決此問題的方式做說明,若有問題歡迎提出一起討論或是給予指導。

解決方案
根據過去經驗,根本原因很有可能是因為 IIS port 被佔用造成啟動異常,因此第一步是先確定目前設定站台的 Port 號是否已使用,步驟如下

查詢 PID
測試站台使用 port 號為 6379,在 windows 中可以下  netstat -ano  指令查看哪個 process 咬住 port
PS C:\> netstat -ano 
可以得知 ProvessID 13276 目前佔用 6379 Port

工作管理員
開啟工作管理員切換到詳細服務 tab,從 PID 13276 得知是 vpnkit.exe,後面參數是 -- ethernet hyperv,vpnkit.exe 是 Docker 的一部分,因此需要在 docker 中下指令確認更多資訊
Docker
透過 docker ps 指令確認目前執行的 container 中是否含有佔用 6379 port,得知在測試 server 中 Redis 已佔用此 port由於 IIS 站台設定的 Port 被佔用,Bingo !! 兇手就是 Redis 了
PS C:\WINDOWS\system32> docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
e819ea3d7a7b        redis               "docker-entrypoint.s…"   5 weeks ago         Up 37 minutes       0.0.0.0:6379->6379/tcp   redis-lab 

處理方式
處理方式就是調整該測試站台的對應 Port,至繫結調整指定新的 Port ,設定完畢後即可正常執行

心得
一開始看到錯誤訊息內容時,以為是在佈署檔案時檔案被咬住造成的失敗,但後來仔細思考 IIS 站台根本還沒起來怎麼可能會有咬住檔案的情形發生,因此就從 Port 思考方向去釐清,經驗證後果然猜測沒錯成功解決,但其錯誤訊息文字乍看之下也太容易猜錯方向,也提醒自己在寫 Log 時也是要關注異常時顯示的內容訊息,否則當其他同事在值班時也會遇到類似問題,也會讓追問題的時間拉長造成他人的不變。

2019年3月22日 星期五

[.NETCore] 如何取得 appsettings.json 組態設定

問題
在開發時有時會將資訊紀錄在 Web.Config 設定檔中的開發經驗,在 .NET Framework 要取得 web.config 自訂組態設定時可以透過  ConfigurationManager.AppSettings ,相信大家都有了解且不陌生;但今天場景換到 ASP.NET Core 時,在一開始建立好新專案後只有  appsettings.json  設定檔,那麼該如何取得 json 檔案中自己定義的組態設定內容呢 ? 今天就是要分享的是在 ASP.NET Core 中取得 appsettings.json 自訂組態設定值的方式若有問題歡迎提出一起討論或是給予指導

解決方案
一開始我先在 appsettings.json 自訂 Member 區段,其中裡面定義 Account 為 Marcus 與 Password 為 1234qwer 其組態設定屬性與相對應的值 
"Member": {
    "Account": "Marcus",
    "Password": "1234qwer" 
  } 
設定好 appsettings.json Member 的組態設定之後,接下來步驟就是要取得其 config 的內容,在 ASP.NET Core 中一開始執行的 Startup 類別就會透過 DI 注入  IConfiguration  組態設定檔內容,代碼如下
public Startup(IConfiguration configuration)
{
    Configuration = configuration;
} 
 IConfiguration  是在  Microsoft.Extensions.Configuration  namespace 底下的 interface,其內容是用 Dictionary 的方式來進行讀取以及存放,此 interface 取得可以透過用 key、GetSection、GetChildren 取得其組態設定資料或是資料集合,如下圖所示
簡單介紹了 IConfiguration 介面,知道了預設會將組態設定檔的內容會用 Dictionary 的方式存放在 IConfiguration 的 instance 中,下一步就是如何取得我們想要的組態設定內容值,步驟如下

建立類別
首先先建立一個相對應的類別 MemberConfig
public class MemberConfig
{
    public string Account  { get; set; }
    public string Password { get; set; }
} 

注入 IConfiguation
指定一個型別為 IConfiguration 的屬性 _config,並在 Controller 建構子時透過 DI 注入 IConfiguration 
private IConfiguration _config;
public ValuesController(IConfiguration config)
{
    _config = config;
}

使用 GetValue<T> 取得組態設定內容
先 new MemberConfig 的類別,接著可以使用 IConfiguration 的擴充方法 GetValue<T> 帶入 Key 取得想要的組態設定值,以上述的範例是輸入 Member:Account 可以取得 account 資訊,輸入 Member:Password 取得 Password 資訊,並將其結果輸出至網頁上確認是否正確 
[HttpGet]
public ActionResult<string> Get()
{
    var memberConfig = new MemberConfig();
    memberConfig.Account = _config.GetValue<string>("Member:Account");
    memberConfig.Password = _config.GetValue<string>("Member:Password");

    return $"Account is {memberConfig.Account} Password is {memberConfig.Password} ";
}
輸出結果如下
解決方案 - IOptions
如果覺得使用 GetValue<T> 需要逐一設定有點麻煩,則可以參考 IOptions 方式注入 MemberConfig,建立強型別的類別步驟還是需要,步驟如下

在 ConfigureServices 中加入 MemberConfig 配置
在 Startup 檔案中  ConfigureService  加上以下代碼
services.Configure<MemberConfig>(Configuration.GetSection("Member"));

注入 IOptions
並在 Controller 建構子時透過 DI 注入 IOptions,其型別為 MemberConfig 
public MemberConfig _memberConfig { get; set; }

public ValuesController(IOptions<MemberConfig> memberConfig)
{
    _memberConfig = memberConfig.Value;
}

輸出資訊
在輸出端整體就變單純許多,可以直接使用 memberConfig 類別內容,不用在逐一取得組態設定資訊內容,輸出結果相同這裡也就不在重複PO圖
[HttpGet]
public ActionResult<string> Get()
{
    return $"Account is {_memberConfig.Account} Password is {_memberConfig.Password} ";
}

感想
最近接觸 ASP.NET Core 越多越能發現 DI 注入在 .NET Core 上可以說是無所不在,從 Logging 到 Config 檔甚至其他更多的應用,在撰寫相關代碼時也幾乎不會看到 new ClassA() 的出現,從設計面著手透過 DI 來避免耦合度過高的問題產生,之後如果對 DI 有更多了解也希望自己可以整理出一篇文章,讓自己可以更了解帶來的好處與價值,以上如果有問題歡迎提出討論,Happy Coding !
    參考
    Configuration in ASP.NET Core

    Copyright © m@rcus 學習筆記 | Powered by Blogger

    Design by Anders Noren | Blogger Theme by NewBloggerThemes.com