Why is an AppDomain relevant to a plugin manager implemented with MEF - c#

I wrote a simple plugin manager class with MEF and FileSystemWatcher for refreshing plugins automatically, but I hear some say that to be able to add and remove assemblies (plugins) on the fly we need to use an AppDomain also.
Can anyone guide me as to when we need to use AppDomain and MEF together (especially for my plugin manager scenario)?
What is the relationship between them ?

In short, an AppDomain is required when you want to overwrite .dlls that are in use by an application, and that AppDomain must specify the ShadowCopyFiles = "true".
Shadow copying files means that the AppDomain will copy the .dlls to a temporary directory and load them from that temporary directory so the original .dlls can be overwritten.
Unfortunately the assemblies which are loaded into any AppDomain cannot be unloaded unless the AppDomain that contains them is unloaded.
With that in mind, refreshing a plugin is difficult because you would have to A) unload the entire AppDomain which necessarily unloads all other .dlls in that AppDomain, or B) allow a new version of the same .dll to be loaded increasing the memory footprint of your application. The second option also requires your plugin .dlls to be strong named and a different version number in order for MEF to recognise a difference and load the new .dll.

Related

C# Avoid file lock when Assembly.Load

My situation is;
I am designing a plugin application, based on dynamically loading plugin assemblies more than one user may run the application that's hosted on a server machine. Application is auto-updating plugin assemblies from my live update server on its startup. So plugin files (and its satellite dlls should not be locked on file system.
byte[] assemblyBytes = File.ReadAllBytes("asm-path");
var assembly = Assembly.Load(assemblyBytes);
as expected not locking the dll file. but what if the dll I am loading has static reference dlls itself? they are locked on file system now.
to name files, lets say;
APP.Exe is my base application;
PL1.dll is my first plugin, APP.exe loads it in a new Appdomain
PL2.dll is my second plugin, APP.exe loads it in a new Appdomain
PL1_S.dll is a static reference dll for PL1.dll plugin, loaded in PL1 AppDomain
PL_COMMON is a static reference dll for both PL1 and PL2 plugins, loaded by PL1 and PL2 AppDomains
PL1_S and PL_COMMON also should not be locked in file like PL1 and PL2 assemblies
Any idea on how to solve that?
There is functionality already built in to do this, as used by e.g. ASP.Net. See Shadow Copying Assemblies:
Shadow copying enables assemblies that are used in an application domain to be updated without unloading the application domain. This is particularly useful for applications that must be available continuously, such as ASP.NET sites.
So just turn that on via the AppDomainSetup.ShadowCopyFiles when creating your AppDomains

C# Load different versions of assembly to the same project

I'm creating some tool what performs several operations like NUnit.
Inside this tool I open .dll assembly and invoke methods form it to run some test.
Everything is going OK till the time I need to reload .dll withour program restart. The idea is that when tool is run we copy required assembly to some temporary folder and invoke from there. If I need to reload I copy another one to another temporary folder and try to load newly copied from another folder and load to previous assembly object
ExecutingAssembly = Assembly.LoadFrom(AssemblyFullPath);
But my problem is that after I change AssemblyFullPath to new one and call Assembly.LoadFrom it returns just old assembly what was loaded first time but not the second one!
Maybe the problem is that we cannot load several assemblies with different versions? What is the solution?
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.
I'm guessing it's more likely that you are dealing with unsigned assemblies. If that is the case then what you're asking for isn't really possible. Once a given assembly is loaded into an AppDomain it will remain there until the AppDomain is unloaded. To get this to work you will have to abstract out all of the work around the loaded assemblies into a separate AppDomain and use a new AppDomain for every assembly
To expand on JaredPar's answer, you will need to create a new AppDomain and use Remoting to communicate between the two.
Check out http://msdn.microsoft.com/en-us/library/kwdt6w2k(v=vs.85).aspx to help get you started.
Try like this:
string dllFile = "C:\\sample.dll";
Assembly asmLoader = Assembly.LoadFile(dllFile);
Type[] types = asmLoader.GetTypes();
Since all resources from the assembly cannot be reloaded/replaced it's assembly resources while application is still running. It will only be replaced/removed when application is unloaded or the that Assembly that holds it.
Use LoadFile() method. Hope it helps.

Dynamic type instantiation without locking dll

I have a COM+ Application composed of several serviced components.
One of them instantiates on demand a type from a dll through Activator.CreateInstance(pluginType, args);. The type is not a serviced component itself, it contains just the implementation of certain interface that the component knows about.
I don't have problems with the instantiation itself, the problem is that the dll is getting locked by the dllhost.exe, and I would like, if possible, to be able to replace it without shutting down the COM+ Application.
Is it possible somehow to unlock the dll without shutting down the COM+ App?
Or from another point of view, is there a way I could programmatically unload the dll to unlock it?
NOTE: The dll is located in the COM+ application root directory with rest of the dlls.
Loaded modules are referenced counted in windows. In windows we have loadLibrary and freelibrary. If freelibrary is called and the reference count is zero then the dll will be unloaded and free to replace, delete, etc.
In .NET though once an assembly is loaded into an appDomain it is there until the appdomain dies (unless reflection emit collectable assembly). If you were to spawn another appdomain and do the create instance here when the new appdomain dies the assembly will be unloaded.
Asp.net does something called shadow caching. Asp.net you can replace the dlls at anytime without having to kill the worker process. They do this by not loading the dlls in the bin folder but instead they copy them to a seperate location and load them from there in a seperate appdomain. They watch the bin folder, when a change happens they kill the appdomain, copy the dlls to the temp folder, and create a new appdomain.
Shadow Copying Assemblies
http://msdn.microsoft.com/en-us/library/ms404279.aspx
This is not a specific problem for COM+, it is universal on Windows and fundamental to the way it works. Loading code from an executable file (exe or dll) is done by Windows creating a memory-mapped file for the executable file. Code is only read from the file when a page fault requires actually reading the file and mapping the code into RAM. This will happen repeatedly when other processes compete for RAM and the code gets unmapped.
The MMF puts a lock on the file. Required so that the file cannot be modified while it has code mapped into RAM.
There's no workaround for this, the process that has the DLL loaded must terminate or must cooperate and unload the DLL before the lock is released. At best you can rename the file while it is in use, that doesn't otherwise affect the process.

Unload DLL from Memory

How can I unload a DLL from memory that belongs to another process, or the process had ended but the DLL is still loaded into memory?
I know how to find an existing process and even list all the loaded DLL's for that, but what I can't seem to get is how to find loaded DLL's and unload them from memory.
Everything I've read talks about AppDomains, but I'm not sure if this case would apply or not to that method since I want to close DLL's that aren't called by my app.
Any help would be appreciated.
I don't think you can unload a single assembly. But you can unload an AppDomain that contains the assembly.
A shared DLL loaded in domain neutral manner cannot be unloaded unless the Process is exited. Domain specific DLLs are unloaded alongwith Domain. There is however no way to unload individual DLLs even if the DLL has been loaded for reflection only.

Unload AppDomain Assembly

I want to read some information from a .Net assembly, then modify the DLL by appending a short sequence of characters.
The first part works fine, but the second step fails, as the assembly is still in use.
This is the case although I loaded the assembly in its own AppDomain and after I finished step 1 unloaded the AppDomain.
Your best bet is to use something like Cecil that allows you to inspect and rewrite assemblies without loading them in the AppDomain.
References from assemblies loaded into a separate AppDomain have a nasty habit of "leaking" into the parent AppDomain, especially if you're reflecting the assembly and exchanging Type information across the boundary. There are lots of "gotchas" with keeping assemblies isolated in AppDomains truly segregated.
However, there's good news: you probably don't need to worry about the assembly being unloaded in order to modify it on disk - just use shadow copying! Create an AppDomainSetup instance, set its ShadowCopyFiles property to true, and pass it when you create the new AppDomain. This will cause the assembly to be copied to a temporary file before being loaded, keeping the original assembly unlocked and available for modification.
You cannot unload an assembly without unloading the AppDomain:
http://msdn.microsoft.com/en-us/library/ms173101(VS.80).aspx
There is no way to unload an
individual assembly without unloading
all of the application domains that
contain it. Even if the assembly goes
out of scope, the actual assembly file
will remain loaded until all
application domains that contain it
are unloaded.
Are you sure that no other process or AppDomain is using the assembly?

Categories

Resources