Reputation: 3155
We all know that the assembly can be queried for attributes using the GetCustomAttributes method. I want to use this to identify an extension module for my application. However, to avoid loading every assembly I prefer a defensive approach:
using Assembly.ReflectionOnlyLoadFrom to get more details about an assembly (has it my ModuleAttribute?)
if the ModuleAttribute is found, I will finally load it using Assembly.LoadFrom
Unfortunately it seems that there is no way to get the attributes from an assembly, that is loaded into the reflection-only context:
myAssembly.GetCustomAttributes(typeof(ModuleAttribute), false)
fails with an InvalidOperationException
It is illegal to reflect on the custom attributes of a Type loaded via ReflectionOnlyGetType
and
CustomAttributeData.GetCustomAttributes(myAssembly)
fails with ReflectionTypeLoadException
because of dependant assemblies not being loaded.
So how to get the attributes without
?
Upvotes: 9
Views: 12778
Reputation:
Really old question, but this has been possible since .NET 2.0. The problem is that everything loaded into the reflection-only load context is implemented so that you cannot accidentally trigger loading the assembly into the current AppDomain. Reflecting over the custom attribute types defined in the assembly is one of these.
You would need to use CustomAttributeData
to view these custom attributes.
var assy = Assembly.ReflectionOnlyLoadFrom("MuhPlugin.dll");
// gets assembly-level attributes, but you get the idea
var attrs = CustomAttributeData.GetCustomAttributes(assy);
CustomAttributeData
instances contain information about the attribute, including its Type, constructor parameters and named parameters. Note that the Types in the object graph are reflection-only, so if you try to do things with them that would trigger loading an assembly it'll throw the familiar exception.
You can use this metadata about your metadata to determine if the reflected assembly qualifies for whatever your needs are.
Upvotes: 3
Reputation: 442
If you are remoting or using a proxy configuration, you will have something similar to this:
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) => OnReflectionOnlyResolve(e, directory);
However, if you are getting an exception across the domain, it probably looks like this:
{System.InvalidOperationException: It is illegal to reflect on the custom attributes of a Type loaded via ReflectionOnlyGetType (see Assembly.ReflectionOnly) -- use CustomAttributeData instead.
Here is the code that will trigger that off the 1st line.
object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyTitleAttribute), false);
return attributes.Length == 0 ? "" : ((AssemblyTitleAttribute) attributes[0]).Title;
You will need to change it to use CustomAttributeData like this:
foreach (CustomAttributeData cad in assembly.GetCustomAttributesData().Where(a => a.AttributeType == typeof (AssemblyTitleAttribute)))
return cad.ConstructorArguments.FirstOrDefault().Value as String;
return String.Empty;
Upvotes: 2
Reputation: 937
It is possible to load (reflection only) assemblies with dependencies.
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve +=
(s, e) => Assembly.ReflectionOnlyLoad(e.Name);
would fix failures when loading, assuming they are all in the same folder.
Upvotes: 5
Reputation: 3155
After checking all answers and doing some more research, it seems that there is simply no way to do what I want: check if an assembly is a valid extension module before loading it into the application domain.
Either I have to load the assembly that should be inspected into another application domain, inspect it there and when it succeeds load it again into my current application domain or I need to store metadata of the assembly outside the assembly itself and trust this metadata. Option one is not possible because of architectural restrictions, option two just shifts the problem but not solves it.
Probably the best alternative is to use the Managed Extensibility Framework, but this is unfortunately not that easy in the current setup.
I end up with trusting that there is nothing "bad" in the modules directory and loading everything (with some checks for not exceeding a maximum file size and not being loaded already).
Nevertheless, thank you for your ideas.
Upvotes: 4
Reputation: 2879
Have you looked at the microsoft AddIn Framework.
It allows for loading modules (AddIns) in the different app domains and processes, and querying by interface on specific attribute data.
It might not be what you want, but could be worth a look.
http://msdn.microsoft.com/en-us/library/bb384200.aspx
string[] warnings = AddInStore.Update(Environment.CurrentDirectory);
Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(YourType), Environment.CurrentDirectory)
AddInEnvironment addInEnvironment = new AddInEnvironment(AppDomain.CurrentDomain);
YourType module = tokens[0].Activate<YourType>(addInEnvironment);
And with a bit of LINQ you can query it
AddInToken myToken = tokens.FirstOrDefault(currentAddInToken => currentAddInToken.Name.Equals(moduleName))
Upvotes: 2
Reputation: 30013
If I were you I'd cache assembly metadata in, for instance, XML files. Using this approach you completely eliminate a need to load anything. And you can store even information, to calculate which you need to perform a time-consuming operation.
You can have XML files pregenerated or generated on-the-fly by scanning all existing assemblies.
Upvotes: 1