Load AppConfig with binding redirects from resource - c#

I have a .NET Framework 4.6.2 application myApp.exe with several internal and external dependencies. All dependencies are included into the main application myApp.exe as resources.
On application startup, our custom assembly loader gets notified via the event AssemblyResolve and loads the requested assembly from myApp.exe's resources via GetManifestResourceStream() and Assembly.Load().
This works fine, except App.config: the resources contain myApp.App.config, but the custom assembly loader is not loading it yet. This leads to a problem because all the assembly binding redirects specified in App.config are not applied by the .NET Runtime. When placing myApp.exe.config beside myApp.exe (as usual in .NET), everything is working fine.
I'm trying to find a way to avoid the additional file myApp.exe.config. Instead, I want to load the app config (especially the binding redirects) from the resources as well. Is that even possible? If yes, how can I do so?

Unfortunatelly, you can't tell CLR which file you want to load as a runtime config. Runtime looks up for xml configuration in following order:
application config
machine config
The lookup is performed every time when CLR needs info from config, for example binding redirects and assembly locations on assembly load.
It is mostly a part of assembly loading process and it is better to leave as is. You can read more about it here: https://learn.microsoft.com/en-us/dotnet/framework/deployment/how-the-runtime-locates-assemblies
Edit: However, you can live without config file. You said you have all your assemblies embedded into exe assembly and you load everything via custom resolver. Note that your custom assembly resolver is called only when standard resolve pipeline fails to load an assembly. Take a look at this answer: https://stackoverflow.com/a/26673659/2811109

Related

Load an assembly from plugin

INTRODUCTION
I'm writing a plugin for Navisworks and i'm using the Dropbox api to download/upload documents from a repository.
PROBLEM
Dropbox.Api uses the Newtonsoft.Json.dll version 7.0, the problem is that Navisworks uses the 4.0 version of the same assembly, so I cannot use the Dropbox api because it throws an exception every time:
System.AggregateException: One or more errors occurred. ---> System.IO.FileLoadException: Could not load file or assembly 'Newtonsoft.Json, Version=7.0.0.0, ...
So as I understand the program has the assembly 4.0v so Dropbox.Api cannot execute properly.
By now, what I've done is to use another process which I can load the right assembly and download/upload the files from there, but I would like to avoid using a second process.
I'm trying to use reflection to load the assembly at runtime, but it takes no effect, the program still cannot find the newer assembly.
//Load the assembly at the beginning of the plugin
var ass = System.Reflection.Assembly.Load(Properties.Resources.Newtonsoft_Json);
//Use the Dropbox api
//Exception...
Can I force, somehow, the program to use the newer assembly (temporary)?
Is there some solution that I've missed?
You are encountering this issue because you cannot load two different non-strong-named versions of a .NET assembly (no matter where it is on the file system) into the same AppDomain. By default you start with a single AppDomain called the Primary AppDomain for your process.
A strong-named assembly is one that takes the filename; version; signing key and culture to produce a unique assembly.
By now, what I've done is to use another process which I can load the right assembly and download/upload the files from there, but I would like to avoid using a second process.
No need to create a second process, you can instead create a 2nd AppDomain in the same process. Each AppDomain can load different versions of assemblies including Newtonsoft.Json without conflict.
I'm trying to use reflection to load the assembly at runtime, but it takes no effect, the program still cannot find the newer assembly.
That won't work, it's essentially the same as letting .NET doing it for you automatically.
The only time when you can load multiple versions of .NET assemblies into the same AppDomain is if the assembly (in this case the NuGet package) and dependent assemblies are all strong-named. For some reason I have never been able to fathom is why most open source .NET devs refuse to strong-name assemblies.

How to use a newer version of an assembly that Orchard uses

I want to implement a custom module in Orchard CMS. the problem is that I want to reference libraries used by various Orchard CMS modules, which are referencing older versions of these libraries
I want my plugin/module to be self sustaining - I don't want to require modifications be made to orchard.web's web.config file. i want users who are running orchard without a source enlistment to be able to utilize my modules.
How can I achieve this? This page describes my own module's web.config as more or less meaningless in terms of binding redirects... I tried just putting these references (the libraries in question are System.IdentityModel.Tokens.Jwt and Newtonsoft.Json) in the bin directory of my module and reference from there, but when I attempt to install the module I get an error:
Could not load file or assembly 'Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference.
I have read a little bit about Orchard's App_Data/dependencies folder and the orchard.web/bin folder but I am having trouble understanding how it all comes together. How can I allow my module to use a new version of a library while still allowing orchard modules to utilize an old (incompatible) version of that library?
Orchard does a good job of giving the illusion of a composed application with very few global, application-level entities that can't be overridden at the module level. Assembly loading is one of those places where it breaks down however. When the application is compiled and loaded, including when dynamic compilation enters the mix, the assemblies generated and depended upon by all the modules are gathered and copied into the global application's bin folder. Module's bin folders are actually just places assemblies are copied from, but the actual loading happens from the app's bin. So in the end, if you want to know what exactly is loaded into the app, you need to look in the top-level bin folder. If you find the wrong version in here, it's time to dig further and look at what versions modules have, and in what order they are contributing. If I'm not mistaken, that would depend on their dependencies on one another, the outermost dependencies getting their assemblies copied last, and thus winning. That means that you may have some power to tweak what makes it by tweaking module dependency order in their manifest.
The second thing that is global, because that's just the way ASP.NET is, is assembly binding configuration. There are ASP.NET settings that can be set at the directory level, but assembly binding needs to happen at the top, because the app is just one app domain in the end, and only one copy of each assembly will be loaded. Of course, that's a problem in any modular application, because modules had different expectations of versions of their dependencies at the times they were written. .NET deals with this with binding redirects, which basically are a way of instructing the runtime to just assume that everything will be fine if it loads that specific version. It will also tell assemblies that depend on those redirected dependencies a lie, in that it pretends that the version they expected was present, but will actually use the version specified in the redirect. It often works, but not always (breaking changes happen). And if that specific version that you redirect to isn't actually available, either in the GAC or in the app's bin, it will fail.
So in summary:
There's nothing modules can do at their level to unify the version of an assembly that the app will use, it needs to be a binding redirect set at the application level.
The assembly being redirected to must end up in the app's bin.
A module can affect, to a degree, what assemblies get deployed to the app's bin by changing its dependency order in its manifest (but that requires knowledge of those other modules).
Of course, ideally, all third-party modules, as well as Orchard core modules, would get updated to use the latest, but that's clearly rarely an option.

Understanding ASP.NET assembly reference management in Web.config file

I have a simple doubt: I have an external assembly that I want to reference. I have an Asp.Net application. I want to use that assembly in my Asp.Net application.
I add a reference and what VS does is just placing my dll in the Bin subdirectory of my web site.
I thought that VS would have modified my web.config file adding external references...
So does it happen only when referencing assemblies in GAC??? (which makes sense given that public tokens and versions are required).
Thankyou
When the CLR loads your assembly for execution, it checks the assembly's manifest to determine what dependencies are required for it to run. It goes through a series of steps to do this:
Check for redirects - if the assembly is strongly-named, the CLR will first check the appropriate config (app.config, web.config, etc.) to see if there are any binding redirects specified. A binding redirect allows the CLR to say, where I am supposed to load v1.0.0.0, instead load v2.0.0.0. If no binding redirect is found for a strongly-named assembly, it will check a policy file in the GAC, and if no policy file is found, it checks the machine.config. If no binding redirect is specified, the CLR will use the assembly name specified in the calling assembly's manifest to load the assembly.
Check to see if the assembly has already been loaded - the CLR determines if the assembly has previously been loaded, if it has, it uses that same loaded assembly, otherwise it continues...
Load the assembly from the GAC - If the assembly could not previously be loaded and is strongly-named, the CLR will attempt to load the assembly from the Global Assembly Cache.
CodeBase - If the CLR still can't find the assembly, it will use the codeBase path to try and locate the assembly.
Probing - If the CLR still can't find the assembly, it will check the probing path for the assembly. The default probing path is the application base path of the AppDomain into which assemblies are currently being loaded.
(That's all adapted from a great article called Understanding .Net Assemblies and References ).
In the case of your web application, the CLR still does all of the above, but the AppDomain application base path is the /bin folder within your IIS application.

Adding a WCF Service to a project

I have a C# WinForms Project which contains some WCF service definition files which I have created in the project by adding standard classes (not using Add Item > WCF Service).
The project contains some dependencies that require me to build the project for x86 processors.
If I edit the app.config file with the WCF Service Configuration Editor and try to use the "Create New Service ..." wizard, and then browse to the project EXE file (in the debug folder) I get an error, (which I've read is because I'm targeting the x86 processor):
Could not load file or assembly 'EXE_FILE_NAME' or one of its dependencies. An attempt was made to load a program with an incorrect format.
So, upon changing the target to All CPUs, compiling the project again, and then trying to create the service in the WCF Service Configuration Editor again, I now get a different error:
Could not load file or assembly 'SOLUTION_NAME, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
(To clarify, I CAN still compile the project when targeting all CPUs, but get an error at runtime due to a dependency)
Is anybody able to help me with this problem, so I can add the services defined in app.config file using the wizards in WCF Service Configuration Editor? (I think that if I add the services to the WCF Service Configuration Editor when targeting All CPUs I can make any modifications thereafter, regardless of the target)
When debugging, look at the exception detail. From what you've described my guess is you will an assembly loading error in the fusion log because the dependencies of the WCF service can't be satisfied by looking in the default locations.
Say for example you're referencing MrCritter.MyService.dll which defines a WCF service and has a dependency on something like log4net or nHibernate. As long as nothing from those dependencies is leaking into the WCF host (eg returning an ILog) yes you'll be able to compile fine but will get ReflectionTypeLoadException thrown when trying to instantiate the service class if those dependencies aren't somewhere it can find (eg in the executing directory, in GAC etc).

How to prevent a .NET application from loading/referencing an assembly from the GAC?

Can I configure a .NET application in a way (settings in Visual Studio) that it references a "local" assembly (not in GAC) instead of an assembly within the GAC, although both assemblies have the same name and the same version?
If both assemblies are strong-named (signed), the CLR will always load from the GAC.
Here are the steps the runtime uses to resolve assembly references (from How the Runtime Locates Assemblies):
Determines the correct assembly version by examining applicable
configuration files, including the
application configuration file,
publisher policy file, and machine
configuration file. If the
configuration file is located on a
remote machine, the runtime must
locate and download the application
configuration file first.
Checks whether the assembly name has been bound to before and, if so,
uses the previously loaded assembly.
If a previous request to load the
assembly failed, the request fails
immediately without attempting to load
the assembly.
Checks the global assembly cache. If the assembly is found there, the
runtime uses this assembly.
Probes for the assembly (... some material omitted ...)
As stated later in that same article:
There is no version checking for assemblies without strong names, nor does the runtime
check in the global assembly cache for assemblies without strong names.
So if you can afford to remove signing from the local assembly, the application will use it instead of the one in the GAC.
For more detail than you could probably ever want about the runtime-binding mechanisms, see Suzanne Cook's blog.
This blog entry from Scott Hanselman also provides a great overview of the binding process.
If you can change the version number of the local dll, you can use a dll version redirect using the oldVersion attribute. You can use a strong name for the local assembly:
Please look this page:
http://msdn.microsoft.com/en-us/library/7wd6ex19.aspx
Also you should consider that it is possible to modify the version number of a compiled assembly like it is described here:
Change Assembly Version in a compiled .NET assembly
You can use ilmerge and merged the assemblies into a single library to get around it.
To successfully deploy your .NET Framework application, you must understand how the common language runtime locates and binds to the assemblies that make up your application. By default, the runtime attempts to bind with the exact version of an assembly that the application was built with. This default behavior can be overridden by configuration file settings.
You can view binding information in the log file using the Assembly Binding Log Viewer (Fuslogvw.exe), which is included in the Windows Software Development Kit (SDK).

Categories

Resources