MEF not loading a references again - c#

I have a interface as follows:
[InheritedExport(typeof(ITransform))]
public interface ITransform
{...}
Now, I have two classes:
namespace ProjectA
{
public class Transform:ITransform {....}
}
And
namespace ProjectB
{
public class Transform:ITransform {....}
}
I am using DirectoryCatalog for loading each parts. Each project is compiled and their binaries(build output) location is given as an input to DirectoryCatalog for further composition.
The code for fetching ITransform parts is as follows:
public static class ExtensionFactory
{
public static ITransform GetExtension(string extensionPath)
{
IEnumerable<ITransform> extensions = null;
try
{
AggregateCatalog catalog = new AggregateCatalog();
catalog.Catalogs.Add(new DirectoryCatalog(extensionPath));
CompositionContainer container = new CompositionContainer(catalog);
container.ComposeParts(catalog);
extensions = container.GetExportedValues<ITransform>();
return extensions.FirstOrDefault();
}
catch (Exception ex) {........}
return extensions.FirstOrDefault();
}
}
I have another project ProjectXYZ(auto generated by third party tool(Altova Mapforce 2012 SP1)).
For ProjectA:
namespace ProjectXYZ
{
public classA{...}
}
For ProjectB:
namespace ProjectXYZ
{
public classA{...}
public classB{...}
}
ProjectA.Transform uses ProjectXYZ.ClassA, whereas ProjectB.Transform uses ProjectXYZ.ClassB from another implementation of ProjectXYZ. The implementation and classes of ProjectXYZ varies across for different implementation of ITransform. The classes in ProjectXYZ are automatically generated through some third-party tools, which I need to use directly. So, I cannot make any changes to ProjectXYZ.
So, when first time MEF loads ProjectA.Transform, it also loads ProjectXYZ to be used as a reference for ProjectA. When ProjectB.Transform is getting loaded/exported, then as ProjectXYZ being already in MEF memory, it uses the ProjectXYZ reference available from ProjectA. Thus, when ProjectB.Transform is executing, it searches for ProjectXYZ.ClassB, which it does not gets as MEF has load ProjectXYZ reference available in ProjectA.
How to resolve this problem. The MEF loads the parts correctly, but it does not load the supporting dll's references in a desired manner. I have also tried PartCreationPolicy attribute, but the results are same.

It not a MEF problem. The problem is in the loading model of .NET. (or better the way you're objects are loaded by .net)
When MEF loads it returns the correct objects. But when looking for class ProjectXYZ when projectB is loaded there is already a ProjectXYZ dll loaded with the correct assembly name projectB is referring to. And the loader the dll actually referenced by projectB is not loaded.

Related

Load Assembly at Runtime .NET 6

We are in the beginning stages of converting a c# Winforms App from .NET Framework to .NET 6. We can get the project to build and run in .NET 6, but when it comes to a dynamically loaded assembly, we are having issues. We can get the assembly to load but attempting to access the custom class within it returns a null. I recreated this scenario in two smaller projects as an example.
Solution 1/Project 1 - The code for the assembly to be loaded into the main application. This is a class library that creates the TestAssembly.dll
namespace Custom.TestAssembly
{
public class TestClass : CallingModule
{
public override object GetValue()
{
return "Hello World";
}
}
}
Solution 2/Project 1 - This is a project and class within the main application's solution. This is a class library that creates the Custom.TestAssembly.dll
namespace Custom.TestAssembly
{
public class CallingModule
{
public virtual object? GetValue()
{
return null;
}
}
}
Solution 2/Project 2 - A button has been placed on a form. When it is clicked, the assembly should be loaded, which it is. However, attempting to extract the class from the assembly always returns a NULL.
Form1.cs
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Loader;
namespace TestCallingApplication
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Assembly dynamicAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(#"C:\LocationOf\TestAssembly.dll");
Module customizationModule = dynamicAssembly.GetModule("TestAssembly.dll");
Type customClientModule = customizationModule.GetType("Custom.TestAssembly.TestClass"); //THIS RETURNS A NULL??
}
}
}
Just trying to understand what I am missing. Any thoughts? Or a better way to load runtime assemblies and access classes within them in .NET 6?
Did you reference Solution 2/Project 1 ?
Since they have the same assembly name Custom.TestAssembly, the runtime will not load it again if already loaded in memory.
You can, however, load it under a different AssemblyLoadContext, there's an example on MSDN as well.
Also, you may want to take a look at DotNetCorePlugins, which takes care of assembly loading, reloading, isolation, shared type, and dependency resolving.

Sharing code between two c# assemblies

1) I have a project called SocketServer in which there is a Class Room, this project is a complied exe and can also be complied as a DLL.
2) In Another project called, lets say, MyGame, there is a class called MyAwesomeGame, in which I need to do something like:
public class MyAwesomeGame
{
public Init(Room room)
{
//I can get data from Room
}
}
Now MyGame Project is compiled into a MyGame.DLL, and is placed somewhere relative to the SocketServer.exe, which at runtime loads MyGame.DLL and Instantiates MyAwesomeGame class and calls the Method Init and passes room as its parameter. Code is SocketServer project is something like:
public class Room
{
private InstantiateGameRoom()
{
//Load External MyGame.DLL Assembly
Task<MyAwesomeGame> task = Task<MyAwesomeGame>.Factory.StartNew(() => (MyAwesomeGame)Activator.CreateInstance(classType, new object[] { this }));
MyAwesomeGame instance = task.result;
instance.init(this);
}
}
So my question is how can I get reference of the Class room in MyGame Project? Should I add reference of my SocketServer Project? and if I do, wont it get complied into my MyGame.dll?
p.s: I also intend to distribute the socketserver.dll as an API to thrid-party users.
You should add the SocketServer's generated dll to the references of the MyGame project. Right click on MyGame, click Add, click References..., in the dialog select Projects and select the SocketServer project.
I believe it is also wise to make sure that SocketServer is a depedency for the MyGame project to make sure SocketServer is built before MyGame.
--edit: please read the comment below that I made after Sushant's clarification. Or go straight to Sid's answer. :-)
I think the proper way to share Models among two or more projects is to extract all the models and relative logics to a separate project.
Then you can reference that project from every project that needs to use that data.
The scenario I am talking about is the following:
ModelsProject (Contains models and business logic)
ProjectA (has a dependency on ModelsProject)
ProjectB (has a dependency on ModelsProject)
Scheme

Instantiate a class with internal ctor, from a different assembly

I thought an internal ctor cannot be accessed from a different assembly. Today for the first time I actually needed to use this idea, but it doesn't work as I expected - it can be accessed from a different assembly.
namespace A {
public class AA {
internal AA() { }
}
}
namespace TestNamespace {
public class TestClass {
public void TestMethod() {
var instance = new A.AA(); // <-- this compiles!
}
}
}
...so I'm doing it wrong, or don't know what I'm doing.
Assembly != Namespace
Namespaces provide a logical organizational system. Namespaces are
used both as an "internal" organization system for a program, and as
an "external" organization system — a way of presenting program
elements that are exposed to other programs.
While
Assemblies are used for
physical packaging and deployment. An assembly may contain types, the
executable code used to implement these types, and references to other
assemblies.
Assemblies are usually projects, C# wise.
Read more about it here.

C#, Context Object with Method that takes Interface to External DLL, Throws MissingMethodException

I have a service which acts as a scripting engine for normalization of content. The service extends its scripting language by dynamically loading DLLs at startup, passing a context object interface to a Load() method in each DLL. The context object provides the DLL with methods for Binding string names to factory objects, commands/functors, script object wrappers, etc.
I am having a problem where when the Load() method of one of the DLLs is called it throws a MissingMethodException if it attempts to call one of the binding methods. The method is one which takes an interface which is declared in another DLL, and I suspect something is going on with the method signature when passing the context object through Type.GetMethod.Invoke().
It should be noted that the exception is only thrown when dynamically loading the DLL via Assembly.LoadFile(). Referencing the DLL in the main process and using reflection to get the class/method, then dynamically invoking it from there causes no exception to be thrown.
My code/project reference setup ...
Content.dll:
References nothing
public class ContentPackage { ... }
public interface IContentDataSource
{
ContentPackage Receive();
void Send(ContentPackage content);
}
public interface IContentDataSourceFactory
{
IContentDataSource CreateInstance(string param);
IEnumerable<IContentDataSource> CreateInstances(string param);
}
public class ContentXmlDataSource : IContentDataSource
{
ContentPackage IContentDataSource.Receive() { ... }
void IContentDataSource.Send(ContentPackage content) { ... }
public class Factory : IContentDataSourceFactory
{
IContentDataSource IContentDataSourceFactory.CreateInstance(string param) { return new ContentXmlDataSource(param); }
IEnumerable<IContentDataSource> IContentDataSourceFactory.CreateInstances(string param) { ... }
IContentDataSourceFactory.CreateInstance(
public static Factory Singleton { get { return s_Singleton; } }
private static Factory m_Singleton = new Factory();
}
}
ExtensionInterface.dll:
References Content.dll
public interface IXmlTagCommand() { ... }
public interface IExtensionBindingContext
{
void BindCommand(string name, IXmlTagCommand cmd);
void BindDataSourceFactory(string name, IContentDataSourceFactory factory);
}
Service.dll:
References ExtensionInterface.dll, Content.dll
public partial class TheService
{
public class ExtensionBindingContext : IExtensionBindingContext
{
void IExtensionBindingContext.BindCommand(string name, IXmlTagCommand cmd)
{ ... }
void BindDataSourceFactory(string name, IContentDataSourceFactory factory)
{ ... }
ExtensionBindingContext(string basePath)
{
var paths = Directory.GetFiles(basePath, "*.dll");
foreach (var path in paths)
{
Assembly assembly = Assembly.LoadFile(path);
Type t = assembly.GetType("ExtensionLibrary");
MethodInfo method = t.GetMethod("Load",
BindingFlags.Public | BindingFlags.Static);
method.Invoke(null, new object[] { this }); // Throws exception!
}
}
}
TheService()
{
(new ExtensionBindingContext()).LoadExtensions(".\DLLFolder\");
}
}
Extension.StandardContentFactories.dll: (Loaded dynamically)
References ExtensionInterface.dll, Content.dll
public class ExtensionLibrary
{
public void Load(IExtensionBindingContext context)
{
context.BindCommand("SomeCommand", XTagCommands.SomeCommand); // Fine: No exception.
context.BindDataSourceFactory("ContentXml", ContentXmlFactory.Singleton); // Causes whole function to throw exception if not commented out, preventing even the "BindCommand" call to not be reached.
}
}
The exception specifics:
Exception:
System.Reflection.TargetInvocationException, {"Exception has been thrown by the target of an invocation."}
Inner Exception:
{"Method not found: 'Void SomeNamespace.ContentService.IExtensionBindingContext.BindDataSourceFactory(System.String, SomeNamespace.Content.IContentDataSourceFactory)'."}
If I comment out the BindDataSourceFactory() line in StandardContentFactories.dll, the problem goes away. I am also successfully loading multiple other DLLs in this manner.
I've tried the following:
- Checking GAC folders for same named assemblies from project, none found
- Cleaning solution and rebuilding
- Verifying that all assemblies were using same .NET (4.0, not client profile)
- Passing the interface in a wrapper object instead, changing the method to take the wrapper: Field not found exception instead
- Passing the factory object as an "Object" (updating method parameters as well), then casting that object back into the interface that the factory object inherits: Invalid cast
Is there some kind of issue with passing objects behind interfaces to DLLs via dynamic assembly loading and invoking if that interface has a method that takes an interface which is defined in another referenced assembly? Are there possible work arounds to allow for this dynamic binding of factories from unknown DLLs if the interface issue is non-solvable?
EDIT:
After checking the modules as per mike z's suggestion, I see that their are two copies of each shared interface DLL being loaded (Content.dll and ExtensionInterface.dll); the first set of DLLs is being loaded from the service's host process bin\Debug directory; the second set of DLLs is being loaded from the bin\Debug directory of the first addon DLL that is loaded by Assembly.LoadFile().
The process uses an array of directory paths to search for addon DLLs in, and I was able to force the process to find/load the DLL that has the problem first, which caused the problem to go away. I'm guessing that all of the Assembly.LoadFile() loaded DLLs are using the set of shared interface DLLs that are loaded as a side effect of the first call to Assembly.LoadFile() (i.e. for the first addon DLL). When these DLLs load, maybe they are only including interface information for the IExtensionBindingContext.BindCommand() but not the IExtensionBindingContext.BindDataSourceFactory() and/or IContentDataSourceFactory since they are not used by that DLL.
Does anybody know if this assessment is correct? If so, is there a (standard or correct) way to force my Assembly.LoadFile() DLLs to correctly load ALL of the interface/method information from a shared library? (I'd rather not use contrived solutions like forcing the load order from a config file or creating a fake library that used all the features at the start.)

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.

Categories