How to reference dynamically loaded assembly from runtime compiled assembly? - c#

I have runtime-compiled assembly that references another assembly (DynLoadedAssembly) that is located in specific path and loaded dynamically. When I execute runtime-compiled assembly, I get an error: System.IO.FileNotFoundException Could not load file or assembly 'DynLoadedAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies.
Check out code:
public string Run()
{
var text = #"
using DynLoadedAssembly;
public class DynClass
{
public static string Evaluate()
{
return new SomeClass().Do();
}
}";
var dynAssemblyPath = #"c:\path\to\DynLoadedAssembly.dll";
Assembly.LoadFile(dynAssemblyPath);
var tree = SyntaxFactory.ParseSyntaxTree(text);
var compilation = CSharpCompilation.Create(
"calc",
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
syntaxTrees: new[] { tree },
references: new[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(dynAssemblyPath)
}
);
Assembly compiledAssembly;
using (var stream = new MemoryStream())
{
var compileResult = compilation.Emit(stream);
compiledAssembly = Assembly.Load(stream.GetBuffer());
}
Type dynClass = compiledAssembly.GetType("DynClass");
MethodInfo evaluate = dynClass.GetMethod("Evaluate");
return evaluate.Invoke(null, null).ToString();
}
Here SomeClass is defined in DynLoadedAssembly that loaded dymanically from dynAssemblyPath.
If I place DynLoadedAssembly into execution assembly folder - then it works fine.
Other workarounds are:
specify probing (https://msdn.microsoft.com/en-us/library/823z9h8w(v=vs.110).aspx) element in App.config
use AssemblyResolve event handler
But I want to load referenced assembly dynamically. So my questions are:
Why "Step 2: Checking for Previously Referenced Assemblies" (https://msdn.microsoft.com/en-us/library/yx7xezcf(v=vs.110).aspx#step2) failed? Why execuded code do not see DynLoadedAssembly library in current domain?
How to make my compiled assembly find previosly loaded assembly in current domain?

Related

GetTypes of a dinamically compiled class could not load assembly

I'm trying to dynamically compile a .cs file. This .cs file has some custom .dll references that I also need to reference dynamically.
All works good, but when I try to get all the properties of the .cs file by reflection, I obtain
{"Could not load file or assembly 'CUSTOMDLL, Version=3.2.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.":"CUSTOMDLL, Version=3.2.0.0, Culture=neutral, PublicKeyToken=null"}
I tried to import those dll through
Assembly.LoadFile(dllPath)
but seems not working.
My code is currently:
Dictionary<string, string> providerOptions = new Dictionary<string, string>
{
{"CompilerVersion", "v4.0"}
};
CSharpCodeProvider provider = new CSharpCodeProvider(providerOptions);
CompilerParameters compilerParams = new CompilerParameters
{
GenerateInMemory = true,
GenerateExecutable = false,
ReferencedAssemblies = { "System.dll",
"System.Data.dll",
"mscorlib.dll",
}
};
string referencesPath = PATH CONTAINING ALL .DLL NEEDED TO COMPILE;
foreach(string filePath in Directory.GetFiles(referencesPath))
{
compilerParams.ReferencedAssemblies.Add(filePath);
Assembly.LoadFile(filePath);
}
CompilerResults results = provider.CompileAssemblyFromSource(compilerParams, SOURCE CODE OF .CS CLASS);
Assembly assembly = results.CompiledAssembly;
assembly.GetTypes(); // This cannot resolve all types
There is any way to "import" those dll in order to get the GetTypes() working?

How to use CSharpScript with 'globals' type from a DLL which is dynamically loaded into custom collectible AssemblyLoadContext

Script evaluation code snippet:
using (var loader = new InteractiveAssemblyLoader())
{
var script = CSharpScript.Create<TDataType>(
code: expr,
options: ScriptOptions.Default.AddImports(globals.GetType().Namespace)
.AddReferences(refAssemblies),
globalsType: globals.GetType(),
assemblyLoader: loader);
return script.RunAsync(globals).Result.ReturnValue;
}
The code-analysis assemblies and the globals-type assembly are loaded into the same Collectible AssemblyLoadContext...
Also tried adding references/imports explicitly through the ScriptOptions but get the same following error:
Inner Exception 1:
InvalidCastException: [A]TestLibrary.TestClass cannot be cast to [B]TestLibrary.TestClass. Type A originates from 'TestLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location '...\TestLibrary.dll'. Type B originates from 'TestLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location '...\TestLibrary.dll'.
It seems the assembly loader is loading twice TestLibrary in your dynamic code, you can avoid that resolving manually the assembly (beware that TestLibrary must be the same version in both projects)
//Somewhere in your startup code:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
static Assembly MyResolveEventHandler(object sender, AssemblyLoadEventArgs args)
{
//First time it will be null and the resolver will load it from the default path, the succesive requests to load the library will return the already loaded assembly.
if(args.Name == "TestLibrary")
return AppDomain.CurrentDomain.GetAssemblies().Where(a => a.Name == args.Name).FirstOrDefault();
return null;
}

.NET Core Assembly.LoadFile at PostBuild event

I need generate typescript files from some of my C# classes after build.
I created dotnet cli tool and added post-build event
dotnet tsgenerator "$(TargetPath)"
where $(TargetPath) is macros pointing, for example, D:\Test\bin\Release\netcoreapp2.0\my.dll
Next, i tried to load assembly next way:
public static void Main(string[] args)
{
var dllPath = args[0]; // "D:\Test\bin\Release\netcoreapp2.0\my.dll"
var assembly = Assembly.LoadFile(dllPath);
var types = assembly.GetExportedTypes(); // Throws exception
}
But i got ReflectionTypeLoadException that says Could not load file or assembly for some references assemblies (for example, Microsoft.AspNetCore.Antiforgery).
How i can load assembly for .NET Core applications?
I'm found solution at github issue. Message by amits1995 and angelcalvasp.
I'm added <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> to my csproj and using this code to load assembly:
public static class AssemblyLoader
{
public static Assembly LoadFromAssemblyPath(string assemblyFullPath)
{
var fileNameWithOutExtension = Path.GetFileNameWithoutExtension(assemblyFullPath);
var fileName = Path.GetFileName(assemblyFullPath);
var directory = Path.GetDirectoryName(assemblyFullPath);
var inCompileLibraries = DependencyContext.Default.CompileLibraries.Any(l => l.Name.Equals(fileNameWithOutExtension, StringComparison.OrdinalIgnoreCase));
var inRuntimeLibraries = DependencyContext.Default.RuntimeLibraries.Any(l => l.Name.Equals(fileNameWithOutExtension, StringComparison.OrdinalIgnoreCase));
var assembly = (inCompileLibraries || inRuntimeLibraries)
? Assembly.Load(new AssemblyName(fileNameWithOutExtension))
: AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyFullPath);
if (assembly != null)
LoadReferencedAssemblies(assembly, fileName, directory);
return assembly;
}
private static void LoadReferencedAssemblies(Assembly assembly, string fileName, string directory)
{
var filesInDirectory = Directory.GetFiles(directory).Where(x => x != fileName).Select(x => Path.GetFileNameWithoutExtension(x)).ToList();
var references = assembly.GetReferencedAssemblies();
foreach (var reference in references)
{
if (filesInDirectory.Contains(reference.Name))
{
var loadFileName = reference.Name + ".dll";
var path = Path.Combine(directory, loadFileName);
var loadedAssembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
if (loadedAssembly != null)
LoadReferencedAssemblies(loadedAssembly, loadFileName, directory);
}
}
}
}
Usage:
public static void Main(string[] args)
{
var dllPath = args[0]; // "D:\Test\bin\Release\netcoreapp2.0\my.dll"
var assembly = AssemblyLoader.LoadFromAssemblyPath(dllPath);
var types = assembly.GetExportedTypes(); // No exceptions
}
Well you are able to load assembly but GetTypes() and GetExportedTypes() depend on the public classes within that assembly, if they have external references you get this exception.
Answer:
This means the Types of that assembly depend on other assembly which the current .NetCore does not have access at the run time because it can not connect to other dependent assemblies
Solution:
Get dependencies of the DLL assemblies and compile all of them, then load each assembly iteratively to get all ExportedTypes (i.e publicly visible Types)
Code:
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.Extensions.DependencyModel;// add this nuget
class Program
{
static void Main(string[] args)
{
var asl = new AssemblyLoader();
var asm = asl.LoadFromAssemblyPath(#"C:\temp\Microsoft.AspNetCore.Antiforgery.dll");
try
{
var y = asm.GetExportedTypes();
Console.WriteLine(y);
}
catch (Exception e1)
{
Console.WriteLine("Got exception at first attempt of GetExportedTypes ");
Console.WriteLine("\t*********" + e1.Message + "**************");
var deped = asl.CallForDependency(asm.GetName());
try
{
Console.WriteLine("\n" + deped.ToString());
Console.WriteLine("----------All Exported Types------------");
foreach (var item in deped.ExportedTypes)
{
Console.WriteLine(item);
}
}
catch (Exception e2)
{
Console.WriteLine("Got exception at second attempt of GetExportedTypes ");
Console.WriteLine("\t*********" + e2.Message + "**************");
}
}
Console.ReadLine();
}
}
public class AssemblyLoader :AssemblyLoadContext
{
protected override Assembly Load(AssemblyName assemblyName)
{
var deps = DependencyContext.Default;
var res = deps.CompileLibraries.Where(d => d.Name.Contains(assemblyName.Name)).ToList();
var assembly = Assembly.Load(new AssemblyName(res.First().Name));
return assembly;
}
public Assembly CallForDependency(AssemblyName assemblyName)
{
return this.Load(assemblyName);
}
}
Output :
Got exception at first attempt of GetExportedTypes
*********Could not load file or assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'. An operation is not legal in the current state. (Exception from HRESULT: 0x80131509)**************
Microsoft.AspNetCore.Antiforgery, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
----------All Exported Types------------
Microsoft.Extensions.DependencyInjection.AntiforgeryServiceCollectionExtensions
Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions
Microsoft.AspNetCore.Antiforgery.AntiforgeryTokenSet
Microsoft.AspNetCore.Antiforgery.AntiforgeryValidationException
Microsoft.AspNetCore.Antiforgery.IAntiforgery
Microsoft.AspNetCore.Antiforgery.IAntiforgeryAdditionalDataProvider
Microsoft.AspNetCore.Antiforgery.Internal.AntiforgeryFeature
Microsoft.AspNetCore.Antiforgery.Internal.AntiforgeryOptionsSetup
Microsoft.AspNetCore.Antiforgery.Internal.AntiforgerySerializationContext
Microsoft.AspNetCore.Antiforgery.Internal.AntiforgerySerializationContextPooledObjectPolicy
Microsoft.AspNetCore.Antiforgery.Internal.AntiforgeryToken
Microsoft.AspNetCore.Antiforgery.Internal.BinaryBlob
Microsoft.AspNetCore.Antiforgery.Internal.CryptographyAlgorithms
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryAdditionalDataProvider
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenGenerator
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenSerializer
Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgeryTokenStore
Microsoft.AspNetCore.Antiforgery.Internal.DefaultClaimUidExtractor
Microsoft.AspNetCore.Antiforgery.Internal.IAntiforgeryFeature
Microsoft.AspNetCore.Antiforgery.Internal.IAntiforgeryTokenGenerator
Microsoft.AspNetCore.Antiforgery.Internal.IAntiforgeryTokenSerializer
Microsoft.AspNetCore.Antiforgery.Internal.IAntiforgeryTokenStore
Microsoft.AspNetCore.Antiforgery.Internal.IClaimUidExtractor
Reference and explanation on ReflectionTypeLoadException:
Assembly.GetTypes Method ()
ReflectionTypeLoadException
The assembly contains one or more types that cannot be loaded. The
array returned by the Types property of this exception contains a Type
object for each type that was loaded and null for each type that could
not be loaded, while the LoaderExceptions property contains an
exception for each type that could not be loaded.
Remarks
The returned array includes nested types.
If the GetTypes method is called on an assembly and a type in that
assembly is dependent on a type in an assembly that has not been
loaded (for example, if it derives from a type in the second
assembly), a ReflectionTypeLoadException is thrown. For example, this
can happen if the first assembly was loaded with the
ReflectionOnlyLoad or ReflectionOnlyLoadFrom methods, and the second
assembly was not loaded. It can also happen with assemblies loaded
using the Load and LoadFile methods if the second assembly cannot be
located when the GetTypes method is called.
Note
If a type has been forwarded to another assembly, it is not included
in the returned array. For information on type forwarding, see Type
Forwarding in the Common Language Runtime.
Linked :
How to load assemblies located in a folder in .net core console app
How to dynamically load assemblies in dotnet core
Try the LoadFrom method for loading in the assembly, rather than LoadFile:
public static void Main(string[] args)
{
var dllPath = args[0]; // "D:\Test\bin\Release\netcoreapp2.0\my.dll"
var assembly = Assembly.LoadFrom(dllPath);
var types = assembly.GetExportedTypes(); // Throws exception
}
You will also need to add the same references that are in the ddl file to your current project, so that the types are defined.

ModuleInit Fody Initialize not called

I've a WPF application that loads 3 different assembly which containts application extensions. I've added to the 3 projects the ModuleInit.Fody and for now just put a Console.WriteLine("module")
I load the assemblies with this piece of code
var files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "MyApplication.Modules*.dll");
List<Assembly> assemblies = new List<Assembly>();
foreach (var file in files)
{
AssemblyName assamblyName = AssemblyName.GetAssemblyName(file);
var assembly = Assembly.Load(assamblyName); //this throw an exception
// AssemblyName name = new AssemblyName()
//var assembly = Assembly.LoadFrom(file);
assembly.GetTypes().First();
assemblies.Add(assembly);
}
What am I doing wrong?
Thanks
UPDATE1 you can find a demo here
A module initializer is run the first time any type from that assembly is used. NOT when the module is loaded

Could not load file or assembly or one of its dependencies. The system cannot find the file specified

I have a code like this
public static Type ToType(XmlSerializableType xmlSerializableType)
{
string func = "XmlSerialzationType.ToType";
Type type = null;
if (xmlSerializableType != null && xmlSerializableType.Name != string.Empty)
{
type = Type.GetType(xmlSerializableType.Name);
if (type == null)
{
// May be a user defined class
try
{
Assembly assembly = Assembly.Load(xmlSerializableType.AssemblyName);
type = assembly.GetType(xmlSerializableType.Name);
}
catch (Exception ex)
{
TestDebug.DebugTraceSevere(func, "Exception " + ex.ToString());
}
}
}
return type;
}
I have a base class named "leaf" and a userdefinedclass named "roundedtree"
when 'xmlSerializableType.Name' becomes userdefined class '_rounded_tree', first time i am getting value for 'assembly as _rounded_treeGOLD, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' and so for 'type as {Name = "_rounded_tree" FullName = "_rounded_tree"}'. But after saving if i restart my application i cannot load value for 'assembly' getting exception 'Could not load file or assembly '_rounded_treeGOLD, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.":"_rounded_treeGOLD, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' and return type becomes null this should not happen
For baseclass "leaf" no issuses i will get xmlSerializableType.Name as " Root.Systemmodel.leaf" and 'type' becomes {Name = "leaf" FullName = "Root.Systemmodel.leaf"} assembly will be Root.Systemmodel, Version=8.0.7.0, Culture=neutral, PublicKeyToken=83bd062a94e26d58
What should i do in these circumstances
This is a bit of code which will generate assembly for userdefined class
public Type CreateType()
{
string func = "ManagedClass.CreateType";
// Create instances of AssemblyBuilder and ModuleBuilder for the new Type
AppDomain myDomain = Thread.GetDomain();
AssemblyName myAsmName = new AssemblyName();
// Create the assembly name by appending the machine name to the typename.
myAsmName.Name = this.TypeName + Environment.MachineName;
// Define assembly that can be executed but not saved
this.UserClassAssemblyBuilder = myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.Run);
// Create dynamic module with symbol information
this.UserClassModuleBuilder = this.UserClassAssemblyBuilder.DefineDynamicModule("userdefinedmodule", true);
UPDATE
probably my assembly is creating for userdefined class but not saving that may be the reason i am not facing any issue first time, once i close the application i will lose that one see my code
// Define assembly that can be executed but not saved
this.UserClassAssemblyBuilder = myDomain.DefineDynamicAssembly(myAsmName,
AssemblyBuilderAccess.Run);
how to overcome this situation
UPDATE
Here my database is xml files. When i checked for base class leaf i can see the entry is <Name>Root.Systemmodel.WindowsSystem</Name><AssemblyName>Root.Systemmodel, Version=8.0.7.0, Culture=neutral, PublicKeyToken=83bd062a94e26d58</AssemblyName> in this case if restart my application no issues, but for user defined class "roundedtree" xml entry is <Name>_rounded_tree</Name> <AssemblyName>_rounded_treeGOLD, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null</AssemblyName>
Here first time no issues, but if i restart my application i am getting exception
it happens because maybe the assembly you're going to load references to the another assembly that not exist in the same directory or system directory put all assembly in same folder
I,ve just copy paste my code but its clear
private string asmBase;
public Type[] GetAllTypeinAssembly(string assemblyName)
{
asmBase = System.IO.Path.GetDirectoryName(assemblyName);
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
System.Reflection.Assembly asm = System.Reflection.Assembly.Load(System.IO.File.ReadAllBytes(assemblyName));//domain.Load(bt) ;//
return asm.GetTypes();
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//This handler is called only when the common language runtime tries to bind to the assembly and fails.
//Retrieve the list of referenced assemblies in an array of AssemblyName.
Assembly MyAssembly, objExecutingAssemblies;
string strTempAssmbPath = "";
objExecutingAssemblies = args.RequestingAssembly;
AssemblyName[] arrReferencedAssmbNames = objExecutingAssemblies.GetReferencedAssemblies();
//Loop through the array of referenced assembly names.
foreach (AssemblyName strAssmbName in arrReferencedAssmbNames)
{
//Check for the assembly names that have raised the "AssemblyResolve" event.
if (strAssmbName.FullName.Substring(0, strAssmbName.FullName.IndexOf(",")) == args.Name.Substring(0, args.Name.IndexOf(",")))
{
//Build the path of the assembly from where it has to be loaded.
strTempAssmbPath = asmBase + "\\" + args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
break;
}
}
//Load the assembly from the specified path.
MyAssembly = Assembly.LoadFrom(strTempAssmbPath);
//Return the loaded assembly.
return MyAssembly;
}
I had the same issue, if you are working on SharePoint, sometime the references might not get bundled in WSP so, while deploying, these referenced dlls will not be deployed. Resolution is to manually these from the GAC or force these into WSPs or copy them to the local bin. The last one solved for me.

Categories

Resources