Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assembly not found at runtime when running a Roslyn script

Tags:

c#

roslyn

I am embedding a script engine based on Roslyn into my application and started facing some issues. When loading a reference into my engine, I do the following

var engine = new Roslyn.Scripting.CSharp.ScriptEngine();
engine.AddReference("MyAssemblyLocation");

Questions:

  1. If MyAssemblyLocation assembly is referencing other assemblies, how do I tell Roslyn to load them without doing an engine.AddReference("MyAssemblyLocation");
  2. The host application in which the script is supposed to be running has already loaded some assemblies I would like to use in the script, is there a way for Roslyn to hook itself into the already loaded dlls in memory and not load new instances?
  3. Finally, is there a way to tell Roslyn to recursively load all dependencies of MyAssemblyLocation

Thank you.

like image 626
GETah Avatar asked Nov 18 '12 08:11

GETah


1 Answers

1) Short answer: you need to AddReference on all assemblies whose types are you going to use in the script.

Details: The C# compiler doesn't guess where the dependent files are. An assembly file doesn't specify full paths to all of its dependencies. It only contains their names and that's generally not enough for the compiler to be able to locate the assembly file.

More details: There are two kinds of dependencies. I'll call them compile time dependencies (a) and runtime dependencies (b).

(a) Suppose you have "public class A : B {}" in assembly A.dll, "public class B { }" in assembly B.dll, and your script is using A, say you create a new instance: "new A()". The compiler requires you to add references to both A.dll and B.dll in this case, since A derives from B and the compiler needs to analyze the inheritance hierarchy of each type you use. So B.dll is a compile-time dependency of your script - the compiler needs it in order to analyze your script properly.

(b) Consider another example: "public class A { public object M() { return new B(); } }" in A.dll and the same B.dll as above. Now when compiling "new A().M()" the compiler doesn't need to know about B, since the reference to B only occurs in the body of method M and the compiler doesn't analyze bodies of imported methods. In this case it suffices to add a reference to A.dll in order to compile the script. When the script executes it calls method M. At that point the CLR loads assembly B. This gets a bit complex, so I'll skip details but in common scenarios we'll be able to locate the assembly for you, so you don't need to add the reference explicitly.

2) I'd recommend using the overload of AddReference that takes Assembly object. Something like: engine.AddReference(typeof(SomeTypeInAssemblyFoo).Assembly) to load assembly Foo that contains type SomeTypeInAssemblyFoo.

3) Not a straightforward one. You can enumerate all references using Reflection or Roslyn APIs and load each one of them.

Feel free to ask further questions if the explanation is not clear or you wish to know more details.

like image 184
Tomas Matousek Avatar answered Nov 08 '22 18:11

Tomas Matousek