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.)
Related
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.
I have a WCF plugin service that loads plugins via MEF and runs them.
Each plugin is a directory with multiple dll's that implements a specific interface.
I load all the plugins in the same AppDomain using MEF (DirectoryCatalog) and run them in a generic way (using reflection).
Now lets assume I have two plugins with dll dependencies:
Plugin1
----> Entities 1.0.0.1
Plugin2
----> Entities 1.0.0.2
I've added some new entities in 1.0.0.2.
When I run the plugin's I get a sporadic error that the new entities doesn't exist in the dll.
I guess that the error occurs since I'm running the code in the same AppDomain and the first Entities.dll that loads is the one that will serve all my plugins.
So, how can I run each plugin with isolation, without creating a new appdomain?
Is there any way I can tell MEF to load all plugin dependencies somehow?
I've read about a couple of solutions on the web:
Create a new appdomain for each plugin - I don't want to go there.
Use the <dependentAssembly> - This didn't work when I first tried it and I don't want my plugin server to get updated on each assembly dependency version change. Plus, I want to be able to run plugins with different assembly versions.
Sign the assemblies with a snk - I didn't try this yet and I'm not sure this solution will work. How will the framework know that he needs to load a different assembly? How is this different from assemblies with different versions? Will I need to configure my service somehow in order to make this work?
Does anybody have any better idea for me?
What's the recommended way to run isolated plugins?
You need to sign your assemblies And make sure the assembly version is different on each one. See the answer from the following: C# Load different versions of assembly to the same project
The CLR does support loading multiple versions of strongly named assemblies into the same AppDomain. This only works though if your assemblies are strongly named and each one has a different version than the other.
We have 2 different applications (developed with .NET):
1st App: A Winservice.
2nd App: A Desktop App.
The Desktop App is loading the assembly from the GAC.
The Winservice was in the past doing the same thing, but now we need that the Winservice loads THE SAME ASSEMBLY from another location.
But the GAC is winning and we can´t make this Winservice (using codebase in the app.config) to load the assembly. It always get the GAC´s one.
If we remove the GAC´s one. It works. But we need the other behavior. Desktop from the GAC and Winservice from the defined path in the codebase.
Is this possible without changing the version of the Assembly?
This is not possible without some kind of work around that modifies the assembly. In short, the GAC always wins. Period. If an assembly has a strong name the GAC is always checked first. Even if you load it into memory as a byte array and use Assembly.Load(byte[]), the strong name will be checked and if it is a GAC'd assembly it gets loaded from the GAC.
A couple possible work arounds:
Bump the assembly version.
Resign the assembly with a different strong name key. Build the service referencing this version.
My target:
We are allowing to integrate our product with third party components (libraries) which are not installed as part of our product because of licensing. At the moment we want to load features related to third party components only if these components are installed on the client's machine.
Current solution:
I'm using Assembly.ReflectionOnlyLoad with providing full names of third party assemblies to validate installation of third party components before the application loads related features. This works for following scenarios:
Exact versions of libraries are installed to GAC
Exact versions of libraries are copied to application directory / probing paths
Problem:
Now I need to modify the solution to support publisher policies (redirecting assembly binding to new version). I just tested my code and it looks like ReflectionOnlyLoad ignores publisher policy deployed to GAC so my mechanism will not load expected features even third party assemblies are correctly installed (new version with assembly redirection).
If I remove my validation (= features will be loaded every time) the application will correctly load new version of third party assemblies so publisher policy works correctly because features are still compiled with dependency to old version.
How to validate existence of the assembly in both GAC and probing paths when using versioning and assembly redirection?
It seems you can call AppDomain.ApplyPolicy to have the policies applied to the assembly name. You can then call ReflectionOnlyLoad() on the returned name.
You can't by using just ReflectionOnlyLoad.
The entire purpose of ReflectionOnlyLoad and friends is to be able to inspect the assembly metadata without regard to policy and versioning.
Junfeng Zhang explains this in his examination of the ReflectionOnlyLoad methods.
I suspect that if you want to apply policy to loading, you'll need to load the assemblies in a separate AppDomain and do reasoning about them there. The benefit of this approach will be that you will be able to unload the AppDomain you use for reflection and verification. The downside is it introduces significant complexity. I don't see much alternative, however.
Once you have a separate AppDomain, you will need to have some MarshalByRefObject class to allow you to do verification in the remote AppDomain from your main one. That class can be loaded into the remote AppDomain and also do any Assembly.Load calls, keeping track of the results. When it is done, you return some kind of report to the caller in the main AppDomain. Finally, you can unload the remote AppDomain. The types you loaded there will not have been loaded in the main AppDomain.
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).