I am trying to create a POC with mef where i have the requirement to load dll dynamically in an all ready running project , for this i have created one console application project and a class Library
project .
the code for console application project is as follows-
namespace MefProjectExtension
{
class Program
{
DirectoryCatalog catalog = new DirectoryCatalog(@"D:\MefDll", "*.dll");
[Import("Method1", AllowDefault = true, AllowRecomposition = true)]
public Func<string> method1;
static void Main(string[] args)
{
AppDomainSetup asp = new AppDomainSetup();
asp.ShadowCopyFiles = "true";
AppDomain sp = AppDomain.CreateDomain("sp",null,asp);
string exeassembly = Assembly.GetEntryAssembly().ToString();
BaseClass p = (BaseClass)sp.CreateInstanceAndUnwrap(exeassembly, "MefProjectExtension.BaseClass");
p.run();
}
}
public class BaseClass : MarshalByRefObject
{
[Import("Method1",AllowDefault=true,AllowRecomposition=true)]
public Func<string> method1;
DirectoryCatalog catalog = new DirectoryCatalog(@"D:\MefDll", "*.dll");
public void run()
{
FileSystemWatcher sw = new FileSystemWatcher(@"D:\MefDll", "*.dll");
sw.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.Size;
sw.Changed += onchanged;
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(this);
Console.WriteLine(this.method1());
sw.EnableRaisingEvents = true;
Console.Read();
}
void onchanged(object sender, FileSystemEventArgs e)
{
catalog.Refresh();
Console.WriteLine(this.method1());
}
}
}
the library project which satisfy import looks as follow-
namespace MefLibrary
{
public interface IMethods
{
string Method1();
}
public class CallMethods : IMethods
{
[Export("Method1")]
public string Method1()
{
return "Third6Hello";
}
}
}
once i compile the library project(MefLibrary) and put the dll in D:\MefDll location and run the console application for first time i will see the output as
Third6hello on screen
but now if i change the implementation of method1 and make it return "third7hello" build MEF Library project and replace at D:\MefDll while my console app is running the onchanged handler even after calling catalog refresh prints Third6hello on screen rather than third7hello
Whether anyone knows what is the reason for this , if yes please help.
DirectoryCatalog.Refresh
will only add new or remove existing assemblies. It will not update an assembly. A crude workaround is:
DirectoryCatalog.Refresh
. This will remove the part(s) contained in the assembly.DirectoryCatalog.Refresh
. This will add the updated part(s) contained in the assembly.Note:
AssemblyVersionAttribute
). This is needed because when parts are removed using the DirectoryCatalog.Refresh
the actual assembly will not be unloaded. Assemblies can only be unloaded when the whole application domain is unloaded. So if DirectoryCatalog.Refresh
finds a new assembly it will create an AssemblyCatalog
using the assembly filepath. AssemblyCatalog
will then call Assembly.Load
to load the assembly. But this method will not load an assembly that has the same AssemblyName.FullName
with an already loaded assembly.CompositionOption.IsThreadSafe
flag.As I mentioned this is a workaround. Another approach would be to download MEF's source code and use DirectoryCatalog.cs as a guideline for your own directory catalog implementation.
Once a dll is loaded in an app domain it can't be unloaded from that domain. Only the whole domain can be unloaded. As such it is not easy to implement what you are after. It is possible to constantly scan for the changes and load new copies and repoint the calls (you will be accumulating more and more useless assemblies in your domain this way), but I don't believe this is something that MEF implements out of the box. In other words the behaviour you are observing is by design.
The implementation of this can be also tricky and bug prone because of state. Imagine you set a filed in a class instance of the old DLL and assign it to a variable. Then the new dll comes through. What happens to the old instance and its fields? Apparently they will stay the same and now you have different version of your plug-in in use in memory. What a mess!
And in case you are interested here is the reason why there isn't an Assembly.Unload method. And possible (conceptual) workaround.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With