Assembly not found at runtime when running a Roslyn script - c#

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:
If MyAssemblyLocation assembly is referencing other assemblies, how do I tell Roslyn to load them without doing an engine.AddReference("MyAssemblyLocation");
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?
Finally, is there a way to tell Roslyn to recursively load all dependencies of MyAssemblyLocation
Thank you.

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.

Related

Comparing project classes against a compiled dll using IsSubclassOf

C# DLL referencing comparing question.
I have a Multiple project solution - all are DLLs
All of the projects (except the first dll), are optional DLLS that refence the first one since these are 'addons' to the first DLL.
Lets say the first and main DLL has a class called 'Base'.
The other DLLS have a class that inherit from this one.
With that in mind, I have this code:
Assembly assembly = Assembly.LoadFrom("second.dll");
foreach (Type type in assembly.GetTypes());
{
// In the itiration of classes, the one inhereting from Base class will come,
// but the compiler won't recognize it, even if in debug they do appear to be so.
if (type.IsSubclassOf(typeof(Base))
// Successful code here
}
}
All of the dependent DLLs are Built first before passing execution to the Base DLL.
Any ideas?

Reference has not compile-time usages

I've been watching over the architecture view of our solution created by ReSharper, when I noticed some of the project references has no compile-time usages, does that mean I can change assemblies at runtime?
In simple terms, no compile-time usage means that your code will compile even if you remove the reference. You cannot directly derive anything regarding runtime from this statement. It might even be that your application runs perfectly fine if you just remove this reference. It might also be that your reference is somewhat obfuscated and the compiler doesn't know it. This could be because the reference is implementing interfaces that you compile against or you are looking for it manually at runtime (see Florians answer). You could probably also hide it with reflection if you really wanted to. But that would also need to load the assembly manually at runtime.
At compile-time, the compiler will link the new binaries to the corresponding code in the referenced assemblies. This will allow automatically loading the assembly at runtime. It will also copy const values to your assembly.
You can definitely change the referenced assembly between compile- and runtime, although you should tread very carefully. If method signatures changed, compile-time references will break.
At runtime, referenced assemblies will be loaded once you try to interact with them. Once an assembly is loaded, it cannot be unloaded directly. You can only unload AppDomains. So if you want to change assemblies at runtime, look into AppDomains.
So what could be an intended use of those non-compile-time references? The most common architecture that uses this was mentioned by Florian in the other answer: Plugins. Also other dependencies where you want to separate your code from the actual implementation via interfaces. Your project references without compile-time dependencies are then only used to deliver the implementation to the actual application. Otherwise you would need to add this to your delivery and debugging process, which can be a pain depending on your project.

How to access a referenced assembly's types?

If I reference an assembly A.dll in my project and A.dll references B.dll. My project cannot access the exported namespaces/classes of B.dll, only A.dll. Is there something I can do so I don't have to directly reference B.dll in my project in order to access its classes?
No, there is no way *) You have to reference all assemblies you want to use types for. This implies that if you use A and if you use anything from A that exposes a type from B, you must reference the B too.
quick edit: uh, ok, actually there is some way - if you don't want to use B.dll but if you wouldn't mind using a XYZ.dll instead, you could make a XYZ.dll that mimics relevant types from the B.dll, and then try to develop some ticks ased on i.e. assembly redirection.. with it you theoretically could have your app reference A.dll and XYZ.dll but in runtime it would use A.dll and B.dll. If speaking about project&compilation, there are some tricks the compiler uses (or used to use) to support cross-platform compilation (i.e. stubbing assemblies from WindowsPhone just to get references satisfied)..
*) afterthought: you could try using dynamic and its dynamic binding:
// in A.dll
public dynamic Method() {
return new ClassFromB(); // thing from B.dll
}
// in your app:
// do not ever explicitely name ANY type from:
dynamic foo = ClassFromA.Method();
dynamic result = foo.CallSomething(1,2,3); // even when getting results
dynamic other = result.Boom(foo); // and even when passing params
as long as all reference types and methods are be shadowed by dynamic, there is some chance it won't ask you to reference the B.dll, since there's no explicit type reference.. - but I actually never tried it for this effect, so I can't tell for 100% sure
however, when using dynamic all the time, you will resign from many compile-time checks and any error in parameter types or method names will be rised at runtime only - just like using reflection
This behavior is by design. Except stuff like using reflection to access any entities in your B.dll on run-time, there's no other way than referencing B.dll directly.

Ensure early loading of (some) referenced assemblies

I have a solution containing several projects.
There is one project that references all other projects, there may be references between these referenced projects as well.
Let's assume project A as the main one.
Project B has a MyType class.
Project C has an extension method for MyType.
Each assembly has an attribute. It allows me to determine if the assembly is mine or an external library (maybe it's not the best way to do it, but it works).
At some point in A I have a mechanism that uses reflection to find all extension methods of MyType. To do that I handle event AppDomain.AssemblyLoad to create a list of my loaded assemblies (marked with an attribute). Then I can scan the assemblies for proper extension methods.
Unfortunately, at this moment C is not loaded, because nothing from C has been called. Thus, the extension is not found.
I've tried using Assembly.GetExecutingAssembly().GetReferencedAssemblies(), but I think it returns only loaded assemblies, no luck here.
My final solution in A is:
public static void Main(string[] args)
{
TypeFromProjectC temp;
//further code...
}
It works because the assembly gets loaded on temp declaration, but...
I could try to load assemblies by path/name, but let's assume it's not an option. I want to create a generic solution that would not need to be altered each time I add another reference. In fact that is why I have added this attribute for my assemblies.
Something like GetReferencedAssemblies() would be great. It would be the best if I could check the assembly for my attribute before calling Load(), but it's not that necessary. (Does it even makes sense? When I think of it now, I'd guess that it must be loaded before the attributes are available, but maybe some meta information could do?)
It's the first time I'm playing with multiple assemblies with quite a complicated structure, so it's possible that I don't really have a clue how does it work and my question is plainly stupid.
I hope it's not ;]
Some notes and comments before my answer:
1) You have a reference to ClassLibrary1 and ClassLibrary2, and instantiate Class1 from ClassLibrary1. In that case compiler throws away reference to ClassLibrary2 and Assembly.GetReferencedAssemblies() returns to you actual optimized list of references. When you use any type from ClassLibrary2, complier will respect that reference also. The best way for making strong refs is:
static class StrongReferencesLoader
{
public static void Load()
{
var ref1 = typeof(Class1); // ClassLibrary1.dll
var ref2 = typeof(Class2); // ClassLibrary2.dll
}
}
This allows to reference static classes also and this allows to force loading of every strong-referenced library by calling this method.
2) You do use an AppDomain.AssemblyLoad event. Consider subscribing here from your entry point. Be careful, because if your entry point make something else this can lead to loading assembly before subscribing occurs.
class Program
{
static void Main()
{
AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
new Class1();
}
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
Console.WriteLine("! " + args.LoadedAssembly.FullName);
}
}
in this sample, you will not get an output about loading ClassLibrary1, because loading of library was performed before executing main method. Runtime loads all necessary libraries for executing specified method. If we extract this call to another method - everything become fine.
class Program
{
static void Main()
{
AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
RealMain();
}
static void RealMain()
{
new Class1();
}
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
Console.WriteLine("! " + args.LoadedAssembly.FullName);
}
}
In that case, Main is executed without problem, and subscribes immediately. After that, assembly loading performed due to call RealMain that contain a byte-code referenced to ClassLibrary1, and your event handler works.
3) Note that in any time you can list all loaded assemblies by AppDomain.CurrentDomain.GetAssemblies()
I'm not fully understand your problem. Is that only solution-wide analysis, or generic idea for future plugins, created by any one else? Solution-wide analysis can be solved in compile-time or run-time, but open plugin system is only runtime.
Run time solution
First of all, you should understand that it is impossible to detect every plugins. Let say, Class1 have extensions, written by some body for another application and installed in another directory:
some pseudo-structure:
\GAC\ClassLibrary1.dll
\ProgramFiles\MyApp\ConsoleApplication1.exe // our entry point
\ProgramFiles\SomeApp\ClassLibrary3.dll // some library from another solution, that references shared ClassLibrary1
You can detect plugins in your GAC, AppDomainBase path, RelativeSearchPath (all of that done by built-in assembly resolver), any custom hardcoded path, and paths of already loaded assemblies AppDomain.CurrentDomain.GetAssemblies().Select(x => Path.GetDirectoryName(x.Location)).Distinct();
And you can analyse that paths recursively.
But you'l never find path like \ProgramFiles\SomeApp\ClassLibrary3.dll unless you scan entire computer.
I hope, that searching in deep to already loaded assemblies location can solve your problem.
Note that you can use Mono.Cecil, but it is convenient to use Assembly.ReflectionOnlyLoad for that purpose. You can load by file name using Assembly.ReflectionOnlyLoadFrom, and you can help resolve assemblies in another location by AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve event. But you can not use that for custom attributes analysis. In that case some hack is required, like special class name (static class MySuperMark{}), which existance can be checked with easy. It is less hacky to use special interface that every plugin required to implement (but it is not so close to your case).
Compile time solution
Only for solution-wide analysis.
Since you have noted, that
I want to create a generic solution that would not need to be altered each time I add another reference.
Probably you are looking for solution-wide answer. I can suggest some metaprogramming here. Usually I'm solving such task by metacreator http://code.google.com/p/metacreator/. You can run some piece of code before compilation of ConsoleApplication1. For example, you can create a class holding strong references for every referenced dlls automatically by metacode and you can run such class for ensuring early loading of referenced assemblies. This class will actualized on every compilation automatically.
Please, describe more details, which case I can describe more deeply.
In my opinion, assembly analysis with Mono.Cecil is useful here. For example you can enumerate references (of course without ones deleted from project by compiler) without loading given assembly and any of its references by:
var assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly("protobuf-net.dll");
foreach(var module in assembly.Modules)
{
foreach(var reference in module.AssemblyReferences)
{
Console.WriteLine(reference.FullName);
}
}
Such code prints:
mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
System.Runtime.Serialization, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
However IMO better idea is to scan through directory, possibly containing plugins and check them for your attribute using Cecil and therefore without loading (because you do not know at that point whether you want to load them at all):
var assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly("protobuf-net.dll");
foreach(var attribute in assembly.CustomAttributes)
{
Console.WriteLine(attribute.AttributeType.FullName);
}
There are couple options for comparing your attribute (which is a class from loaded assembly) to Cecil's attribute description, full type name is one of them.
Assembly.GetReferencedAssemblies() gives you a very reliable list of assemblies that your code actually uses. You will have to recurse through the list so you will find all of them, not just the ones that you main .exe assembly uses.
What you'll get does not necessarily match the assemblies you added with Project + Add Reference. The compiler makes sure to only include referenced assemblies in the metadata that your code actually uses. Which is perhaps why TypeFromProjectC temp; works.
If there are other assemblies lying around that ought to be loaded but are not found by recursing through GetReferencedAssemblies() then you have to load them yourself explicitly with Assembly.Load/From(). Actually using types from such assemblies has to be done manually too, using Activator.CreateInstance() to create an object and Reflection to call methods etcetera.
There's no evidence in your question that you actually intend to use Reflection, especially because you mention extension methods. An extension method that actually gets used in your code will always end up producing an assembly that's discovered through ReferencedAssemblies(). It is also fairly unclear why you want to do this in the first place. The just-in-time compiler is already very good at loading assemblies on demand, doing so when it compiles code just in time. Which is almost always desirable since it turns a long startup delay you'd get by forcing the CLR to find all assemblies into a hundred needle pricks that the user won't notice. Lazy assembly loading is very much a feature, not a bug.
If you don't have a static, compile-time reference to an assembly, directly or indirectly, such an assembly is not going to get loaded and thus is invisible to your enumeration logic. It is possible, however, to dynamically load all assemblies in, say, a particular directory at run-time.
On the other hand, I don't believe there is a place for a system-global dictionary of assemblies that might have a particular custom attribute, which seems to be what you're asking for.
You are looking for your ext methods so the referenced assemblies can be found int the following locations:
the sub directories of the executing asm - simple to implement
sub directories of called asms - simple to implement using the assembly loaded event
mainfest - you have to figure this one out but I dont think it is that hard. Just parse the xml and read the elements
GAC - if you register your asms in the GAC. The solution can be found here:Extract assembly from GAC

include assemblies via code? C#

In msvc i can write
#pragma comment(lib, "my.lib");
which includes my.lib in the linking stage. In my solution i have 2 projects. One is a class project the other is my main. How do i include the reference dll in code instead of adding the reference in the project?
Contrary to popular belief, it is possible :-)
To statically link .NET assemblies check out ILMerge. It's a utility that can be used to merge multiple .NET assemblies into a single one. Be it an executable or a DLL.
You could create a batch script that packages your assemblies together as a post-build step.
Edit: One thing to note however is that this does not remove the need to reference the library. The reference is still needed in order to compile your code that is dependent the external types. Much like including header files under C(++). By default c# assemblies are independent, there is no linking involved. However the tool I mentioned above allows you to create a new assembly with the external dependencies included.
As far as I know, you can't. If you need to access type that are included in a non referenced assembly, you'll have to use Assembly.Load().
I'm afraid you can't.
You can dynamically load the assembly via Assembly.Load(...) but then you have use reflection to explicitly create each Type you need to use.
I don't think you can include a dll from code without adding a reference. What you can do however is to use reflection to load that assembly and use a type from that assembly.
Assembly.Load() will get you a handle on the assembly and then you should be able to iterate through the types in the assembly.
Managed code doesn't use a linker. The C/C++ equivalent of a reference assembly is the #include directive, you need that in C/C++ to allow the compiler to generate code for an external type. Exact same thing in C#, you can't use an external type unless the compiler has a definition for it. The reference assembly supplies that.
The equivalent of C/C++ linking is done at runtime in a managed program. The JIT compiler loads assemblies as needed to generate machine code.
One thing you can do in a C# program that you can't do in a C/C++ program is using Reflection. It allows you to invoke a constructor and call a type's methods with type and method names as strings. Start that ball rolling with Assembly.GetType() and the methods of the Type class. However, consider a plug-in model with, say, the System.AddIn namespace first.
If you want to load an assembly at runtime, you can use Assembly.LoadFrom(filePath). But that way you are not referencing the assembly, and you can't use strong typing.
For example, you can have different plugins implementing a known interface (the one which is in a separate, referenced assembly), and have them all placed in a folder. Then you can check the folder and load all implementing classes at runtime (like in this example).

Categories

Resources