.NET 将 .config 文件嵌入到程序集
最近,团队中的一位同事实现了一个小程序,供主程序调用。为了小程序分发的方便性,使用了 Costura.Fody
将其依赖的 dll
都嵌入到了 exe
中。但是,其中的 log4net.dll
又需要一个 *.config
文件才能正常工作,而 Costura.Fody
又不支持此类文件的嵌入。
我们先来复现一下问题场景,关于 Costura.Fody
和 log4net
的使用可以参考:
首先,在一个 C#
控制台程序中通过 NuGet
引用 log4net
和 Costura.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
去探索更多的方法。