只有累積,沒有奇蹟

2018年12月28日 星期五

[UnitTest] Visual Studio 2017 按右鍵無法建立單元測試 ?

問題
最近心血來潮使用家中舊電腦小白寫 Code,在練習測試中發現竟然有點怪異,在要測試的 method 按下右鍵沒有 建立單元測試 Create Unit Test 選項但相同練習專案拿到公司筆電就是正常的,經比對後發現舊筆電 Visual Studio 版本少安裝測試功能,以下簡單紀錄解決問題的過程

解決方案
在 Visual Studio 2017 早期版本這是已知問題,有開發者在 vs community 回報給開發團隊,團隊回覆已修覆此問題,討論連結如下
https://developercommunity.visualstudio.com/content/problem/24835/create-unit-tests-missing-when-right-click-in-meth.html
這裡介紹兩種解決方案,我自己是兩種都設定完才生效 

更新 Visual Studio 
Step 1. 開始 > 輸入 Visual Studio Installer
Step 2. 如果太久沒更新 Visual Studio Installer 會要求你一併更新安裝程式 (OS : 我舊電腦是有多舊 XD
Step 3. 更新完畢 Visual Studio Installer,接著選擇左邊 更新 Visual Studio 版本
 
安裝 測試工具核心功能
Step 1. 開始 > 輸入 Visual Studio Installer
Step 2. 選擇左邊的 修改
Step 3. 個別元件 > 偵錯跟測試 > 選取 測試工具核心功能
Step 4. 會進行功能的安裝
安裝完畢後,重新開啟專案就發現功能回來了 (大師兄回來啦
可以寫測試保護 Code 了 !!!!!

參考
Create Unit Tests context menu option missing in Visual Studio 2017?


2018年12月27日 星期四

[.NET] 無法載入檔案或組件 'Newtonsoft.Json' 或其相依性的其中之一 (發生例外狀況於 HRESULT: 0x80131040)

問題
接獲同事報案,反應在測試環境出現以下錯誤訊息 無法載入檔案或組件 'Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' 或其相依性的其中之一。 找到的組件資訊清單定義與組件參考不符。 (發生例外狀況於 HRESULT: 0x80131040) ,實際專案底下 bin 資料夾該 dll 是存在的,心想這問題每隔一陣子都會遇到決定不在挑戰大腦記憶體,將這問題解法記錄下來

探索問題
有踩過這雷的人就會很快知道問題,是多個專案參考到的 dll 版本不同造成,其實仔細看錯誤網頁中有提到幾個關鍵字 組件繫結失敗,跟 google 大神請教後使用 Fuslogvw.exe 可以查到相關線索Fuslogvw (Assembly Binding Log Viewer) 能夠查看應用程式在執行時組件繫結的 Log 資訊與紀錄,協助釐清應用程式無法執行的原因是因為佈署到錯誤的資料夾或是版本號無法對應的問題,且隨著 Visual Studio 一起安裝(同捆包概念),這裡簡單介紹如何使用

開啟 Fuslogvw 
Step 1 : 左下角開始 > 輸入 "Prompt"  >  點選 Developer Command Prompt for VS 2017 
( 補充 : 記得要用管理者權限,否則會無法使用相關功能 )
Step 2 : 在 Developer Command Prompt for VS 2017 輸入 Fuslogvw ,按下 Enter
Step 3 : 會跳出 組件繫結紀錄檢視器 (如果無法按就是沒用 admin 權限開啟)

綁定繫結紀錄 
接下來步驟是說明如何透過 Fuslogvw.exe 的設定取得應用程式中 assembly Binding 的紀錄與資訊
Step 1 : 按下設定,設定以下兩個資訊
  • 啟用紀錄 : 預設是停用紀錄,有紀錄失敗的組件繫結與記錄所有的內容,我比較貪心選擇全部
  • 紀錄檔案的位置 : 設定紀錄 Log 檔的位置,我選擇有個暫存好找的位置存放,看個人喜好
Step 2 : 為了要取得組件繫結失敗 log 資訊,我們再重新執行一次應用程式
Step 3 : 執行完應用程式後按下 重新整理,就會發現左邊區塊出現 assembly Binding 的 log 資訊,像是應用程式assembly 以及 Log的時間等資訊,這樣就代表 Fuslogvw.exe 繫結成功


檢視記錄檔 
回到最初的問題錯誤訊息是 Newtonsoft.Json 載入失敗,這時可以透過 Fuslogvw 查看載入該組件的紀錄,點選 Newtonsoft.Json 後按 檢視記錄檔

Newtonsoft.Json 載入失敗訊息如下
*** 組件繫結器記錄項目 (2018/12/27 @ 上午 04:08:15) ***

作業失敗。
繫結結果: hr = 0x80131040。 沒有可用的描述。

組件管理員的載入來源:  C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
正在可執行檔下執行  c:\windows\system32\inetsrv\w3wp.exe
--- 下列是詳細的錯誤記錄。

=== 繫結前狀態資訊 ===
記錄: DisplayName = Newtonsoft.Json
 (Partial)
警告: 提供了組件的部分繫結資訊:
警告: 組件名稱: Newtonsoft.Json | 網域 ID: 2
警告: 如果只提供部分的組件顯示名稱,就會發生部分繫結。 
警告: 這可能會使繫結器載入不正確的組件。
警告: 建議為組件提供完全指定的文字識別,
警告: 該識別是由簡單名稱、版本、文化特性和公開金鑰語彙基元組成。
警告: 如需詳細資訊和這個問題的一般解決方法,請參閱白皮書 http://go.microsoft.com/fwlink/?LinkId=109270。
記錄: Appbase = file:///D:/application folder/
記錄: 初始 PrivatePath = D:\application folder\bin
記錄: 動態基底 = C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\applicationName\f8aec008
記錄: 快取基底 = C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Temporary ASP.NET Files\applicationName\f8aec008
記錄: AppName = 57a93a57
正在呼叫組件 : (Unknown)。
===
記錄: 此繫結在 default 載入內容中開始。
記錄: 正在使用應用程式組態檔: D:\application folder\web.config
記錄: 使用主機組態檔: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet.config
記錄: 從 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config 使用電腦組態檔。
記錄: 目前不會套用原則至參考 (私用、自訂、部分或以位置為主的組件繫結)。
記錄: 正在嘗試從新的 URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/applicationName/f8aec008/57a93a57/Newtonsoft.Json.DLL 下載。
記錄: 正在嘗試從新的 URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/applicationName/f8aec008/57a93a57/Newtonsoft.Json/Newtonsoft.Json.DLL 下載。
記錄: 正在嘗試從新的 URL file:///D:/application folder/bin/Newtonsoft.Json.DLL 下載。
記錄: 已順利下載組件。正在嘗試安裝檔案: D:\application folder\bin\Newtonsoft.Json.dll
記錄: 進入下載快取安裝階段。
記錄: 組態名稱為: Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
記錄: 從應用程式目錄中執行部分指定的組件繫結成功。必須重新套用原則。
記錄: 正在使用應用程式組態檔: D:\application folder\web.config
記錄: 使用主機組態檔: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet.config
記錄: 從 C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config 使用電腦組態檔。
記錄: 在應用程式組態檔中找到重新導向: 重新導向 8.0.0.0 至 11.0.0.0。
記錄: 原則後參考: Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
記錄: GAC 查閱失敗。
記錄: 原則後組件參考必須再進行探查。
記錄: 正在嘗試從新的 URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/applicationName/f8aec008/57a93a57/Newtonsoft.Json.DLL 下載。
記錄: 正在嘗試從新的 URL file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/Temporary ASP.NET Files/applicationName/f8aec008/57a93a57/Newtonsoft.Json/Newtonsoft.Json.DLL 下載。
記錄: 正在嘗試從新的 URL file:///D:/application folder/bin/Newtonsoft.Json.DLL 下載。
記錄: 已順利下載組件。正在嘗試安裝檔案: D:\application folder\bin\Newtonsoft.Json.dll
記錄: 進入下載快取安裝階段。
記錄: 組態名稱為: Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
警告: 比較組件名稱發現不符項目: 主要版本
錯誤: 組件參考不符合找到的組件定義。
錯誤: 安裝失敗,hr = 0x80131040。
錯誤: 無法完成組件的安裝 (hr = 0x80131040)。已終止探查。
 
小弟用我淺薄的知識簡單說明一下 Log : 
  • Line 11 : 說明應用程式要繫結的組鍵名稱
  • Line 20-21 : 本次要載入的應用程式路徑,dll folder位置
  • Line 22-23 : IIS 預設會把編譯後的應用程式資料夾存放在 Framwork安裝目錄 \ Temporary ASP.NET Files 目錄下,詳細可以參考 Understanding ASP.NET Dynamic Compilation 
  • Line 35-37 : 找到繫結組件的 dll 位置與版本等資訊
  • Line 38-39 : Web.Config 中可以設定 assemblyBinding 的版本號與關聯提供載入組鍵時參考詳細可以參考 MSDN 
  • Line 44 : GAC 確認失敗Global Assembly Cache 
  • Line 49-51 : 嘗試從應用程式資料夾底下的dll folder 安裝 Newtonsoft.Json 組鍵,這裡抓到版本號是 8.0.3
  • Line 52-53 : 發現版本不同指定的版號是 11.0.3 安裝的卻是 8.0.3
  • Line 54 : 安裝失敗顯示錯誤訊息 HRESULT: 0x80131040
解法
透過 Fuslogvw 分析完案發現場,確認是 dll 版本不同原因造成,在與同事確認專案的架構確實有多個專案參考來參考去的現象Class lib A 與 Class lib B 都依賴於 Class lib C只要 Class lib C 版本不同時,就會發生執行到不相配的 Json 版本的 dll 時,就會造成 runtime exception發生原因了解了接著看如何解決這問題

設定 assemblyBinding
在使用工具查看 Log 時候可以發現繫結過程中,會去參考 Web.Config 的 assemblyBinding 元素定義來加載 dll 內容,加上以下設定,告訴它在載入組件的過程中要綁定某個版本 ( 相關說明請服用 MSDN )

重建專案檔
目的 : 讓 Visual Studio 重啟加載專案檔及相關設定,重新檢查 Package 之間依賴性
設定方式 : 專案檔右鍵 > Clean > 重啟 Visual Studio > 重新 Build 專案 > 設定為 Release > 開始 debug

重裝異常的 Dll 
目的 : 讓異常的 dll 版本一致 
Step 1 : 專案 Reference > 異常 Dll 右鍵 > 屬性 > 顯示版本 
Step 2 : 到有使用異常 dll 的專案 > Manage Nuget Packages > 選擇 dll > Update version
成功訊息
透過以上方式,應用程式就可以正常使用,在使用 Fuslogvw 查看 Newtonsoft.Json 已正常載入,正常載入訊息如下,宣告破案 (握拳

參考
Could not load file or assembly or one of its dependencies
Fuslogvw.exe (Assembly Binding Log Viewer)
<assemblyBinding>元素<執行階段>

2018年12月24日 星期一

[VS2017] 如何修改 Visual Studio IDE 顯示語系

前言
目前筆電使用的 Visual Studio 是繁體中文版,在使用上用起來沒英文版那麼順,很多字詞在翻譯上總是讓人覺得驚喜,忍無可忍無須再忍決定改為英文版,過去 VS 可以透過下載的方式安裝語言包,但這招在 Visual Studio 2017 似乎無效(?這邊簡單紀錄 Visual Studio 如何安裝不同語系的過程

設定語系
IDE 語系設定 
設定方式 : 工具 > 選項 > 環境 > 國際設定 > 語言 
Note : 如果在一開始安裝 Visual Studio 的時候沒有特別勾選,在語言選項就只會有預設的語言

使用 Visual Studio Install 新增  
Visual Studio 預設的情況下,在安裝語言會以作業系統的語系打勾,這次配的筆電 OS 語系是繁體中文,自然不會特別勾選所要的英文語系,我們可以透過 Visual Studio 安裝程式安裝新的語言包,步驟如下
Step 1 : 開始 > 輸入 Visual Studio > 點選 Visual Studio Install
Step 2 : 如果您的 Visual Studio 不是最新版,會很貼心(誤) 地告訴你安裝升級完才能下一步
 Step 3 : 升級完成後會看到下圖,點選左邊的 修改 
Step 4 : 點選語言套件,選擇所需要新增的語系,按右下角修改
Step 5 : 安裝語言套件過程是需要關閉 Visual Studio 應用程式,跳出下面視窗莫急莫慌莫害怕把 Visual Studio 關掉就好
Step 6 : 安裝完畢後,在重新到語言設定位置就可以看到新的語系了
大功告成英文不好的人終於可以繼續用英文版的 Visual Studio 啦 !! 

參考
Install Visual Studio 2017

2018年12月23日 星期日

[VS2017] 目前無法叫用中斷點 未載入這個文件的符號

問題
在與同事 pair programming 進行開發時,反應他筆電的 Visual Studio 在偵錯時都無法進到中斷點,會出現白底紅圈加上驚嘆號(一般是紅色圈圈),滑鼠移上去會出現 目前無法叫用中斷點 未載入這個文件的符號 ,情境如下示意圖
處理方式
整理以下幾種可能發生的原因,以及遇到當下該如何進行問題排除
確認 Debug Mode 
確認 Visual Studio 方案組態設定是否為 Debug Mode,如果為 Release Mode 則所有中斷點都不會進去 
(不要鬧了 你醒醒阿阿 !!!!

設定偵錯 - Just My Code  
來回到 Visual Studio IDE 輸出視窗來源選擇 偵錯模式可以發現在載入 dll 的過程中有些不一樣的訊息提示,在無法偵錯中斷點是白底紅圈圈時皆顯示 已略過載入符號,模組已最佳化,並已啟用 [Just My Code] 偵錯工具選項,這段文字在說甚麼 ? 心想誰偷改我電腦設定 or 準備重灌 (誤,原因如下

當在 Application 進行 Debug 的時候debugger 會嘗試去找正在執行的 symbols 中的代碼,它才可以知道開發者在哪段 Code 有設定中斷點,蒐集加入監看示的變數內容或是檢查 Call Stack 等資訊,在 Visual Studio 中 debug 時預設會跳過不屬於此專案的代碼或是 assemblies這設定名稱為 Just My Code,如果想取消此設定可以透過以下方調整 
設定方式 : Visual Studio 點選工具 > 選項 > 偵錯 > 一般 > 取消 啟用 Just My Code 選項

設定 最佳化程式碼 
設定方式 : 專案點選右鍵 > 屬性 > 建置 > 取消 最佳化程式碼

什麼是最佳化程式碼 ? 
- 此設定預設是開啟,當 C# compiler 時會幫你的程式碼進行最佳化,目的是讓 C# compiler 後的檔案更小 以提升執行時的效能 ( 詳細可以參考 黑暗大 smart Compiler )

後續
以透過以上設定,就可以順利進入中斷點
可以繼續寫更多 bug 嚕 (握拳

參考
Just My Code
Smart C# Compiler
最佳化您的程式碼
'Just My Code' is Enabled ?

2018年12月20日 星期四

[AWS] 使用 AWS SDK上傳檔案到 AWS S3

前言
最近在與第三方做串接時有個需求是要將圖片存起來放在 Application Server 上,與同事討論建議將圖片放在AWS S3 上,多年前有微軟剛推出 Azure 時有接觸過一些,但這幾年隨著年紀增長早已忘光光,這篇簡單紀錄 C# 透過 AWS SDK 將圖片上傳到 AWS S3 上的步驟與要注意的細節

AWS S3 
Amazon Simple Storage Service 功能簡稱 S3,是Amazon的物件儲存服務主要提供使用者存放 Data 像是 html, css, js...等靜態檔案,上傳前需要建立儲存體 (Buckets) 來存放需要放在的物件接著透過 AWS Manager Console 的後台服務上傳物件到雲端上,使用者就可以透過 S3 的服務讀取到上傳的資料,如下圖所示

安裝 AWS SDK 
除了可以透過 AWS Manager Console 後台上傳檔案AWS 也提供 SDK 讓開發者可以透過 API 來上傳儲存的物件,在 .NET 有兩種方式可以安裝此套件
1. 可以到 nuget 主控台 輸入下列指令下載 AWS SDK 套件
Install-Package AWSSDK -Version 2.3.55.2
2. 到 nuget 管理員輸入 AWSSDK 直接下載

設定 Config Key
在 Application Config 中設定 AccessKey , SecretKey

使用 AWS SDK 上傳物件到 S3
參考 開發人員指南 上傳 Code 如下
using System;
using System.Threading.Tasks;
using Amazon.S3;
using Amazon.S3.Transfer;
using Amazon;
using Amazon.S3.Model;

namespace AWS
{
    class blog
    {
        private const string bucketName = "your bucketName";
        private const string keyName = "Test.png";
        private const string filePath = @"D:\Test.png";
        private static readonly RegionEndpoint bucketRegion = RegionEndpoint.APNortheast1;
        private static IAmazonS3 s3Client;

        static void Main(string[] args)
        {
            s3Client = new AmazonS3Client(bucketRegion);

            try
            {
                PutObjectRequest ObjectRequest = new PutObjectRequest
                {
                    BucketName = bucketName,
                    FilePath = filePath,
                    Key = keyName,
                    CannedACL = S3CannedACL.PublicRead
                };

                // upload object
                PutObjectResponse myResponse = s3Client.PutObject(ObjectRequest);
            }
            catch (AmazonS3Exception e)
            {
                Console.WriteLine("Error encountered on server. Message:'{0}' when writing an object", e.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine("Unknown encountered on server. Message:'{0}' when writing an object", e.Message);
            }
        }
    }
}
 
程式說明 : 
  • Line 12 : 要上傳到的 bucket Name 儲存體
  • Line 13 : 要上傳到的位置 + 檔名 , 舉例 : 可以透過目錄區分物件 public / test / test.jpg
  • Line 14 : 上傳物件路徑 
  • Line 15 : 定義 RegionEndpoint , 在一開始建立 bucket 會設定機房的位置 (參考備註)
  • Line 20 : 初始化 s3Client 物件 透過建構子定義 Endpoint
  • Line 24 : 初始化要上傳的物件 putObjectRequest,設定要上傳到的 bucketName 物件路徑 與 設定此物件為公開 (非必要)
  • Line 24 : 使用 putObject 方法上傳物件
使用 AWS SDK 上傳物件到 S3 (非同步)
如果在開發上是有非同步的需求,可以參考下面
using System;
using System.Threading.Tasks;
using Amazon.S3;
using Amazon.S3.Transfer;
using Amazon;

namespace AWS
{
    class blog
    {
        private const string bucketName = "urbucketName";
        private const string filePath = @"D:\Test.png";
        // Specify your bucket region (an example region is shown).
        private static readonly RegionEndpoint bucketRegion = RegionEndpoint.APNortheast1;
        private static IAmazonS3 s3Client;

        static void Main(string[] args)
        {
            s3Client = new AmazonS3Client(bucketRegion);
            UploadFileAsync().Wait();
        }

        private static async Task UploadFileAsync()
        {
            try
            {
                var fileTransferUtility = new TransferUtility(s3Client);
                await fileTransferUtility.UploadAsync(filePath, bucketName);

                Console.WriteLine("上傳完成");
            }
            catch (AmazonS3Exception e)
            {
                Console.WriteLine("Error encountered on server. Message:'{0}' when writing an object", e.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine("Unknown encountered on server. Message:'{0}' when writing an object", e.Message);
            }
        }
    }
}

程式說明 : 
  • Line 27 : 初始化 TransferUtility 物件 透過建構子定義 Endpoint
  • Line 28 : 透過 TransferUtility 的 UploadAsync 方法上傳物件到 S3 上
補充
在定義 BucketRegion 需特別注意要指定到你一開始建立的機房位置,如果指定錯誤的話會出現
Error encountered on server. Message:'The bucket you are attempting to access must be addressed using the specified endpoint. Please send all future requests to this endpoint.' when writing an object ? 
建立位置與對應 EndPoint 可以參考官方 AWS Regions and Endpoints 說明,避免踩雷 XDDD

參考
Amazon S3 Features
Upload a File to an S3 Bucket Using the AWS SDK for .NET (High-Level API)
使用AWS SDK for .NET
Build a Serverless Web Application

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

Design by Anders Noren | Blogger Theme by NewBloggerThemes.com