Choosing a Binding Context


There are three ‘contexts’ for assembly binding:



  • Load context
    In general, if the assembly was found by probing in the GAC, a host assembly store (if hosted), or the ApplicationBase / PrivateBinPaths of the AppDomain, the assembly will be loaded in the Load context.
  • LoadFrom context
    In general, if the user provided Fusion a path which was used to find the assembly (and the assembly at that path wouldn’t have been found in the Load context), then it’s in the LoadFrom context. There are various methods to load by path: LoadFrom(), CreateInstanceFrom(), ExecuteAssembly(), loading an assembly through interop using a codebase, etc.
  • Neither
    If the user generated or found the assembly instead of Fusion, it’s in neither context. This applies to assemblies loaded by Assembly.Load(byte[]) and Reflection Emit assemblies (that haven’t been loaded from disk). Assembly.LoadFile() assemblies are also generally loaded into this context, even though a path is given (because it doesn’t go through Fusion).

Which context is right for you? In general, I strongly recommend that you use the Load context whenever possible. But, of course, there will be times when other contexts are more appropriate for you. So, here are the advantages and disadvantages of each option:



















  Advantages Disadvantages
Load

  • Gets the benefits of versioning and policy.
  • Best avoids “Dll Hell.” Dll Hell can break your app over time.
  • Dependencies available in the Load context are automatically found.


  • Dependencies in other contexts are not available unless you subscribe to the AppDomain.AssemblyResolve event.
LoadFrom

  • Assemblies can be loaded from multiple paths, not just from beneath the ApplicationBase.
  • Dependencies already loaded in this context will automatically be found.
  • Dependencies in the same dir as the requesting LoadFrom context assembly will automatically be found.


  • If a Load context assembly tries to load this assembly by display name, it will fail to be found by default (e.g., when mscorlib.dll deserializes this assembly).
  • Worse, an assembly with the same identity but at a different path could be found on the probing path, causing an InvalidCastException, MissingMethodException, or unexpected method behavior later on.
  • If an assembly by the same identity is already loaded, you’ll get that one back, even if you’ve specified a path to a different one.
  • It does a FileIOPermission.Read + PathDiscovery demand, or a WebPermission demand on the path/URL specified.
  • The ngen image (if any) won’t be used.
  • Can’t be loaded as domain-neutral.
  • v1.0 and v1.1 CLR only: won’t be affected by policy, so can’t be centrally serviced.
Neither

  • Avoids probing costs for loading this assembly.
  • You can load multiple assemblies with the same identity into the same appdomain.
  • You get the bits from the location you specified (as opposed to LoadFrom). Starting in v2, policy/GAC overrides it, however.


  • Nothing can bind to this assembly unless you’ve subscribed to the AssemblyResolve event.
  • Dependencies can only be loaded from the Load context or using the AssemblyResolve event.
  • Passing this assembly to another AppDomain may become tricky (e.g., when this is a Reflection Emit assembly that hasn’t been saved).
  • The ngen image (if any) won’t be used.
  • Can’t be loaded as domain-neutral.
  • v1.0 and v1.1 CLR only: won’t be affected by policy, so can’t be centrally serviced.

Comments (41)

  1. Dmitriy Zaslavskiy says:

    What changes, if anything, if I subscrive to AssemblyResolve and resolve
    original Load() calls with LoadFrom()

    Thanks

  2. Yiru Tang says:

    I found the load articles here are very helpful to my work. Thanks.

    I don’t quite understand one statment in the table though. It is the first item in LoadFrom, DisAdvantages:"If a Load context assembly tries to load this assembly by display name, it will fail by default (e.g., when mscorlib.dll deserializes this assembly). " What does it mean, could you give an more detailed example?

  3. Suzanne says:

    The AssemblyResolve event just passes the already-loaded Assembly around. If it was loaded in the LoadFrom context (or whatever), it will remain there – no changes.

  4. Suzanne says:

    Thanks, Yiru! For your question: say your AppDomain’s ApplicationBase is C:foo, and you’ve done a LoadFrom(@"C:barA.dll"). So, assembly A is in the LoadFrom context. If you serialize A, and then deserialize it in that appdomain, deserialization will call Load("A, […]"). (Note that path info is not included in that call.) If A is not available in the Load context, you’ll get an exception, even though A is already loaded in the appdomain. You would have to subscribe to the AssemblyResolve event to resolve it. But, worse, if another copy of A is available in the GAC or beneath the ApplicationBase, then Load() will load that instead of the one in the C:bar dir. Then, both A.dll’s will be loaded in the same appdomain, and their types will not be castable to each other.

  5. Hi Suzannne (great log).

    What is the difference (if there is any) beetween:

    Assembly.Load(AsssemblyName.GetAssemblyName(path))

    and

    Assembly.LoadFrom(path);

    It seems to be that the first is better…

    Simon.

  6. Suzanne says:

    Thanks. The difference is that the the first one may load that assembly at a different path than the one specified, if it’s available in the Load context. Also, in that case, it causes policy to be applied when it would not have been for LoadFrom() (pre-v2.0).

    Also consider the performance implications of the first one: it may cause the file to be loaded and unloaded, when it’s going to be immediately loaded, anyway. Plus, it may cause extra probing by display name. If the ApplicationBase starts with "http:", that would cause a possibly significant delay.

  7. We have a light exe on the client machine that calls Assembly.LoadFrom to No Touch Deploy our assemblies from a web server. If I understand you, calling Load may help our performance. We already handle the AssemblyResolve in this light exe:

    AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf AssemblyResolve

    I would appreciate your feedback on this: greg@cds-am.net

  8. Roopesh says:

    Hi,

    I am using the probing in my app.config. Even though LoadFrom is not working . Can any body give suggestions

    Regards

    -Roopesh

  9. Commonality says:

    Jon Flanders posts here about a problem he diagnosed recently about Assembly.LoadFrom() and Assembly.Load() behaving differently. The issue he basically…

  10. Scott says:

    Is there any known problems with calling LoadFrom() from one Forms application to start another Forms application in a different thread.

    I have an applicaton with a simple [Serializable] struct. In this app I have a function that serializes and deserializes the struct. It works fine if I call the function when the process is started normally, but if it is called from a thread created by a different assembly I get either:

    SerializationException: Cannot find the assembly …

    or if I put the assembly with that serialzation stuff in the same directory with the application that creates the thread I get:

    InvalidCastException: Specified cast is not valid.

  11. Matthew H says:

    We have an app that loads a bunch off dlls. We have a base class called say ‘A’ defined in a dll in the APP_BASE that all our plugin dlls subclass.

    All our plugin dlls live in private paths under APP_BASE for which I search on app start and call Load, get the types that implement our custom interface, create an instance of each and cast to A. All fine.

    Now when I want to load plugin dlls external to the APP_BASE. I am forced to use LoadFile/LoadFrom but now when I try to cast to A, it fails because A in my app and A in the loadfile/loadfrom context is different! Very annoying, any ideas how to fix this? I notice that if A is an interface then there’s no problem (btw, I cant make it an interface).

    Thanks for the help

    Matthew

    (Matthew dot Hayhurst at Gmail dot com)

  12. Jim WIese says:

    I recently ran into the problem with the AssemblyResolve event in the AppDomain. Basically, multiple threads were concurrently firing this event, and multiple instances of the *same* assembly (binary the same, but differenct instances) were loaded. The end result was that I received the MethodMissingException and chased it for about a week. Long story short, I now have a lock in the class that handles this event, and I have to keep a look aside hashtable with WeakReferences to the assembly. I don’t keep direct references because I want to enable the unloading of the Assembly from the AppDomain if desired

  13. Prasad says:

    Hello,

    I am running into specified cast error and I posted the question on the forum. I ran into this error only after upgrading to Windows 2003 SP1 which I believe also installs .NET framework 1.1 SP1.

    http://groups.google.co.in/group/microsoft.public.dotnet.framework.aspnet/browse_frm/thread/1427cdfa08ee5efd/ad4f9ef611969749?tvc=1&q=%22Shade+some+light%22&hl=en#ad4f9ef611969749

    Can you please look at the thread and repond?

    Thanks.

    -Prasad

  14. adhingra says:

    Hi Suzanne

    I have been following your threads on Assembly loading, Binding Contexts and all. I also looked at some articles on gotdotnet.com especially the one that talks in detail about Assembly.LoadFrom (http://www.gotdotnet.com/team/clr/LoadFromIsolation.aspx)

    I recently wrote a test sample to test out all these explanations. The result that Assebmly.LoadFrom api gave was not consistent with the theory. Thinking that I may be doing something wrong in my test, I revalidated the test very carefully, but the results are the same.

    I would really appreciate if you can explain whats going on here.

    The sample is an attempt to explore the feasibility within the .NET framework to come up with a plug-in style architecture for an application that involves on demand loading of component and their dependencies. Here are the files that I created

    1) Sample.Exe – the main client exe

    2) Interfaces.Dll – assembly that defines the interfaces. This assembly is NOT strongly named

    3) SampleComponent.Dll – The sample plug-in component that implements the interfaces from the dll above. This assembly is NOT strongly named.

    4) CommonFuncs.dll – The assembly to hold some common util functions. This assembly IS strongly named and i created two versions of it differing in version number but same public key. We will call them v1.0 and v2.0

    Here are the deployment directories

    c:SampleSample.exe

    c:SampleInterfaces.dll

    c:SampleCommonFuncs.dll (v1.0)

    c:SampleComponentSampleComponent.dll

    c:SampleComponentInterfaces.dll

    c:SampleComponentCommonFuncs.dll (v2.0)

    Note: The client.exe has a compile time static reference to v1.0 of the CommonFuncs.dll and the samplecomponent.dll has a static compile time reference of v2.0 of the samplecomponent.dll. This is done deliberately to simulate a situation that there may be different plugins that are compiled with different version numbers of a common dependent dll and may have their own internal changes for that dll. But strong name signing will make all these dependent dlls unique in Identity.

    The client code executable also has an internal object that implements interfaces from Interfaces.dll. In the sameple.exe i first create this internal object using the interface so that the interfaces.dll from the sample directory gets loaded. This internal object then calls a utility function from Commonfuncs.dll so v1.0 of this strongly named dll is loaded. Now as per the theory both these dls are loaded in the load context of the exe.

    The client code then loads the samplecomponent.dll using Assembly.LoadFrom from the path. Now by the theory the Interfaces.dll that is already loaded from the static binding of the client is used to reference the component coming in from the "LoadFrom" which is what we expect.

    But the surprising thing is when i call a function on the sample component which internally uses v2.0 of the commonfuncs.dll, the call fails. Using processview it is very clear that the loader sub system is trying to match the static reference of commonfuncs.dll in the sample component (i.e. 2.0) against v1.0 of the commonfuncs.dll which is already loaded in the load context of the exe.

    As per the info that I have gathered from your blogs and especially the gotdotnet article (mentioned above) the loader will INDEED first look into the load context and the appbase to look for the dependency v2.0 of the sample component, but they should have failed because the Identity of the assemblies will not match.

    In my opinion then the loader should have proceeded to look in the directory where the sample component is loaded from and would have found a match for v2.0 of commonfuncs.dll that can be resolved as a static reference to the samplecomponent.dll

    Now its possible that I may have gotten the theory wrong, but this is a very common scenario for component/plug-in software architecture. If my understanding is not correct then how does .NET framework offers to handle this kind of scenario.

    Thanks

    Ashoo

  15. PRK says:

    Help required on running 1.1 binaries in 2.0

    Hi,

    We’ve built the binaries under 1.1 environment and they are running fine on a machine with 1.1 and 2.0 environments. But when the same binaries are built in 2.0 environment, following errors are seen. Any idea how to resolve them.

    ‘Managed’: Loaded ‘F:WindowsassemblyGAC_32mscorlib2.0.0.0__b77a5c561934e089mscorlib.dll’, Skipped loading symbols. Module is optimized and the debugger option ‘Just My Code’ is enabled.

    ‘Managed’: Loaded ‘C:servicetestIpfilterEngine.exe’, Symbols loaded.

    ‘Managed’: Loaded ‘C:servicetestutility.dll’, Symbols loaded.

    ‘Managed’: Loaded ‘F:WindowsassemblyGAC_MSILSystem2.0.0.0__b77a5c561934e089System.dll’, Skipped loading symbols. Module is optimized and the debugger option ‘Just My Code’ is enabled.

    ‘Managed’: Loaded ‘F:WindowsassemblyGAC_MSILSystem.Xml2.0.0.0__b77a5c561934e089System.Xml.dll’, Skipped loading symbols. Module is optimized and the debugger option ‘Just My Code’ is enabled.

    ‘Managed’: Loaded ‘F:WindowsassemblyGAC_MSILSystem.Configuration2.0.0.0__b03f5f7f11d50a3aSystem.Configuration.dll’, Skipped loading symbols. Module is optimized and the debugger option ‘Just My Code’ is enabled.

    Managed Debugging Assistant ‘BindingFailure’ has detected a problem in ‘C:servicetestIpfilterEngine.exe’.

    Additional Information: The assembly with display name ‘ipfilterengine.XmlSerializers’ failed to load in the ‘LoadFrom’ binding context of the AppDomain with ID 1. The cause of the failure was: System.IO.FileNotFoundException: Could not load file or assembly ‘ipfilterengine.XmlSerializers, Version=6.0.5268.0, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. The system cannot find the file specified.

    File name: ‘ipfilterengine.XmlSerializers, Version=6.0.5268.0, Culture=neutral, PublicKeyToken=null’

    === Pre-bind state information ===

    LOG: User = SRA-STRESS-1Administrator

    LOG: DisplayName = ipfilterengine.XmlSerializers, Version=6.0.5268.0, Culture=neutral, PublicKeyToken=null, processorArchitecture=MSIL

    (Fully-specified)

    LOG: Appbase = file:///C:/servicetest/

    LOG: Initial PrivatePath = NULL

    Calling assembly : System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089.

    ===

    LOG: This bind starts in default load context.

    LOG: No application configuration file found.

    LOG: Using machine configuration file from F:WindowsMicrosoft.NETFrameworkv2.0.50727configmachine.config.

    LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).

    LOG: Attempting download of new URL file:///C:/servicetest/ipfilterengine.XmlSerializers.DLL.

    LOG: Attempting download of new URL file:///C:/servicetest/ipfilterengine.XmlSerializers/ipfilterengine.XmlSerializers.DLL.

    LOG: Attempting download of new URL file:///C:/servicetest/ipfilterengine.XmlSerializers.EXE.

    LOG: Attempting download of new URL file:///C:/servicetest/ipfilterengine.XmlSerializers/ipfilterengine.XmlSerializers.EXE.

    Managed Debugging Assistant ‘BindingFailure’ has detected a problem in ‘C:servicetestIpfilterEngine.exe’.

    Additional Information: The assembly with display name ‘ipfilterengine.XmlSerializers’ failed to load in the ‘Load’ binding context of the AppDomain with ID 1. The cause of the failure was: System.IO.FileNotFoundException: Could not load file or assembly ‘ipfilterengine.XmlSerializers’ or one of its dependencies. The system cannot find the file specified.

    File name: ‘ipfilterengine.XmlSerializers’

    === Pre-bind state information ===

    LOG: User = SRA-STRESS-1Administrator

    LOG: DisplayName = ipfilterengine.XmlSerializers

    (Partial)

    LOG: Appbase = file:///C:/servicetest/

    LOG: Initial PrivatePath = NULL

    Calling assembly : System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089.

    ===

    LOG: This bind starts in default load context.

    LOG: No application configuration file found.

    LOG: Using machine configuration file from F:WindowsMicrosoft.NETFrameworkv2.0.50727configmachine.config.

    LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).

    LOG: Attempting download of new URL file:///C:/servicetest/ipfilterengine.XmlSerializers.DLL.

    LOG: Attempting download of new URL file:///C:/servicetest/ipfilterengine.XmlSerializers/ipfilterengine.XmlSerializers.DLL.

    LOG: Attempting download of new URL file:///C:/servicetest/ipfilterengine.XmlSerializers.EXE.

    LOG: Attempting download of new URL file:///C:/servicetest/ipfilterengine.XmlSerializers/ipfilterengine.XmlSerializers.EXE.

    ‘Managed’: Loaded ‘C:servicetestremoteserver.dll’, Symbols loaded.

    ‘Managed’: Loaded ‘igg8sw7i’, No symbols loaded.

    Managed Debugging Assistant ‘BindingFailure’ has detected a problem in ‘C:servicetestIpfilterEngine.exe’.

    Additional Information: The assembly with display name ‘ipfilterengine.XmlSerializers’ failed to load in the ‘LoadFrom’ binding context of the AppDomain with ID 1. The cause of the failure was: System.IO.FileNotFoundException: Could not load file or assembly ‘ipfilterengine.XmlSerializers, Version=6.0.5268.0, Culture=neutral, PublicKeyToken=null’ or one of its dependencies. The system cannot find the file specified.

    File name: ‘ipfilterengine.XmlSerializers, Version=6.0.5268.0, Culture=neutral, PublicKeyToken=null’

    === Pre-bind state information ===

    LOG: User = SRA-STRESS-1Administrator

    LOG: DisplayName = ipfilterengine.XmlSerializers, Version=6.0.5268.0, Culture=neutral, PublicKeyToken=null, processorArchitecture=MSIL

    (Fully-specified)

    LOG: Appbase = file:///C:/servicetest/

    LOG: Initial PrivatePath = NULL

    Calling assembly : System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089.

    ===

    LOG: This bind starts in default load context.

    LOG: No application configuration file found.

    LOG: Using machine configuration file from F:WindowsMicrosoft.NETFrameworkv2.0.50727configmachine.config.

    LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).

    LOG: The same bind was seen before, and was failed with hr = 0x80070002.

    Managed Debugging Assistant ‘BindingFailure’ has detected a problem in ‘C:servicetestIpfilterEngine.exe’.

    Additional Information: The assembly with display name ‘ipfilterengine.XmlSerializers’ failed to load in the ‘Load’ binding context of the AppDomain with ID 1. The cause of the failure was: System.IO.FileNotFoundException: Could not load file or assembly ‘ipfilterengine.XmlSerializers’ or one of its dependencies. The system cannot find the file specified.

    File name: ‘ipfilterengine.XmlSerializers’

    === Pre-bind state information ===

    LOG: User = SRA-STRESS-1Administrator

    LOG: DisplayName = ipfilterengine.XmlSerializers

    (Partial)

    LOG: Appbase = file:///C:/servicetest/

    LOG: Initial PrivatePath = NULL

    Calling assembly : System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089.

    ===

    LOG: This bind starts in default load context.

    LOG: No application configuration file found.

    LOG: Using machine configuration file from F:WindowsMicrosoft.NETFrameworkv2.0.50727configmachine.config.

    LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).

    LOG: The same bind was seen before, and was failed with hr = 0x80070002.

    ‘Managed’: Loaded ‘sxj1yu2h’, No symbols loaded.

    ‘Managed’: Loaded ‘F:WindowsassemblyGAC_MSILSystem.Runtime.Remoting2.0.0.0__b77a5c561934e089System.Runtime.Remoting.dll’, Skipped loading symbols. Module is optimized and the debugger option ‘Just My Code’ is enabled.

    The thread 0xadc has exited with code 0 (0x0).

    The thread 0xddc has exited with code 0 (0x0).

    The thread 0xe1c has exited with code 0 (0x0).

    The program ‘[2996] IpfilterEngine.exe: Managed’ has exited with code 0 (0x0).

  16. Divakar says:

    Hi

    In my project i have multiple dlls with same name and different versions.

    Example i have 3 Dllsignature.dll in different locations. Each ones veriosn are different.

    My aim is to load the latest version of Dll in to my application and do access that letest Dll and dipsly its properties in UI.

    Since the Dll’s name is same with same identity i have used LoadFile since i need correct version of all dlls.

    But one of this Dllsignature dll has some dependency dlls also. While loading iam getting assembly.GetExportedTypes(). So if i use LoadFile then it fails to load the dependencies and it throws an exception.

    So problem is in the preload stage i want to use the LoadFile. Once i get all the Dlls i store it in a list with sorted order son that always 1st dll is the latest verison. And i try to load again back using LoadFrom.

    Problem: Since i have used LoadFile in the preload stage and later stage iam trying to load the latest version using LoadFrom in order to load all its dependencies. but still i am getting exception "Dependency Dll file does not found" at the line assembly.GetExportedTypes.

    Here is the piece of code

    Assembly a = Assembly.LoadFile(DllPath);

    string version = string.Empty;

    int i = a.FullName.IndexOf("Version=");

    if (i >= 0)

    {

    int j = a.FullName.IndexOf(‘ ‘, i);

    version = a.FullName.Substring(i + 8, j – (i + 8) + 1);

    }

    FileInfo fi = new FileInfo

    (DllPath);

    version += fi.LastWriteTimeUtc.ToString

    ("yyyy-MM-dd hh:mm:ss");

    //Get publicly-visible types.

    Type[] types = a.GetExportedTypes();

    foreach (Type t in types)

    {

    }

    So could you please give me some solution code..

    You can mail me to divakara_v@yahoo.com

    Thanks

    Divakar

  17. Jake Tober says:

    I’ve been bashing my head against the wall for two days now about a webservice call in a loaded assembly resulting in "specified cast is not valid" and then I found your article and the answer was right in front of my eyes. 🙂

    Thank you so much.

  18. Seth Flowers Deputy Van Halen says:

    The problem I am experiencing is with LoadFrom.

    On a development machine, LoadFrom is resolving dependencies. After deploying, I can debug remotely, and watch LoadFrom load the correct module. At this point in our code we attempt to create an instance of a type in a dependent assembly. On a development machine, I can see that this new assembly is loaded up correctly, but in the deployed version, no dependencies are loaded up, and an error is thrown.

    LoadFrom is supposed to check the path of this newly loaded assembly for dependencies, which it correctly does on a development machine, but fails in our deployed version.

    We are developing against the 2.0 framework ( 2.0.50727.42 ).

  19. Thanks to Suzanne Cook for this one. "First, obviously, find the two types for which the cast failed,

  20. AppDomain.AssemblyResolve – Cool, but dangerous … just the way I like my coffee

  21. Calling Load(AssemblyName) is not necessarily the same as calling Load(String). If the AssemblyName.CodeBase

  22. First, obviously, find the two types for which the cast failed and verify that they are the same type

  23. Be careful – these aren’t the same thing. LoadFrom() goes through Fusion and can be redirected to another

  24. I hesitate to talk about this because I don’t want people who don’t know about it to think, "Hey, what’s

  25. There are two types of assembly identity that the loader deals with: bind-time and after bind-time. The

  26. So, after checking out the binding context options , you’ve decided to switch your app to use the Load

  27. Eriq VanBibber says:

    Suzanne,

    I’ve looked around but i can’t seem to find a concise doc about this:

    I’m using AssemblyResolve in an assembly that is consumed by an ASP.NET web service.

    Seems that my AssemblyResolve subscription is never being fired.

    Is asp.net listening to that event also and possibly stepping in front of my assembly?

    What are my options?  I really want to use a .Load(Byte[]) method so that i can deliver my assembly as a single file.  I want to do it this way to protect the code inside from being decompiled/inspected.

    Regards,

    Eriq

  28. saikatch says:

    Awesome post … helped me lot to solve my problem

  29. Alan says:

    Excellent post — thanks for the help.

  30. What about performance? Let me see if i understood correctly, I have a ASP.NET site wich will be executing some tasks from a set of dlls, if the connected user triggers the action and I use the Assembly.Load to get the needed instances and execute it will load the assembly for ALL site users? thus optimizing the resources?

  31. Alan says:

    Excellent post — It’s rare to find a post which really gives insight, rather than a laundry list of try-this-and-see-if-it-works items.  Very refreshing.  Very well done.  Thanks for this.

    -Alan.