1. 程式人生 > >.NET 將 .config 檔案嵌入到程式集

.NET 將 .config 檔案嵌入到程式集

.NET 將 .config 檔案嵌入到程式集

最近,團隊中的一位同事實現了一個小程式,供主程式呼叫。為了小程式分發的方便性,使用了 Costura.Fody 將其依賴的 dll 都嵌入到了 exe 中。但是,其中的 log4net.dll 又需要一個 *.config 檔案才能正常工作,而 Costura.Fody 又不支援此類檔案的嵌入。

我們先來複現一下問題場景,關於 Costura.Fodylog4net 的使用可以參考:

首先,在一個 C# 控制檯程式中通過 NuGet

引用 log4netCostura.Fody 兩個元件,然後在程式的 App.config 檔案中新增如下配置:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
      <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
    </configSections>
    <log4net
>
<root> <level value="WARN" /> <appender-ref ref="LogFileAppender" /> </root> <logger name="mylogger"> <level value="ALL"/> </logger> <appender name="LogFileAppender" type="log4net.Appender.FileAppender"
>
<param name="File" value="log-file.txt" /> <param name="AppendToFile" value="true" /> <layout type="log4net.Layout.PatternLayout"/> </appender> </log4net> </configuration>

這些配置用於指定 log4net 如何工作,需要在程式的入口處載入這些配置資訊:

static void Main(string[] args)
{
    // 從預設的配置檔案(App.config)中讀取配置資訊
    log4net.Config.XmlConfigurator.Configure();

    var logger = LogManager.GetLogger("mylogger");
    logger.Info("Starting...");

    Console.ReadLine();
}

然後,為 Costura.Fody 新增一個 FodyWeavers.xml 檔案,檔案內容如下:

<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
  <Costura/>
</Weavers>

最後,編譯此專案。你會發現 log4net.dll 並未出現在生成目錄中,而是被合併到了 MyConsoleApp.exe 中。同時,生成目錄中還有一個 MyConsoleApp.exe.config 檔案(App.config)生成的。如果刪除該檔案,MyConsoleApp.exe 中的 log4net 不能正常工作。

因此,如果要實現 MyConsoleApp.exe 單檔案分發,就要將 App.config 嵌入到該 exe 中,並且 log4net 要能識別嵌入的檔案。

Embedded Resource

好在 Visual Studio 支援將專案中的某個檔案的 Build Action 設定為 Embedded Resource(嵌入資源),這樣一來,檔案將作為資源被嵌入到程式集中。

並且,可以在執行時通過 Assembly.GetManifestResourceStream() 方法取檔案內容:

var assembly = Assembly.GetExecutingAssembly();
var resourceName = "MyCompany.MyProduct.MyFile.txt";

using (Stream stream = assembly.GetManifestResourceStream(resourceName))
using (StreamReader reader = new StreamReader(stream))
{
    string result = reader.ReadToEnd();
}

這只是其中一種將檔案作為資源嵌入到程式集的方法,欲瞭解更多方法,可參考 Various Build Actions in Visual Studio

Configure(Stream configStream)

查資料發現,log4net 支援從 Stream 中載入配置檔案:

log4net.Config.XmlConfigurator.Configure(Stream configStream);

那麼,結合 Embedded Resource,問題就非常簡單了:

static void Main(string[] args)
{
    var assembly = Assembly.GetExecutingAssembly();
    var resourceName = "MyConsoleApp.App.config";

    using (Stream stream = assembly.GetManifestResourceStream(resourceName))
    {
        // 從 Stream 中讀取 log4net 的配置資訊
        log4net.Config.XmlConfigurator.Configure(stream);
    }

    var logger = LogManager.GetLogger("mylogger");
    logger.Info("Starting...");

    Console.ReadLine();
}

再次編譯專案,MyConsoleApp.exe.config 也從生成目錄中消失了,單獨的一個 MyConsoleApp.exe 也能正常運行了。

總結

本文只是以 *.config 檔案為例來說明如何將檔案作為資源嵌入到程式集中,其它格式的檔案也是支援的,並且還有其它的嵌入方式,感興趣的可以通過 Build Action 去探索更多的方法。

參考資料