Best Practices for Assembly Loading
This article discusses ways to avoid problems of type identity that can lead to InvalidCastException, MissingMethodException, and other errors. The article discusses the following recommendations:
Understand the advantages and disadvantages of load contexts
Avoid loading multiple versions of an assembly into the same context
The first recommendation, understand the advantages and disadvantages of load contexts, provides background information for the other recommendations, because they all depend on a knowledge of load contexts.
Understand the Advantages and Disadvantages of Load Contexts
Within an application domain, assemblies can be loaded into one of three contexts, or they can be loaded without context:
The default load context contains assemblies found by probing the global assembly cache, the host assembly store if the runtime is hosted (for example, in SQL Server), and the ApplicationBase and PrivateBinPath of the application domain. Most overloads of the Load method load assemblies into this context.
The load-from context contains assemblies that are loaded from locations that are not searched by the loader. For example, add-ins might be installed in a directory that is not under the application path. Assembly.LoadFrom, AppDomain.CreateInstanceFrom, and AppDomain.ExecuteAssembly are examples of methods that load by path.
The reflection-only context contains assemblies loaded with the ReflectionOnlyLoad and ReflectionOnlyLoadFrom methods. Code in this context cannot be executed, so it is not discussed here. For more information, see How to: Load Assemblies into the Reflection-Only Context.
If you generated a transient dynamic assembly by using reflection emit, the assembly is not in any context. In addition, most assemblies that are loaded by using the LoadFile method are loaded without context, and assemblies that are loaded from byte arrays are loaded without context unless their identity (after policy is applied) establishes that they are in the global assembly cache.
The execution contexts have advantages and disadvantages, as discussed in the following sections.
Load-From Context
The load-from context lets you load an assembly from a path that is not under the application path, and therefore is not included in probing. It enables dependencies to be located and loaded from that path, because the path information is maintained by the context.
In addition, assemblies in this context can use dependencies that are loaded into the default load context.
Loading assemblies by using the Assembly.LoadFrom method, or one of the other methods that load by path, has the following disadvantages:
If an assembly with the same identity is already loaded, LoadFrom returns the loaded assembly even if a different path was specified.
If an assembly is loaded with LoadFrom, and later an assembly in the default load context tries to load the same assembly by display name, the load attempt fails. This can occur when an assembly is deserialized.
If an assembly is loaded with LoadFrom, and the probing path includes an assembly with the same identity but in a different location, an InvalidCastException, MissingMethodException, or other unexpected behavior can occur.
LoadFrom demands FileIOPermissionAccess.Read and FileIOPermissionAccess.PathDiscovery, or WebPermission, on the specified path.
If a native image exists for the assembly, it is not used.
The assembly cannot be loaded as domain-neutral.
In the .NET Framework versions 1.0 and 1.1, policy is not applied.
No Context
Loading without context is the only option for transient assemblies that are generated with reflection emit. Loading without context is the only way to load multiple assemblies that have the same identity into one application domain. The cost of probing is avoided.
Assemblies that are loaded from byte arrays are loaded without context unless the identity of the assembly, which is established when policy is applied, matches the identity of an assembly in the global assembly cache; in that case, the assembly is loaded from the global assembly cache.
Loading assemblies without context has the following disadvantages:
Other assemblies cannot bind to assemblies that are loaded without context, unless you handle the AppDomain.AssemblyResolve event.
Dependencies are not loaded automatically. You can preload them without context, preload them into the default load context, or load them by handling the AppDomain.AssemblyResolve event.
Loading multiple assemblies with the same identity without context can cause type identity problems similar to those caused by loading assemblies with the same identity into multiple contexts. See Avoid Loading an Assembly into Multiple Contexts.
If a native image exists for the assembly, it is not used.
The assembly cannot be loaded as domain-neutral.
In the .NET Framework versions 1.0 and 1.1, policy is not applied.
Avoid Binding on Partial Assembly Names
Partial name binding occurs when you specify only part of the assembly display name (FullName) when you load an assembly. For example, you might call the Assembly.Load method with only the simple name of the assembly, omitting the version, culture, and public key token. Or you might call the Assembly.LoadWithPartialName method, which first calls the Assembly.Load method and, if that fails to locate the assembly, searches the global assembly cache and loads the latest available version of the assembly.
Partial name binding can cause many problems, including the following:
The Assembly.LoadWithPartialName method might load a different assembly with the same simple name. For example, two applications might install two completely different assemblies that both have the simple name
GraphicsLibrary
into the global assembly cache.The assembly that is actually loaded might not be backward-compatible. For example, not specifying the version might result in the loading of a much later version than the version your program was originally written to use. Changes in the later version might cause errors in your application.
The assembly that is actually loaded might not be forward-compatible. For example, you might have built and tested your application with the latest version of an assembly, but partial binding might load a much earlier version that lacks features your application uses.
Installing new applications can break existing applications. An application that uses the LoadWithPartialName method can be broken by installing a newer, incompatible version of a shared assembly.
Unexpected dependency loading can occur. It you load two assemblies that share a dependency, loading them with partial binding might result in one assembly using a component that it was not built or tested with.
Because of the problems it can cause, the LoadWithPartialName method has been marked obsolete. We recommend that you use the Assembly.Load method instead, and specify full assembly display names. See Understand the Advantages and Disadvantages of Load Contexts and Consider Switching to the Default Load Context.
If you want to use the LoadWithPartialName method because it makes assembly loading easy, consider that having your application fail with an error message that identifies the missing assembly is likely to provide a better user experience than automatically using an unknown version of the assembly, which might cause unpredictable behavior and security holes.
Avoid Loading an Assembly into Multiple Contexts
Loading an assembly into multiple contexts can cause type identity problems. If the same type is loaded from the same assembly into two different contexts, it is as if two different types with the same name had been loaded. An InvalidCastException is thrown if you try to cast one type to the other, with the confusing message that type MyType
cannot be cast to type MyType
.
For example, suppose that the ICommunicate
interface is declared in an assembly named Utility
, which is referenced by your program and also by other assemblies that your program loads. These other assemblies contain types that implement the ICommunicate
interface, allowing your program to use them.
Now consider what happens when your program is run. Assemblies that are referenced by your program are loaded into the default load context. If you load a target assembly by its identity, using the Load method, it will be in the default load context, and so will its dependencies. Both your program and the target assembly will use the same Utility
assembly.
However, suppose you load the target assembly by its file path, using the LoadFilemethod. The assembly is loaded without any context, so its dependencies are not automatically loaded. You might have a handler for the AppDomain.AssemblyResolveevent to supply the dependency, and it might load the Utility
assembly with no context by using the LoadFile method. Now when you create an instance of a type that is contained in the target assembly and try to assign it to a variable of type ICommunicate
, an InvalidCastException is thrown because the runtime considers the ICommunicate
interfaces in the two copies of the Utility
assembly to be different types.
There are many other scenarios in which an assembly can be loaded into multiple contexts. The best approach is to avoid conflicts by relocating the target assembly in your application path and using the Load method with the full display name. The assembly is then loaded into the default load context, and both assemblies use the same Utility
assembly.
If the target assembly must remain outside your application path, you can use the LoadFrom method to load it into the load-from context. If the target assembly was compiled with a reference to your application's Utility
assembly, it will use the Utility
assembly that your application has loaded into the default load context. Note that problems can occur if the target assembly has a dependency on a copy of the Utility
assembly located outside your application path. If that assembly is loaded into the load-from context before your application loads the Utility
assembly, your application's load will fail.
The Consider Switching to the Default Load Context section discusses alternatives to using file path loads such as LoadFile and LoadFrom.
Avoid Loading Multiple Versions of an Assembly into the Same Context
Loading multiple versions of an assembly into one load context can cause type identity problems. If the same type is loaded from two versions of the same assembly, it is as if two different types with the same name had been loaded. An InvalidCastException is thrown if you try to cast one type to the other, with the confusing message that type MyType
cannot be cast to type MyType
.
For example, your program might load one version of the Utility
assembly directly, and later it might load another assembly that loads a different version of the Utility
assembly. Or a coding error might cause two different code paths in your application to load different versions of an assembly.
In the default load context, this problem can occur when you use the Assembly.Loadmethod and specify complete assembly display names that include different version numbers. For assemblies that are loaded without context, the problem can be caused by using the Assembly.LoadFile method to load the same assembly from different paths. The runtime considers two assemblies that are loaded from different paths to be different assemblies, even if their identities are the same.
In addition to type identity problems, multiple versions of an assembly can cause a MissingMethodException if a type that is loaded from one version of the assembly is passed to code that expects that type from a different version. For example, the code might expect a method that was added to the later version.
More subtle errors can occur if the behavior of the type changed between versions. For example, a method might throw an unexpected exception or return an unexpected value.
Carefully review your code to ensure that only one version of an assembly is loaded. You can use the AppDomain.GetAssemblies method to determine which assemblies are loaded at any given time.
Consider Switching to the Default Load Context
Examine your application's assembly loading and deployment patterns. Can you eliminate assemblies that are loaded from byte arrays? Can you move assemblies into the probing path? If assemblies are located in the global assembly cache or in the application domain's probing path (that is, its ApplicationBase and PrivateBinPath), you can load the assembly by its identity.
If it is not possible to put all your assemblies in the probing path, consider alternatives such as using the .NET Framework add-in model, placing assemblies into the global assembly cache, or creating application domains.
Consider Using the .NET Framework Add-In Model
If you are using the load-from context to implement add-ins, which typically are not installed in the application base, use the .NET Framework add-in model. This model provides isolation at the application domain or process level, without requiring you to manage application domains yourself. For information about the add-in model, see Add-ins and Extensibility.
Consider Using the Global Assembly Cache
Place assemblies in the global assembly cache to get the benefit of a shared assembly path that is outside the application base, without losing the advantages of the default load context or taking on the disadvantages of the other contexts.
Consider Using Application Domains
If you determine that some of your assemblies cannot be deployed in the application's probing path, consider creating a new application domain for those assemblies. Use an AppDomainSetup to create the new application domain, and use the AppDomainSetup.ApplicationBase property to specify the path that contains the assemblies you want to load. If you have multiple directories to probe, you can set the ApplicationBase to a root directory and use the AppDomainSetup.PrivateBinPathproperty to identify the subdirectories to probe. Alternatively, you can create multiple application domains and set the ApplicationBase of each application domain to the appropriate path for its assemblies.
Note that you can use the Assembly.LoadFrom method to load these assemblies. Because they are now in the probing path, they will be loaded into the default load context instead of the load-from context. However, we recommend that you switch to the Assembly.Load method and supply full assembly display names to ensure that correct versions are always used.
Best Practices for Assembly Loading的更多相关文章
- error——Fusion log——Debugging Assembly Loading Failures
原文 So...you're seeing a FileNotFoundException, FileLoadException, BadImageFormatException or you sus ...
- .NET:CLR via C# Assembly Loading
基础知识 Internally, the CLR attempts to load this assembly by using the System.Reflection.Assembly clas ...
- Load ContextCLR 探测
目录 背景Load ContextCLR 探测过程弱签名程序集的探测过程强签名程序集的探测过程Default ContextLoad-From ContextNo ContextRelfection- ...
- .NET:鲜为人知的 “Load Context”
背景 任何一门语言都要了解其类型加载过程,如:Java 的 Class Loader,NodeJS 的搜索方式等,本文概述一下我对 CLR 如何加载程序集,重点说一下 Load Context. 其编 ...
- .NET 的程序集加载上下文
原文:.NET 的程序集加载上下文 我们编写的 .NET 应用程序会使用到各种各样的依赖库.我们都知道 CLR 会在一些路径下帮助我们程序找到依赖,但如果我们需要手动控制程序集加载路径的话,需要了解程 ...
- hook mono实现Assembly.Load从指定路径读取文件
mono-unity github: https://github.com/Unity-Technologies/mono/blob/unity-staging/mono/metadata/assem ...
- .NET 介绍
In order to continue our effort of being modular and well factored we don’t just provide the entire ...
- 【.Net Framework 体积大?】不安装.net framework 也能运行!?原理补充-3
继续补充点吧.接上一篇,我们实现了.net framework的精简的步骤. 有网友评论了,那个核心的 mscoree.dll 从.net framework 2.0开始,微软开始了新的CLR承载模型 ...
- 2017 .NET 開發者須知
筆記-Scott Hanselman 的 2017 .NET 開發者須知 转载http://blog.darkthread.net/post-2017-01-16-dotnet-dev-should- ...
随机推荐
- MySql 外键重名问题
在使用mysql workbench 来设计数据库模型时可能一不注意就会出现外键约束重名的情况,并在执行sql语句是会报这样的错误: Error 1022 - Can't write; duplica ...
- 冒泡排序(JAVA实现)
基本思想:在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒. 即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将 ...
- 一篇文章彻底弄懂Base64编码原理
在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网 ...
- 小程序:scroll-view组件滑动多次触发scroll事件的bug解决
在项目开发过程中,组件是微信小程序提供给我们的一个分页器,一般滑动到底部时会触发scroll事件,scroll事件中往往包含对后端数据的请求:若是还未滑动到底部时频繁触发事件,则会频繁发请求,达不到想 ...
- windows下复制文件报错“文件名对目标文件夹可能过长 。您可以缩短文件名并重试,或者......”
我将一个路径下文件夹复制到另一个路径下时,出现了报错,报错图片如下: 然后查资料发现: 1.文件名长度最大为255个英文字符,其中包括文件扩展名在内.一个汉字相当于两个英文字符.2.文件的全路径名长度 ...
- LeetCode 5 最长对称串
LeetCode 5 最长对称串 最早时候做这道题的时候还是用Java写的,用的是字符串匹配的思路,一直Time Limit Exceeded.甚至还想过用KMP开优化子串查找. public cla ...
- python数组相关知识
1.np中的reshape函数,可以把矩阵重新划分成m行n列. arange(n)可以把 [0,n-1]装入数组中,一定要注意的是img.reshape()并不会改变原来的数组,所以需要另外新建一个数 ...
- 递归算法+sql三种分页
using Maticsoft.Common; using System; using System.Collections.Generic; using System.Data; using Sys ...
- P1772 [ZJOI2006]物流运输
题目描述 物流公司要把一批货物从码头A运到码头B.由于货物量比较大,需要n天才能运完.货物运输过程中一般要转停好几个码头.物流公司通常会设计一条固定的运输路线,以便对整个运输过程实施严格的管理和跟踪. ...
- flask 异步发送邮件
异步发送邮件 当使用SMTP的方式发送电子邮件时,如果你手动使用浏览器测试程序的注册功能,在提交注册表单后,浏览器会有几秒钟的不响应.因为这时候程序正在发送电子邮件,发信的操作阻断了请求--响应循环, ...