本文主要介绍C#中应用集加载过程的相关信息,通过示例代码进行了非常详细的介绍,对大家的学习或工作有一定的参考价值。有需要的朋友就跟着下面的边肖学习吧。
目录
了解C #中程序集如何加载程序集、模块和引用。网。程序集绑定绑定重定向当问题开始出现时,进行故障排除并记下参考
了解如何在C #中加载程序集。网
我们一直在处理库和NuGet包。无论好坏,先进。NET开发人员需要知道。NET运行时加载程序集。
这些库依赖于其他流行的库,并且有许多共享的依赖项。有了一个足够大的依赖网络,你就会陷入冲突或困境。处理此类问题的最佳方法是了解该机制的内部工作原理。
在本文中,您将看到。NET进程加载引用的程序集。
您将了解加载哪个库版本,当有多个可用版本时会发生什么,以及为什么有时会由于版本冲突而出现问题。
您将看到如何调试这些类型的问题,检查程序集绑定日志(fusion log)以及解决冲突的一些方法。
程序集、模块和引用
让我们从一些基本术语开始。NET进程。
中的程序集。NET是一个DLL或EXE文件。Visual Studio解决方案中的每个项目都被编译成一个程序集。
每个程序集可以包含多个模块,但事实上,我们几乎总是在一个程序集中有一个模块与程序集同名。
当您在Visual Studio中启动该过程或单击F5时,将执行启动项目程序集。除了。NET框架或者。NET核心程序集,它将是第一个加载的程序集。
之后,该流程将根据需要在运行时加载其他程序集。只有在需要调用其方法或使用其类型时,它才会延迟加载程序集。
下面是为一个简单的“Hello World”加载的模块。NET Framework项目(模块和程序集对于我们所有的意图和目的都是相同的)。MyStartup.dll是这里的开始项目:
时加载的模块。NET核心项目开始。
当从另一个项目引用一个项目时,被引用项目的DLL或EXE在构造时被复制到启动项目的Bin文件夹中。
通常是Bin \ Debug或Bin \ Release。在运行时,当您第一次在引用的项目中使用该类型时,CLR会在应用程序目录中查找与预期名称和版本相同的DLL文件。然后将程序集加载到流程中。这也称为绑定到程序集。
这是一个例子:
假设我们有一个名为MyStartup的简单控制台应用程序,它引用了另一个名为Lib1的项目。MyStartup使用了Lib1程序集中的一些类。
在我的Startup中:
班级计划
{
静态void Main(string[] args)
{
int a=int。解析(控制台。ReadLine());
int b=int。解析(控制台。ReadLine());
控制台。WriteLine('A B=' Add(a,B));
}
私有静态int Add(int a,int b)
{
var计算器=新Lib1。计算器();
退货计算器。Sum(a,b);
}
}
在Lib1中:
公共类计算器
{
公共int Sum(int a,int b)
{
返回a b;
}
}
当进入Main方法时,Lib1程序集尚未加载。但是,当进入Add方法时,CLR会尝试解析Calculator类型,找出它在引用的程序集Lib1中,然后尝试加载该程序集。
程序集绑定在。网
当CLR需要加载一个程序集时,逻辑实际上比在Bin文件夹中搜索要复杂一点。这是实际的执行逻辑(有关详细信息,请参见Microsoft文档[1]):
1.根据配置文件(app.config或web.config)确定要加载的程序集的版本。配置文件的名字是(生成后)【可执行名】. exe.config或者web.config重定向在这里起作用(后面会详细介绍)。
2.检查程序集是否已加载。如果加载了其他版本,将会引发FileLoadException,除非它是可以同时加载多个版本的强名称程序集。
3.如果是强名称程序集,请检查全局程序集缓存[2](GAC)。GAC是在机器上共享多个应用程序部件的地方。如有必要,将缓存该程序集。它只能存储具有强名称的程序集。它可以存储同一程序集的不同版本。可以用gacutil.exe[3]自己安装到GAC。
4.如果它是一个具有强名称的程序集,并且配置文件包含一个codeBase节点,那么它将检查程序集在那里的位置。如果codeBase节点存在,但找不到程序集,FileNotFoundException将引发。
5.根据启发式算法检查程序集DLL或EXE。这个过程被称为“探测”。算法如下:
1.检查文件夹[应用程序库]/[程序集名称]。dll。应用程序库是应用程序可执行文件所在的位置。通常是Bin \ Debug或Bin \ Release文件夹。
2.检查[应用程序库]/[程序集名称]/[程序集名称]。动态链接库
3.如果为引用的程序集指定了区域性信息,则只检查以下目录:[application base]/[culture]/[assembly name]。dll[应用程序库]/[区域性]/[程序集名称]。动态链接库
4.如果探测节点存在于配置文件中,它将在privatePath节点的属性指定的文件夹中查找程序集。
为什么他们让一切变得如此困难,对吗?
其实这种逻辑对我们的发展很有帮助,不会把事情搞复杂。它的存在是为了实现一些重要的目标:
为了确保您引用的是特定的程序集和版本,将加载确切的版本。否则,将引发异常。此外,如果您知道自己在做什么,您可以在配置文件中指定覆盖规则(绑定重定向)。
以便在要加载的程序集中保持灵活性。例如,如果您想根据不同的区域性(语言)加载不同的程序集,您可以很容易地做到这一点。或者,如果您想根据客户配置加载不同的程序集,那也没问题。
为了安全起见,我们使用全名程序集。确保不能“伪造”程序集。例如,如果一个进程想要加载Lib1 v4.5,那么您将无法加载具有相同名称和版本的恶意软件程序集。加载时引发异常。这就是计算机上所有进程共享的GAC只接受强名称程序集的原因。
在大多数应用程序中,您不需要记住程序集加载和探测的复杂逻辑。你不需要知道或考虑GAC,全称程序集或操作配置文件。
你几乎完全不需要考虑库的版本,因为可能的冲突都是通过一种叫做“绑定重定向”的机制自动解决的。
绑定重定向
如果有一件事对理解这笔交易非常重要,那就是绑定重定向。能够告诉运行时它将实际加载哪个版本,而不管它引用的是哪个版本。
这是一个例子:你的过程有两个项目(模块):项目A和项目B,项目A引用log4net.dll的v1.1,项目B引用log4net.dll的v1.2。两个log4net DLL文件都被复制到输出文件夹,但只能有一个log4net.dll文件。
假设复制到输出文件夹的文件是log4net.dll v 1.2 .假设到达的第一个代码是项目A中的代码,引用log4net v1.1运行时会在输出文件夹中查找,找到log4net的不同版本,并失败FileLoadException。
还有一种可能。假设先执行项目B中的代码,在尝试使用log4net时,成功加载了log4net.dll v 1.2,过一会儿项目A中的代码会尝试使用log4net v1.1请看到程序集已经加载了其他版本并抛出了FileLoadException
如果您知道哪个log4net版本将出现在输出文件夹中,那么在这种情况下,您可以告诉运行时应该使用哪个版本。只需app.config将以下几行添加到此运行时部分的文件中:
?xml版本='1.0 '编码='utf-8 '?
配置
.
运行时间
.
assembly binding xmlns=' urn:schemas-Microsoft-com:ASM . v1 '
从属程序集
assemblyIdentity name='log4net '
public key token=' 669 E0 ddf 0 bb 1 aa 2 a ' culture=' neutral '/
bindingRedirect旧版本='0.0.0.0-5.0.0.0 '新版本='1.2.0' /
/dependentAssembly
/assemblyBinding
/运行时
.
/配置
这意味着每当运行时希望绑定到版本范围为0 . 0 . 0 . 0 . 0到的程序集log4net 5.0.0.0时,它将尝试绑定到版本1.2.0。
实际上,您不必手动添加这些重定向,因为它们是自动添加的。如果转到启动项目的属性,您将看到以下设置:
默认情况下,此选项处于选中状态。它自动检测版本冲突,并在。配置文件。
当问题开始出现时
乍一看,绑定重定向似乎可以解决所有问题,但事实并非如此。当使用绑定重定向时,使用的库版本基本上不同于预期的版本。方法被删除了怎么办?或者方法的签名改变了?在这种情况下,当调用此方法时,程序将由于运行时错误而失败。毕竟,版本的产生是有原因的。
如果这些问题确实存在,也有解决办法。查看我的文章:如何解决?NET引用和NuGet包[4]。
排除故障
当你有一个FileLoadException或者类似的东西时,我推荐你做的第一件事就是看看Visual Studio中的“模块”窗口。在这里,您将看到所有已加载的模块,并确定要加载的程序集是否已加载、使用哪个版本以及从哪个路径加载。
此外,您还可以查看程序集绑定日志,也称为融合日志。这些日志将确切显示在程序集绑定尝试期间发生了什么。您将看到运行时找到的程序集版本、运行时找到的文件夹以及故障点。
有几种方法可以查看融合日志。首先,您必须启用它们,因为它们在默认情况下是禁用的。您可以在注册表中手动启用它们,方法是将HKLM \软件\ Microsoft \ fusion \ forcelog值设置为1,将HKLM \软件\ Microsoft \ fusion \ logpath值设置为。日志将自动出现。或者,您可以使用Fusion Log Viewer,它应该安装在fuslogvw.exe的PC上。我建议使用“所有窗口”搜索的[5]类程序来查找。确保使用管理员权限运行Fusion Log Viewer,以便能够启用和禁用日志。最近一个更流行、更现代的工具是Fusion [6]。
旁注
也许你不知道,但是我以前讨厌处理这样的问题。比如一个逻辑问题,让我构建一个东西,甚至解决一个制作错误,但其他问题都好说,只有这一个。
这件事我没办法,只好苦学汇编绑定的内功。我发现,就像其他事情一样,一旦你理解了某件事,它就变得不那么可怕,甚至不那么有趣了。
所以,希望这篇文章对你有意义,在我的路上能给你提供快速的帮助。
参考
[1]微软文档:https://docs . Microsoft . com/en-us/dot net/framework/deployment/how-the-runtime-locations-assemblies
[2]全局程序集缓存:https://docs . Microsoft . com/en-us/dot net/framework/app-domains/GAC
[3]gacutil . exe:https://docs . Microsoft . com/en-us/dot net/framework/tools/gacutil-exe-GAC-tool
[4]如何解决版本之间的冲突。NET引用和NuGet包:https://michaelscodingspot . com/how-to-resolve-NET-reference-and-NuGet-package-version-conflicts/
[5]所有窗口”搜索:https://www.voidtools.com/
[6]融合:https://github.com/awaescher/Fusion/
关于C#应用集加载过程的详细讲解,本文到此为止。有关C#应用程序集加载过程的更多信息,请搜索我们以前的文章或继续浏览下面的相关文章。希望你以后能支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。