Ensure early loading of (some) referenced assemblies - c#

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

Related

ResolveEventHandler only called once, succeed or fail

I am working on a solution that has a set up like this: We have assembly A which performs some functions, and assembly B which is purely domain models that are used by assembly A (and other assemblies). Both are stored in Azure and are downloaded for use when required by the solution.
Assembly A is downloaded and loaded fine, and I have created a ResolveEventHandler to ensure that Assembly B is downloaded when required and I've added it by doing:
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyResolveFunc);
The resolver works fine but the issue is it is only called once, regardless of the outcome. If Assembly B is found then it loads it and all is well, however if Assembly B has not yet been uploaded and can't be found then it fails as expected, but it will never try again. On the next call it will simply throw an error and will not try to resolve the dependency.
I feel like I'm not understanding the way ResolveEventHandlers are meant to be used, but until assembly B is loaded correctly, should it not retry each time?
Yes, you have only got a single change resolving assemblies. This operation is quite expensive, so it won't try it over and over again.
What might help is to manually walk down the referenced assemblies and download and load those assemblies manually afterwards.

Loading Dependent Assemblies Manually

I have a project that loads multiple versions of the same assembly using either Assembly.Load or Assembly.LoadFile. I then use Assembly.CreateInstance to create a type from that specific assembly.
This works great until the type I'm creating references another dependent assembly. I need a way to intercept this specific assembly's request to load another assembly and provide it with the correct version (or, even better, probing path) to look for its dependency.
This is required because v1 and v2 of the assemblies I'm creating with Assembly.CreateInstance will often need different versions of their dependent assemblies as well, but both v1 and v2 will, by default, probe the same directories.
I've seen examples of how to do generally for an AppDomain, but I need to do this in a way that handles all resolution from a particular root assembly. Assuming I do something like:
AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs args)
{
//Use args.RequestingAssembly to determine if this is v1 or v2 based on path or whatever
//Load correct dependent assembly for args.RequestinAssembly
Console.WriteLine(args.Name);
return null;
};
This may work for dependencies immediately referenced by my target assembly, but what about the assemblies that those dependencies reference? If v1 references Depv1 which itself references DepDepv1, I'll need to be able to know this so I can ensure it can find them properly.
In that case, I supposed I would need to track this somehow. Perhaps by adding custom assembly evidence - although I haven't been able to get that to work, and there doesn't appear to be any "assembly meta data" property that I can add to at runtime.
It would be far, far easier if I could simply instruct a particular assembly to load all its dependencies from a particular directory.
Update
I managed to use the AssemblyResolve event to load the dependent assemblies based on the path of the RequestingAssembly, but it seems to be a flawed approach. It seems as though the which dependent assembly version while be used is entirely dependent on which version happens to be loaded first.
For instance:
Load v1
Load v2
Reference v1 causes load of Depv1
Reference v2 causes load of Depv2
Code in v1 uses type from Depv1 (Works)
Code in v2 uses type from Depv2 <-- fails because it gets type from Depv1!
I'm only inferring steps 5 and 6 at this point, but I do see Depv1 AND Depv2 being loaded.
As it turns out, the key to making this work is to ensure you use Assembly.LoadFile. LoadFile is the only method that will load an assembly even if it matches an assembly that .NET thinks is already loaded. I discovered this from an article on codeproject.
Since I needed to load two different assemblies that both had identical full names (i.e. "App.Test.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null") but had different contents, LoadFile was the only way to accomplish this. My initial attempts used the Load overload that accepted the type AssemblyName, but it would ignore the path defined in the AssemblyName instance and instead return the already loaded type.
To force an entire dependency graph to load from a specific location regardless of what other types are already loaded is to register for the AssemblyResolve event:
AppDomain.CurrentDomain.AssemblyResolve += ResolveDependentAssembly;
And ensure that we use LoadFile to load the dependency:
private Assembly ResolveDependentAssembly(object sender, ResolveEventArgs args)
{
var requestingAssemblyLocation = args.RequestingAssembly.Location;
if (thePathMatchesSomeRuleSoIKnowThisIsWhatIWantToIntercept)
{
var assemblyName = new AssemblyName(args.Name);
string targetPath = Path.Combine(Path.GetDirectoryName(requestingAssemblyLocation), string.Format("{0}.dll", assemblyName.Name));
assemblyName.CodeBase = targetPath; //This alone won't force the assembly to load from here!
//We have to use LoadFile here, otherwise we won't load a differing
//version, regardless of the codebase because only LoadFile
//will actually load a *new* assembly if it's at a different path
//See: http://msdn.microsoft.com/en-us/library/b61s44e8(v=vs.110).aspx
return Assembly.LoadFile(assemblyName.CodeBase);
}
return null;
}
Yes, this code assumes that if your root assembly has dependencies, that they're all located at the same path. That's a limitation, no doubt, but you could fairly easily add additional hints for non-local dependencies. This also would only be an issue if the already loaded version of those additional dependencies wouldn't work.
Lastly, none of this would be necessary if the assembly versions were properly incremented. The Load call would not treat an already loaded Depv1 as the same as a request Depv2. In my case, that wasn't something I was willing to deal with as part of my continuous integration and deployment process.
Try Assembly.LoadFrom(path); which will resolve dependencies automatically.

Assembly not found at runtime when running a Roslyn script

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.

MSBuild task to build load my assembly and build a serialised NHibernate Configuration

What I hoped would be an hour or two of work has now turned into quite the debacle with no result in sight.
Problem: I am trying to serialise a copy of my NHibernate Configuration and store it in... the project that was used to generate it!
Current manual solution: I have a project Foo that
contains a series of DomainObject.Map.cs files
References an "nhconfig.bin" file for embedding as a resource.
Contains a static method FooGenerateNHConfig that create a Configuration object and serialise it to nhconfig.bin.
To generate it, I:
(first time only: create an empty nhconfig.bin that acts as placeholder).
Build Foo.
Call a unit test that calls FooGenerateNHConfig.
Rebuild Foo.
Deploy application.
I'm trying to automate this, and thought it would be a simple matter:
Create a project Foo.BuildSupport that referenced Foo.
Define a Task X in it which would call FooGenerateNHConfig.
Setup an AfterCompile target that would call X.
Unfortunately I'm now getting 2 errors.
Firstly, a somewhat odd exception:
FluentNHibernate.Cfg.FluentConfigurationException: An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.
---> System.Runtime.Serialization.SerializationException: Unable to find assembly 'FluentNHibernate, Version=1.1.0.685, Culture=neutral, PublicKeyToken=8aa435e3cb308880'.
I think that's FluentNHibernate complaining that it can't find the FluentNHibernate assembly?
Once I run the Task from visual studio the first time, visual studio (devenv.exe) locks my Foo.BuildSupport.dll AND my Foo.exe (presumably because it just sees them as support libraries and not the actual build libraries), so I can't rebuild them. Generally, this is because vs assumes (and probably rightfully so) that the BuildSupport library is fairly static and does not rely
What's a good way to automate a process such as this? I only have some preliminary thoughts right now but the only thing I can think of is building an entirely seperate executable to be run by msbuild (saw a task to do the equivalent of this, can't find it now), or something rather involved involving a seperate appdomain and manually calling the function via reflection. But before I went further down this path, am I missing something easier and more obvious?
I had a problem very similar to this one. My problem had something to do with MSBuild deserializing Xml to classes contained in a third-party assembly. It could not resolve the assembly needed for the serialization to work, even though the assemblies were part of the project and it had no problem resolving them outside of serialization. Can't give a more technical description of the problem than that, but I found this bit of code that fixed the issue for me,
static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
Assembly ayResult = null;
string sShortAssemblyName = args.Name.Split(',')[0];
Assembly[] ayAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly ayAssembly in ayAssemblies)
{
if (sShortAssemblyName == ayAssembly.FullName.Split(',')[0])
{
ayResult = ayAssembly;
break;
}
}
return ayResult;
}
public Constructor()
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}
I think the reason this works for me is because my application resolves the Assemblies outside of serialization so I override the AssemblyResolution callout to point it back at the good Assemblies that it for some reason wont use on its own.
Hope this is useful!

Indirectly accessing an assembly through reflection

OK, I have the following bit of code:
Assembly assembly = Assembly.LoadFile("W:\\AssemblyFoo.dll");
foreach (Type type in assembly.GetExportedTypes())
{
foreach (object attribute in type.GetCustomAttributes(false)) //Exceptio on that line
{
string attributeString = attribute.ToString();
}
}
The code throws the following exception: Could not load file or assembly 'AssemblyBar, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.
The problem is that one of the attribute is in AssemblyBar, which is referenced by AssemblyFoo, but not directly by the current's project (ProjectBaz) assembly. I'd rather avoid referencing AssemblyBar directly in ProjectBaz, since AssemblyFoo is selected by the user. What's the correct way to go about this ? I'm pretty sure I'm missing something easy.
I know it's possible since Reflector does it.
Mono's Cecil
You may circumvent the problem by not loading the assemblies through the built-in reflection
facilities, using a tool like Mono.Cecil instead. I've had good experiences in applying it for analysis tasks.
From the Cecil site:
with Cecil, you can load existing
managed assemblies, browse all the
contained types, modify them on the
fly and save back to the disk the
modified assembly.
CCI Metadata
As an alternative to Cecil, you might consider CCI Metadata by Microsoft Research. I have not used that tool, so I can not comment on how it stacks up to Cecil.
Hope this helps.
You could include AssemblyBar in you bin directory so that the clr can load it.
MemberInfo.GetCustomAttributesData() was introduced in .NET 4 so that you could examine custom attributes without having to instantiate them.
UPDATE: Taking closer look at MemberInfo.GetCustomAttributesData(), although it doesn't instantiate the attributes, it still need to load the assembly. My apologies.
Another possible route to investigate would be Mono Cecil. You can load the Mono.Cecil assembly and use it with the .NET Framework, not just Mono. It is much more powerful than System.Reflection and Reflection.Emit. According to its homepage:
Cecil does not need to load the assembly or have compatible assemblies to introspect the images.
This sounds exactly like what you're trying to do.

Categories

Resources