野外求生系列又來了,今天要示範「如何在沒有開發工具(Visual Studio、VSCode、MSBuild、LINQPad...)的環境生出 Console Application .exe 整合既有 DLL 程式庫做測試?」

情境是這樣的,主機上有某支 .NET 程式出錯,我們嚴重懷疑是某段 Oracle SELECT 執行計劃踩坑,且與 WHERE 條件使用參數與否有關(延伸閱讀:寫死或參數傳入影響執行計劃的實例)。但吊詭的是,同樣的查詢搬到 SQLPlus 執行怎麼都無法重現問題,似乎要搭配 .NET 與相關程式庫才能觸發。我想到最簡單的驗證方法是寫一小段 C# 程式儘可能仿效問題程式的做法,引用相同公用程式庫執行同一查詢,以找出觸發問題的關鍵。

問題來了,因於問題程式處於管制環境,不允許任意部署程式,而所有動作只能提供 Script 及操作程序交由 OP 人員操作。而為求逼近異常發生情境,測試程式應滿足以下條件:

  1. 由問題程式的 .exe.config 讀取必要 appSetting 參數(後續建立連線使用)
  2. 呼叫自訂程式庫函式取得 OracleConnection 物件執行查詢
  3. 測試程式以原始碼或腳本形式提供,由 OP 人員操作執行
  4. 除 .NET Framework 外,Windows 未安裝任何開發工具或軟體,需在此條件下編譯執行

Console Application 應是最簡單的 .NET 程式形式,只需要一個 Program.cs 就能搞定,最極端的狀況用 type con Program.cs 手動輸入程式碼後編譯,即可無中生有長出 .exe 程式。但若環境允許,是沒必要這樣逼死自己,在工作機用 Visual Studio 開個 Console Application 專案,寫好程式主體測試過再將 Program.cs 丟上管制環境會容易些。當然,如果你功力深又想當苦行僧,堅持用 Notepad 徒手刻完程式也未嘗不可。

寫好 Program.cs,丟上執行環境後可視需要反覆修改編譯跑測試,快速驗證及排除各種猜想,跟 Inline ASPX 一樣,是不可多得的射茶包利器。

至於程式邏輯的第一個挑戰是要讀取其他程式 config appSetting,有兩種做法,可以自己寫 XDocument 解析或透過 OpenMappedExeConfiguration() 存取,我選擇後者。

程式會用到 ODP.NET 跟自訂元件,如果有 Visual Studio 可用,這不是問題,加加參照就解決了。但要記錄所引用的第三方元件(本例用到 Oracle.DataAccess.dll 及 MyAppLibrary.dll),稍後在執行環境編譯時需要額外處理。

範例程式 Program.cs 如下:

using Oracle.DataAccess.Client;
using System;
using System.Configuration;
using System.Data;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var fm = new System.Configuration.ExeConfigurationFileMap();
            fm.ExeConfigFilename = @"E:\OraTest\MyApp.exe.config";
            var config = ConfigurationManager.OpenMappedExeConfiguration(fm, ConfigurationUserLevel.None);
            var idKey = config.AppSettings.Settings["IDKey"].Value;

            using (var cn = MyAppLibrary.DataLayer.GetOraConnection(idKey))
            {
                cn.Open();
                var cmd = cn.CreateCommand();
                cmd.CommandText = @"SELECT A,B,C FROM T WHERE D = :p_date";
                cmd.Parameters.Add("p_date", OracleDbType.Date).Value = new DateTime(2019, 1,1);
                var cnt = 0;
                var dr = cmd.ExecuteReader();
                var t = new DataTable();
                t.Load(dr);
                Console.WriteLine("Row Count={0}", t.Rows.Count);
            }
            Console.ReadLine();
        }
    }
}

在 Visual Studio 測試 OK 後,將此 Program.cs 送上執行環境請 OP 人員編譯及測試。編譯時執行時,需將參照的第三方程式庫(Oracle.DataAccess.dll、MyAppLibrary.dll)由問題程式資料夾複製到 Program.cs 同一目錄下。

再來只要將 Program.cs 編譯成 Program.exe 就大功告成了。編譯方法很簡單,只要 Windows 有裝 .NET Framework,C# 編譯器 csc.exe 早在 C:\Windows\Microsoft.NET\Framework<version>\ 恭候多時。CSC 的參數很多,但本案例只需 -r (-reference 縮寫)參照額外組件檔,以 -platform:x86 指定編譯成 x86 以配合 32bit Oracle.DataAccess.dll。執行 C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc -platform:x86 program.cs -r:Oracle.DataAccess.dll -r:MyAppLibrary.dll,Program.exe 就熱騰騰出爐了:

就醬,我又學會徒手生出 Console Application exe 的把戲,未來遇到當場引用現成 DLL 寫出測試 .exe 的場合,不會再束手無策了。

Example of how to write a single Program.cs file and compile it to exe when you need to test existing assembly on production environment.


Comments

Be the first to post a comment

Post a comment