C# MEF dependency injection: the classes in the module cannot be loaded - c#

I want to use MEF to achieve dependency injection. However, when trying to load all the different .dll files it always runs into the an System.Reflection.ReflectionTypeLoadException: The classes in the module cannot be loaded. This occurs when trying to load "Microsoft.AspNet.Identity.Owin.dll".
I'm trying to do this on a mac, using the mono compiler and the target framework set at: .NET Framework 4.5.2
#region Constructor
public ModuleConfigurationCatalog()
{
var assemblyUri = new Uri(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath));
var assemblyDir = assemblyUri.LocalPath;
var allFiles = Directory.EnumerateFiles(assemblyDir, "*.*", SearchOption.AllDirectories);
var assemblies = allFiles.Where(f => f.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || f.EndsWith(".exe", StringComparison.OrdinalIgnoreCase)).Distinct();
_catalog = new AggregateCatalog();
foreach (var assembly in assemblies)
{
var assemblyCatalog = new AssemblyCatalog(assembly);
if (assemblyCatalog.Parts.ToList().Count > 0)
{
_catalog.Catalogs.Add(assemblyCatalog);
}
}
}
#endregion
breakpoint information
Stacktrace

You're probably missing a dependency of the Microsoft.AspNet.Identity.Owin.dll assembly. Make sure all of its dependencies exist in the same folder. (Or maybe load it from the GAC if it exists there... not sure how to do that with MEF, though.)

Related

Reflection only assembly loading in .Net Core

I have a .Net Framework WPF application that I'm currently migrating to .Net6. At startup it examines certain assemblies in the executable folder looking for any with a custom assembly attribute. Those that have this are then loaded into the current appdomain. (Note that some of these assemblies may already be in the appdomain, as they are projects in the running application's solution).
This is the 4.x code:
private void LoadAssemblies(string folder)
{
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve +=
(s, e) => Assembly.ReflectionOnlyLoad(e.Name);
var assemblyFiles = Directory.GetFiles(folder, "*.Client.dll");
foreach (var assemblyFile in assemblyFiles)
{
var reflectionOnlyAssembly = Assembly.ReflectionOnlyLoadFrom(assemblyFile);
if (ContainsCustomAttr(reflectionOnlyAssembly))
{
var assembly = Assembly.LoadFrom(assemblyFile);
ProcessAssembly(assembly);
}
}
}
The custom assembly attribute (that this code is looking for) has a string property containing a path to a XAML resource file within that assembly. The ProcessAssembly() method adds this resource file to the application's merged dictionary, something like this:
var resourceUri = string.Format(
"pack://application:,,,/{0};component/{1}",
assembly.GetName().Name,
mimicAssemblyAttribute.DataTemplatePath);
var uri = new Uri(resourceUri, UriKind.RelativeOrAbsolute);
application.Resources.MergedDictionaries.Add(new ResourceDictionary { Source = uri });
Just to reiterate, all this works as it should in the .Net 4.x application.
.Net6 on the other hand doesn't support reflection-only loading, nor can you create a second app domain in which to load the assemblies. I rewrote the above code by loading the assemblies being examined into what I understand is a temporary, unloadable context:
private void LoadAssemblies(string folder)
{
var assemblyFiles = Directory.GetFiles(folder, "*.Client.dll");
using (var ctx = new TempAssemblyLoadContext(AppDomain.CurrentDomain.BaseDirectory))
{
foreach (var assemblyFile in assemblyFiles)
{
var assm = ctx.LoadFromAssemblyPath(assemblyFile);
if (ContainsCustomAttr(assm))
{
var assm2 = Assembly.LoadFrom(assemblyFile);
ProcessAssembly(assm2);
}
}
}
}
private class TempAssemblyLoadContext : AssemblyLoadContext, IDisposable
{
private AssemblyDependencyResolver _resolver;
public TempAssemblyLoadContext(string readerLocation)
: base(isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(readerLocation);
}
public void Dispose()
{
Unload();
}
protected override Assembly Load(AssemblyName assemblyName)
{
var path = _resolver.ResolveAssemblyToPath(assemblyName);
if (path != null)
{
return LoadFromAssemblyPath(path);
}
return null;
}
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
{
var path = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
if (path != null)
{
return LoadUnmanagedDllFromPath(path);
}
return IntPtr.Zero;
}
}
(Note the ProcessAssembly() method is unchanged).
This code "works" in so much as it goes through the motions without crashing. However at a later point when the application starts creating the views, I get the following exception:
The component '..ModeSelectorView' does not have a resource identified by the URI '/.;component/views/modeselector/modeselectorview.xaml'.
This particular view resides in a project of this application's solution, so the assembly will already be in the appdomain. The assembly also contains that custom attribute so the above code will be trying to load it, although I believe that Assembly.LoadFrom() should not load the same assembly again?
Just in case, I modified the "if" block in my LoadAssemblies() method to ignore assemblies already in the app domain:
if (ContainsCustomAttr(assm) && !AppDomain.CurrentDomain.GetAssemblies().Contains(assm))
Sure enough, a breakpoint shows that the assembly in question (containing that view) is ignored and not loaded into the app domain. However I still get the same exception further down the line.
In fact I can comment out the entire "if" block so no assemblies are being loaded into the app domain, and I still get the exception, suggesting that it's caused by loading the assembly into that AssemblyLoadContext.
Also, a breakpoint shows that context is being unloaded via its Dispose() method, upon dropping out of the "using" block in the LoadAssemblies() method.
Edit: even with the "if" block commented out, a breakpoint at the end of the method shows that all the assemblies being loaded by ctx.LoadFromAssemblyPath() are ending up in AppDomain.Current. What am I not understanding? Is the context part of the appdomain and not a separate "area"? How can I achieve this "isolated" loading of assemblies in a similar way to the "reflection only" approach that I was using in .Net 4.x?
Okay, so I found the answer, which is to use MetadataLoadContext. This is essentially the .Net Core replacement for reflection-only loading:
private void LoadAssemblies(string folder)
{
// The load context needs access to the .Net "core" assemblies...
var allAssemblies = Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.Client.dll").ToList();
// .. and the assemblies that I need to examine.
var assembliesToExamine = Directory.GetFiles(folder, "NuIns.CoDaq.*.Client.dll");
allAssemblies.AddRange(assembliesToExamine);
var resolver = new PathAssemblyResolver(allAssemblies);
using (var mlc = new MetadataLoadContext(resolver))
{
foreach (var assemblyFile in assembliesToExamine)
{
var assm = mlc.LoadFromAssemblyPath(assemblyFile);
if (ContainsCustomAttr(assm))
{
var assm2 = Assembly.LoadFrom(assemblyFile);
AddMimicAssemblyInfo(assm2);
}
}
}
}

Could not load type 'Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager' from assembly 'Microsoft.Azure.WebJobs.Host

The application is in Azure Functions,
The error that we are getting from container Pod logs is "Could not load type 'Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager' from assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.26.0".
In our application version all of the dll ver is 3.0.30.0
In the "dll" folder of debug is having the version with 3.0.30.0
And in this version 3.0.30.0, it has the class "Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager"
Not sure, where this "assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.26.0" is coming from.
For me this was happening because Azure Functions Core Tools version mismatched due to upgradation of Visual Studio to latest version.
Removing the Azure Function Tools from the system path C:\Users\user.name\AppData\Local\AzureFunctionsTools and Let Visual Studio automatically install Azure Functions Core Tools fixed the issue.
I had the same issue as below log.
Could not load type 'Microsoft.Azure.WebJobs.Host.Scale.ConcurrencyManager' from assembly 'Microsoft.Azure.WebJobs.Host, Version=3.0.25.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.
It was due to my base image for azure functions was old. using the newer base image with below tag(mcr.microsoft.com/azure-functions/dotnet:3.4.2) has fixed my issue.
FROM mcr.microsoft.com/azure-functions/dotnet:3.4.2 AS base
WORKDIR /home/site/wwwroot
EXPOSE 80
FROM mcr.microsoft.com/dotnet/sdk:3.1.416 AS build
WORKDIR /src
This is not direct answer to your question but a tool that would answer it for you. As I had a lot of this kind of errors I have written a helper code to do just that. Its written for the .net framework but with minor changes you can have same thing on core.
//folder where dependencies should be found
var dir = #"C:\Repos\Project\bin";
//dll or exe that you want to inspect
var dll = #"C:\Repos\Project\bin\Project.dll";
var asm = Assembly.ReflectionOnlyLoadFrom(dll);
var stack = new Stack<Data>();
stack.Push(new Data {
ReferencesPath = Array.Empty<Assembly>(),
Assembly = asm
});
List<AssemblyName> visited = new List<AssemblyName>();
while (stack.Any())
{
var current = stack.Pop();
var dependencies = current.Assembly.GetReferencedAssemblies();
visited.Add(current.Assembly.GetName());
foreach (var item in dependencies)
{
if (!visited.Any(x => x.FullName == item.FullName))
{
Assembly dependency;
try
{
dependency = Assembly.ReflectionOnlyLoad(item.FullName);
}
catch
{
var path = Path.Combine(dir, item.Name) + ".dll";
dependency = Assembly.ReflectionOnlyLoadFrom(path);
}
if (dependency.GetName().Version != item.Version)
{
; // put breakpoint here and inspect dependency
// and item when you find your dll in wrong version
// you can inspect current.ReferencesPath to see dependencies
// chain that causes the error
}
stack.Push(new Data
{
Assembly = dependency,
ReferencesPath = current.ReferencesPath.Concat(
new[] { current.Assembly }).ToArray()
});
}
}
}
class Data
{
public Assembly[] ReferencesPath { get; set; }
public Assembly Assembly { get; internal set; }
}

Assembly Loading in .NET Core

Using VS2017 RC, .NET Core
I am trying to load an assembly from a file.
The dependencies of this assembly are in the same folder.
I am using AssemblyLoadContext.Default.LoadFromAssemblyPath.
I realize LoadFromAssemblyPath exclusively loads the requested assembly, ignoring its dependencies; any attempt to iterate through the assembly types fails with a System.Reflection.ReflectionTypeLoadException.
LoaderExceptions contains a list of System.IO.FileNotFoundException.
I'm curious as to why this is the case, since all the required files are in the same folder.
I also tried to load all *.dll files in a folder, but some surprisingly fail with a System.IO.FileLoadException.
What am I doing wrong?
Edit: I wouldn't want to rely on the .deps file (thus ruling out DependencyContext). Is it possible?
Well what works for me is to register a handle with the Resolving event and load required assemblies on demand when LoadFromAssemblyPath needs dependencies. Be aware that this my solution from hours of trial and error, so it might not be the most ideal way. It works for me by now though. Here's my code:
AssemblyLoadContext.Default.Resolving += (context, name) =>
{
// avoid loading *.resources dlls, because of: https://github.com/dotnet/coreclr/issues/8416
if (name.Name.EndsWith("resources"))
{
return null;
}
var dependencies = DependencyContext.Default.RuntimeLibraries;
foreach (var library in dependencies)
{
if (IsCandidateLibrary(library, name))
{
return context.LoadFromAssemblyName(new AssemblyName(library.Name));
}
}
var foundDlls = Directory.GetFileSystemEntries(new FileInfo(<YOUR_PATH_HERE>).FullName, name.Name + ".dll", SearchOption.AllDirectories);
if (foundDlls.Any())
{
return context.LoadFromAssemblyPath(foundDlls[0]);
}
return context.LoadFromAssemblyName(name);
};
}
private static bool IsCandidateLibrary(RuntimeLibrary library, AssemblyName assemblyName)
{
return (library.Name == (assemblyName.Name))
|| (library.Dependencies.Any(d => d.Name.StartsWith(assemblyName.Name)));
}
The IsCandidateLibrary() bit originates from there:
http://www.michael-whelan.net/replacing-appdomain-in-dotnet-core/
I think you could omit this and the whole DependencyContext part, but it acts as a cache and avoids reloading the same assemblies over and over again. So i kept it.
There is a great enhancement in .Net Core 3.0+, wire AssemblyLoadContext.Default.Resolving event as given below and all dependencies will be resolved and loaded:
AssemblyLoadContext.Default.Resolving += (context, name) => {
string assemblyPath = $"{pluginFolder}\\{name.Name}.dll";
if (assemblyPath != null)
return context.LoadFromAssemblyPath(assemblyPath);
return null;
};
Remember to define the variabe pluginFolder
Solution2
You can use the AssemblyDependencyResolver class and resolve dependendencies including ones in .deps.json:
var resolver = new AssemblyDependencyResolver(pluginPath);
AssemblyLoadContext.Default.Resolving += (context, name) => {
string assemblyPath = resolver.ResolveAssemblyToPath(name);
if (assemblyPath != null)
return context.LoadFromAssemblyPath(assemblyPath);
return null;
};

Dynamically load assemblies in ASP.NET 5

I used to have some code which scanned the bin directory of my application for assemblies which weren't loaded in the AppDomain yet and loaded them. It basically looked like:
foreach (var assemblyPath in Directory.GetFiles("path\to\bin", "*.dll"))
{
var inspected = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
Assembly.Load(inspected.GetName());
}
I skipped the try/catch clauses, etc for brevity.
This allowed me to drop assemblies in the bin folder at run-time with implementations for certain interfaces and let the IoC container pick them up automatically. Now with the new Roslyn magic, there are no physical DLL's anymore when debugging. Is there any way to retrieve assembly names, project names or dependency names (in project.json) dynamically.
I guess I have to implement something like this example in the Entropy repo, but I don't know how to implement it for my scenario.
You can use the IAssemblyLoadContextAccessor interface to load ASP.NET 5 class library (.xproj) projects dynamically. The following example code works with Beta 4:
public class Startup
{
public void Configure(IApplicationBuilder app)
{
var assemblyLoadContextAccessor = app.ApplicationServices.GetService<IAssemblyLoadContextAccessor>();
var loadContext = assemblyLoadContextAccessor.Default;
var loadedAssembly = loadContext.Load("NameOfYourLibrary");
}
}
What you are looking for is ILibraryManager implementation which provides access to the complete graph of dependencies for the application. This is already flowed through the ASP.NET 5 DI system. So, you can reach out to it from there.
Sample usage can be found inside RoslynCompilationService.
I solved this issue partly using the ILibraryManager as suggested by #tugberk. I changed the approach a bit which dropped the need of scanning the bin folder for new assemblies. I just want all the loaded assemblies in the current AppDomain.
I injected an instance of the ILibraryManager interface in my type finder class and used the GetReferencingLibraries() method with the name of the core assembly, which is referenced by all the other assemblies in the application.
A sample implementation can be found here, where this is the important part:
public IEnumerable<Assembly> GetLoadedAssemblies()
{
return _libraryManager.GetReferencingLibraries(_coreAssemblyName.Name)
.SelectMany(info => info.Assemblies)
.Select(info => Assembly.Load(new AssemblyName(info.Name)));
}
For .net core users, here is my code for loading assemblies from a specific path. I had to use directives, as it's slightly different for .Net Framework and .Net Core.
In your class header you'll need to declare the using something similar to:
#if NET46
#else
using System.Runtime.Loader;
#endif
And in your function something similar to the following:
string assemblyPath = "c:\temp\assmebly.dll";
#if NET46
Assembly assembly = Assembly.LoadFrom(assemblyPath);
#else
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromAssemblyPath(assemblyPath);
#endif
Its not ASP.NET but it can be converted easily to asp.net.
bellow if function for loading an assembly, and invoke a method inside a class on that assembly.
private static FormCustomized loadLayout(global::System.String layoutFilename, global::System.String layoutNameSpace)
{
FormCustomized mainForm = default;
Type typeMainLayout = default;
FileInfo layoutFile;
layoutFile = new FileInfo(layoutFilename);
layoutFile.Refresh();
if (!layoutFile.Exists)
{
MessageBox.Show("Layout file not found. You need to reinstall the program");
return default;
}
try
{
Assembly assemblyRaw = Assembly.LoadFrom(layoutFilename);
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromAssemblyPath(layoutFilename);
Type typeMainLayoutIni = assembly.GetType(layoutNameSpace + ".initializeLayoutClass");
Object iniClass = Activator.CreateInstance(typeMainLayoutIni, true);
MethodInfo methodInfo = typeMainLayoutIni.GetMethod("AssembliesToLoadAtStart");
enVars.assemblies = (Dictionary<string, Environment.environmentAssembliesClass>)methodInfo.Invoke(iniClass, default);
typeMainLayout = assembly.GetType(layoutNameSpace + ".mainAppLayoutForm");
mainForm = Activator.CreateInstance(typeMainLayout, enVars) as FormCustomized;
}
catch (Exception ex)
{
return default;
}
return default;
}

Using MEF to load DLLs with embedded libraries

I am currently writing an application suite with a plugin system that loads plugins at runtime using the MEF framework.
I have currently setup one of my top level WPF applications to embed it's referenced DLLs as embedded resources and load them at runtime using the method described here.
This works fine and I get my single file WPF application that runs fine.
However, another of my top level console applications uses the MEF framework to load plugins at runtime (the WPF application is fixed and includes the plugins explicitly). My plugins have several dependencies themselves on various libraries and the extensions folder that the console application loads the plugins from is littered with all the various library dlls.
I would like to embed the dependencies of each plugin within itself so that my extensions directory contains only the top level DLL files. The method that I have used above does not cater for this approach as the plugin component cannot find the required dependency as it is only the executing assembly that is being searched for these embedded resources.
My current OnResolveAssembly method looks like this:
public static Assembly OnResolveAssembly(object sender, ResolveEventArgs args)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
var assemblyName = new AssemblyName(args.Name);
string path = assemblyName.Name + ".dll";
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false)
{
path = String.Format(#"{0}\{1}", assemblyName.CultureInfo, path);
}
using (Stream stream = executingAssembly.GetManifestResourceStream(path))
{
if (stream == null)
return null;
var assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
I'm thinking that the best way to proceed would be to add in functionality to keep track of all assemblies loaded in a list and once a new assembly has been loaded in this way, recursively do the same; load any embedded DLLs within those as you go. You can then add these DLLs to the list which will act as a cache.
Is there perhaps a better way to proceed with this?
I have implemented a very similar solution to yours and it works very fine for me. As you can see I keep track of already loaded assemblies in a _references dictionary.
In my case, I do not need to "eagerly" load all embedded dependencies in any recursive way, but rather my embedded assemblies do register themselves with the application host on-demand.
public static class ApplicationHost
{
private static readonly Dictionary<string, Assembly> _references = new Dictionary<string, Assembly>();
[STAThread]
private static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) => _references.ContainsKey(args.Name) ? _references[args.Name] : null;
RegisterAssemblyAndEmbeddedDependencies();
// continue application bootstrapping...
}
public static void RegisterAssemblyAndEmbeddedDependencies()
{
var assembly = Assembly.GetCallingAssembly();
_references[assembly.FullName] = assembly;
foreach (var resourceName in assembly.GetManifestResourceNames())
{
using (var resourceStream = assembly.GetManifestResourceStream(resourceName))
{
var rawAssembly = new byte[resourceStream.Length];
resourceStream.Read(rawAssembly, 0, rawAssembly.Length);
var reference = Assembly.Load(rawAssembly);
_references[reference.FullName] = reference;
}
}
}
}

Categories

Resources