MEF DirectoryCatalog won't load, but AssemblyCatalog will - c#

I have a folder full of DLL, some of which implement my contract, and so I tried to load them like so:
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(this.dllFolder));
When it finds one of the DLL with my interface, it complains that it can't find get_Name in the DLL. My interface defines a Name property with a getter and no setter, which the DLL does in fact implement.
Is there some trick to using properties with these catalogs? As a temporary workaround I'm doing this, which does seem to work (and I'm able to access the Name property just fine from the rest of the code)
var dlls = Directory.EnumerateFiles(this.dllFolder, "*.dll");
foreach (var dll in dlls)
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.LoadFile(dll)));

Related

How to unload assembly created by CSharpCodeProvider?

Problem
CSharpCodeProvider can be used to compile source .cs files into an assembly.
However, the assembly is automatically loaded into the AppDomain.CurrentDomain by default. In my case, this is a problem because I need to be able to re-compile the assembly again during runtime, and since it's already loaded in the CurrentDomain, I can't unload that, so I'm stuck.
I have looked through the docs and there seems to be no way to set the target app domain. I have also tried searching it on Google and only found answers where Assembly.Load was used, which I don't think I can use because I need to compile from raw source code, not a .dll
How would one go about doing this? Are there any alternatives or workarounds?
Main program
using (var provider = new CSharpCodeProvider())
{
param.OutputAssembly = "myCompiledMod"
var classFileNames = new DirectoryInfo("C:/sourceCode").GetFiles("*.cs", SearchOption.AllDirectories).Select(fi => fi.FullName).ToArray();
CompilerResults result = provider.CompileAssemblyFromFile(param, classFileNames);
Assembly newAssembly = result.CompiledAssembly // The assembly is already in AppDomain.CurrentDomain!
// If you try compile again, you'll get an error; that class Test already exists
}
C:/sourceCode/test.cs
public class Test {}
What I tried already
I already tried creating a new AppDomain and loading it in there. What happens is the assembly ends up being loaded in both domains.
// <snip>compile code</snip>
Evidence ev = AppDomain.CurrentDomain.Evidence;
AppDomain domain = AppDomain.CreateDomain("NewDomain", ev);
domain.Load(newAssembly);
The answer was to use CSharpCodeProvider().CreateCompiler() instead of just CSharpCodeProvider, and to set param.GenerateInMemory to false. Now I'm able to see line numbers and no visible assembly .dll files are being created, and especially not being locked. This allows for keeping an assembly in memory and reloading it when needed.

How to create a DirectoryCatalog that will search sub directories for plugins

So I have my directory catalog set up shown below:
DirectoryCatalog directoryCatalog = new DirectoryCatalog(#".\Plugins");
var catalog = new AggregateCatalog(directoryCatalog);
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
Which will find any .dll's in my Plugins folder that meet my import criteria. Now To prevent reference clashes in my plugins I would like to put each plugin in a seperate folder.
e.g.
Plugins -> Plugin1 -> plugin1.dll
-> Plugin2 -> plugin2.dll
But my DirectoryCatalog doesn't find these plugins. Is it possible to implement this or do my plugin libraries have to be in that specified .\Plugins folder
You can solve your problem by adding multiple DirectoryCatalogs to AggregateCatalog.Catalogs property like this:
var catalog = new AggregateCatalog();
// iterate over all directories in .\Plugins dir and add all Plugin* dirs to catalogs
foreach (var path in Directory.EnumerateDirectories(#".\Plugins", "Plugin*", SearchOption.TopDirectoryOnly))
{
catalog.Catalogs.Add(new DirectoryCatalog(path));
}
_container = new CompositionContainer(catalog);
this._container.ComposeParts(this);
There is also this:
https://github.com/MefContrib/MefContrib/blob/master/src/MefContrib/Hosting/RecursiveDirectoryCatalog.cs
I found I needed to wrap the _aggregateCatalog.Catalogs.Add(catalog); under Initialize(...) with a try/catch for ReflectionTypeLoadException to stop it throwing when it found non-plugin dlls.

MEF not loading DLL after adding metadata

After adding Metadata to my MEF exports one of my DLL will no longer load, whereas the other does. I declared my variable like so:
[ImportMany]
private IEnumerable<Lazy<IOOOContract, IOOOContractMetaData>> plugins;
and I defined my metadata interface:
public interface IOOOContractMetaData {
string name { get; }
}
To load the code I just used what I think is the standard way:
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(#"...."));
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
The plugin that I want to load is just a class library that is defined like this:
[Export(typeof(IOOOContract))]
[ExportMetadata("name", "TMRAT")]
public class Class1 : IOOOContract
If I run mefx.exe against the directory, I see each DLL properly listing the IOOOContract, and none of them show any output if I use the /rejected flag. The only thing that I can think which might be effecting it is that the DLL which doesn't load has a reference to another DLL that's not part of the GAC. I tried copying that DLL to the same directory listed on the DirectoryCatalog() but that didn't make a difference.

Can Unity Container Load Assemblies Dynamically?

I was developing an application. I want unity to resolve my types WITHOUT having to reference the assemblies in the main project. I tought it loaded assemblies automatically by configuring the type registration by using but doesn't seem to work unless I add a reference to the assembly containing dependencies.
Is there a wat to load types from the assemblies in the current directory?
Thanks!
Sounds like you want MEF and not Unity. MEF is designed for dynamic discovery.
Read the answer to this question: What is different between and purpose of MEF and Unity?
I know this was asked a while ago, but for anyone looking for an answer, you have to make sure that Unity can locate the assemblies at run-time. So you either need to put them in the GAC or drop your assembly dll in the same directory as your executable. If you are using a different directory for dependencies and have a web app, then you have to set the probing element in your web.config:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="Dependencies" />
</assemblyBinding>
</runtime>
And for anyone out there looking for a piece of code on how to solve this, the following code will look for all assemblies on your defined directory and register them with Unity:
string DependenciesPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Dependencies");
string[] Dependencies = Directory.GetFiles(DependenciesPath, DLL_PATTERN);
Dictionary<Type, Type> pluginMappings = new Dictionary<Type, Type>();
//Load Dependency Assemblies
foreach (string fileName in Dependencies)
{
string assemblyName = Path.GetFileNameWithoutExtension(fileName);
if (assemblyName != null)
{
Assembly pluginAssembly = Assembly.Load(assemblyName);
foreach (Type pluginType in pluginAssembly.GetTypes())
{
if (pluginType.IsPublic) //Only look at public types
{
if (!pluginType.IsAbstract) //Only look at non-abstract types
{
//Gets a type object of the interface we need the plugins to match
Type[] typeInterfaces = pluginType.GetInterfaces();
foreach (Type typeInterface in typeInterfaces)
{
if (pluginMappings.ContainsKey(typeInterface))
{
throw new DuplicateTypeMappingException(typeInterface.Name, typeInterface, pluginMappings[typeInterface], pluginType);
}
pluginMappings.Add(typeInterface, pluginType);
}
}
}
}
}
}
//Web API resolve dependencies with Unity
IUnityContainer container = new UnityContainer();
foreach (var mapping in pluginMappings)
{
container.RegisterType(mapping.Key, mapping.Value);
}
It might be a bit late to help, but Unity can dynamically load assemblies if you use the XML configuration approach, register each assembly, and then register the types accordingly. I've been using this process for minor extensions to an otherwise very DI-heavy framework for awhile now.
If you're encountering an issue where Unity fails to resolve a type that's registered in the main application but defined in another, unreferenced assembly and referencing said assembly resolves the issue, that most likely means that it just wasn't copied to the application's output directory. By default, only assemblies that are directly referenced are automatically copied. If they're copied manually or by a post-build event, or if you redirect your build paths so that the unreferenced assemblies build to the application's output directory, it should work.

Proper way of loading assemblies that implement an interface?

I want to do something like this:
Create a number of assemblies that implement IMyInterface.
In the web.config or app.config of my application, define which one of them to load (they may have different names)
When the application starts, load the assembly as marked in web.config and use its implementation of IMyInterface
what is the best way of doing this?
I am using Framework 3.5 at this time.
(p.s. I know I can just define a variable that contains the assembly name (e.g. key="My assembly", value="myassembly.dll" then dynamically load the assembly, just wanting to know if there is a more correct way of doing this)
Assemblies do not implement interfaces; types do.
For ASP.NET apps, I suggest putting every assembly in the /bin directory and use a web.config configuration option and Activator.CreateInstance to create the actual type:
var typeName = "MyNamespace.MyClass, MyAssembly"; // load from config file
IMyInterface instance =
(IMyInterface)Activator.CreateInstance(Type.GetType(typeName));
I do this as follows (i use base class, with abstract methods instead interfaces):
1 - List the classes implemented with my base class.
Assembly assembly = null;
AssemblyName assemblyName = new AssemblyName();
assemblyName.CodeBase = "FullPathToAssembly";
assembly = Assembly.Load(assemblyName);
Type[] arrayTipo;
arrayTipo = assembly.GetTypes();
var tipos =
from t in arrayTipo
where t.BaseType == typeof(DD.Util.BaseProcesso)
select t;
2 - Call the method of execution in an instance of the class:
Type tipo = engine.GetType("FullClassName");
BaseProcesso baseProcesso = (BaseProcesso)Activaor.CreateInstance(tipo, new object[] { "ConstructorParameter1", "ConstructorParameter1") });
// Event
baseProcesso.IndicarProgresso += new BaseProcesso.IndicarProgressoHandler(baseProcesso_IndicarProgresso);
new Thread(new ThreadStart(baseProcesso.Executar)).Start();
Works for me!

Categories