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.
Related
I'm working on a .net project, which is using third party .net DLLs. Some of this DLLs are using common DLLs (e.g. Microsoft.Practices.EnterpriseLibrary.Logging). Now we want to use the same common DLLs in a newer version. We are not allowed to use the GAC (politics).
We have separated the parts in different directories.
Third party \ Third party.dll
old common (Microsoft.Practices.EnterpriseLibrary.Logging.dll)
Our libs \ our lib.dll
new common (Microsoft.Practices.EnterpriseLibrary.Logging.dll)
Surprise, Surprise, it did not work. In our dll an error is thrown saying, some option is not valid. Yes, I did not find it in the old common, but in the new. So, I guess, the wrong executable was taken.
In Visual Studio (2015) we have enabled the "Spezific Version" in the reference, and in the Debug / module windows, both DLLs are loaded.
Edit: All Dlls have strong names.
How do I determine which DLL was executed (stepping with F11 just jump to the catch block)? How do I force using the correct DLL?
(The architecture loads first the third party DLL, then our own dll. This is not changeable without a few years rewriting...)
You can use assembly binding redirects and hope the universe doesn't break (there's no guarantee the newer DLL is backward compatible) or you can strong name the dlls.
Why? .NET generally does not allow you to load the "same" assembly more than once in the same AppDomain, unless it is strong-named. What's strong naming? It is a form of identity and digital signing that consists of:
Assembly filename
Assembly version
Assembly culture
Assembly public key
When it's strong-named, both dlls run side-by-side in the same AppDomain within the same process with perfect backward compatibility.
Alternatively if you don't want to use strong-naming (because many files may require signing) or binding redirects, you can always create additional AppDomains and load a version of the dlls into each domain.
Though it gets around the problem of fiddling with files, it does require considerable rework of the rest of the app making it an arguably bad choice at this point in your development.
EDIT: I see now you are using strong names on both.
How do I force using the correct DLL
To distinguish between the two types in the exact same namespace, you might have to create an alias for the newer assembly in your dll reference. Tell me more...
I never understand , What actually happens to the the external DLLs when code is compiled by the compiler and converted to Intermediate code to run on CLR.
Does DLL code added to the Intermediate code and the references are not longer needed on the new machine or just the path of added DLLs are stored and we need those DLLs on our drive to run the program.
Generally (ie. there are some exceptions) the referenced assembly needs to be on the deployment system. All that is included in your assembly is the assembly name (this includes version and possibly signature).
Assembly binding (involving the GAC, config overrides etc.) is applied at runtime to get the right assembly. The rules depend on how the app domain was set up (eg. extra folders can be added, which is why ASP.NET web apps apply different rules).
There are various cases where the reference can be embedded (including referencing ActiveX when the right options are set and use of the assembly binding tool).
I've got an AppDomain hosted plugin architecture. Each plugin is installed in their own folders together with whatever dependency assemblies they need. At runtime the plugins are instantiated into their own AppDomains together with the optional plugin.config file that they can use to specify assembly redirects, etc.
The interfaces for Host-Plugin communication are defined in My.API assembly. The host application has its own My.API assembly, but it is likely that the plugins will bundle the My.API assembly as their dependency as well.
As long as these two My.API assemblies are the same version there's no problem - however if the host application is updated with a newer version of My.API assembly, it would be best to use this backwards compatible My.API assembly for the plugin as well.
Is there a way to force the Host-installed My.API assembly to be loaded in the plugin app domain (and used for assembly binding) instead of the My.API assembly that is located in the plugin folder, which is defined as the ApplicationBase of the AppDomain?
The usual AssemblyResolve events won't work here as those are triggered only when the assembly bind fails - in this case the assembly bind succeeds, but it will bind to the possibly outdated assembly.
I can force the correct assembly to be loaded with LoadFrom, but this doesn't populate the assembly binding cache, which will still load the plugin assembly when assembly binding occurs.
One option I've came up with is to programmatically insert an assembly redirect into the plugin.config to force the My.API to be loaded as version 9999.0.0.0. I expect this would result in AssemblyResolve event that I could then resolve using the correct version of the assembly. However as far as I know, the only way to insert the redirection is to physically modify the plugin.config - something I would like to stay away from.
Are there any other approaches to force a specific assembly binary into the app domain?
I guess publisher policies might be able to solve this, but don't those need GAC deployment? Currently none of the assemblies are deployed into GAC. There might be various versions of the application installed and each of these should be using their specific versions of assemblies. GAC would end up forcing v1.0 of the application to end up with v2.0 assemblies - while only v1.0 plugin in v2.0 application should redirect to v2.0 assemblies.
(We're currently using static 1.0.0.0 assembly version so having different assembly identities shouldn't be a problem as long as we could redirect the physical path.)
I'm writing a plug-in for a 3rd party application (.NET). This application lets me choose the plug-in (as a .dll library file) to load. However, if I have two versions of the same library---they have the same name but are in different directories---and try to load one after the other, it only loads the first plug-in and treats the second as if it was the first. In other words, if the first plug-in is supposed to show a message box saying "First plug-in" and the second plug-in is supposed to show a message box saying "Second plug-in," then loading the second plug-in after the first will actually show a message saying "First plug-in" (i.e., the second plug-in was actually never loaded).
After searching and reading online, I believe that the problem is that the 3rd party application loads its plug-ins into its primary AppDomain. Therefore, plug-in libraries are never unloaded (become locked?) and subsequent attempts to load a plug-in with the same name simply uses the library that's already been loaded. I thought perhaps signing my plug-in libraries would fix the problem, but unfortunately, I'm unable to sign them because I depend on a .dll provided by the 3rd party application and it is not signed. Also, I cannot change the 3rd party application's config file, so I cannot play around with probing.
Our current solution is to re-name the assembly for every version of the plug-in library we have (for example, "PlugIn-1.0.dll" and "PlugIn-2.0.dll"), including re-naming all their dependent assemblies. I don't mean just changing their file name, but changing the AssemblyName property and re-compiling. This works, but I'd like to see if there's a cleaner solution. It wouldn't be so bad if it was just the plug-in assembly name we had to change, but we are also forced to change all their dependent .dll's (because different plug-ins may use different versions of these .dll's as well). I tried creating a config file for the plug-in library to change the probing directory, but this doesn't work. It looks like it is the application itself that does the probing, not the library that depends on the .dll's (am I correct in inferring this?)
Finally, I tried having my plug-in create an AppDomain and load its dependent .dll's into it, but unfortunately my plug-in directory location (and dependent .dll's) must be on a remote location relative to the 3rd party app. There are security/permission issues with loading assemblies over network locations in .NET 4.0 (which I'm using) that I haven't been able to solve.
What are my options? Thanks in advance.
I think you're knocking on the right door with spinning up your own AppDomain. That is the only way I know of to have different versions of the same assembly loaded into the same process.
And yes - as soon as a .NET appdomain has loaded an assembly, it will not load another version of that assembly. Whoever is first wins. And of course, there is no way to unload an assembly once it has been loaded.
As for your permission issues.. Can you copy the assemblies from that network location to a user folder (where the user has write permissions) and load them from there? I've been able to do this successfully for an auto-updating application.
I have a MEF-based application which uses adapters to process files. It uses configuration files to determine which directories to watch and which adapter to use to process each type of file. Plugins take the form of a .dll that implements a common interface.
Each .dll requires its own set of dependent libaries. For instance, plugin1.dll might need to use apilibrary.dll and xmllibrary.dll. It is also possible that at a later date I might want to add plugin2.dll, and plugin2.dll might use xmllibrary.dll as well. These dependent libraries are updated regularly, so I can't count on plugin2.dll using the exact same version of xmllibrary.dll used in plugin1.dll.
I'd like to compile each plugin to one .dll file that invisibly includes within itself all of its dependent libraries, which seems like one way to solve this problem. Alternately, I'd like to figure out how each .dll file can look for its dependent libaries in a subfolder, which I believe would also reduce the possibility of versioning conflicts. Or maybe there's a dead simple solution to this problem that I haven't even considered (which is always very, very likely).
Any thoughts?
You should probably try to get this to work with standard .NET loading rules. However, if you do need to control exactly how assemblies are loaded and which versions are loaded, this blog post shows how: Using Loading contexts effectively
I guess you need to weigh up deployability vs. maintenance. The simple solution is to use a tool called ILMerge. ILMerge takes your project output and can take other assemblies and merge them together. This enables you to wrap up all of the assemblies that your plugin is dependent on, and merge them into a single assembly. Optionally you can do things like re-signing with your public key, etc. Here is a good read: Leveraging ILMerge to simplify deployment and your users experience by Daniel Cazzulino.
But while that is good, what happens if a new version of the referenced assembly is distributed that corrects bugs in that which you have embedded? By the rules of Fusions assembly loader, when it loads the types from your referenced assembly, it will see that they have already been loaded, so there is no reason for it to load the updated version. This would then mean you need to recompile your plugin and merge the newer referenced assembly again.
My question would be, is it really that important to ensure a specific version is used? If a newer version provides an updated implementation (that doesn't break backwards compatibility) then surely this should benefit all plugins that need to reference it?
As for as how assemblies are loaded in reference to each other, have a read of Understanding .Net Assemblies and References, which is an invaluable piece of information.
MEF uses standard .NET assembly loading, and everything's loaded in a single AppDomain. You have very little control over how dependencies are loaded - as they just get loaded automatically by the CLR when the assembly is injected via MEF. Normal CLR assembly loading rules apply when using MEF, so dependencies will be loaded as if they were a dependency of your application - no matter where they're located or referenced.
For the most part, if the plugins and their dependencies are properly written, you most likely will not need to worry about this. As long as the versioning in the dependencies is correct, it will likely just work.