I've written a plugin manager so
public class WeinCadPluginManager : NinjectModule, IEnableSerilog
{
public override void Load()
{
var codeBaseUrl = new Uri(Assembly.GetExecutingAssembly().CodeBase);
var codeBasePath = Uri.UnescapeDataString(codeBaseUrl.AbsolutePath);
var dirPath = Path.GetDirectoryName(codeBasePath);
Debug.Assert(dirPath != null, "dirPath != null");
var path = dirPath;
var types = Directory
.GetFiles(path, "*.dll")
.Select (Assembly.LoadFile)
.SelectMany (assembly =>
{
try
{
return assembly.GetExportedTypes();
}
catch (Exception e)
{
this.Serilog()
.Error
(e, "Failed to load assembly {assembly}", assembly);
return new Type[]
{
};
}
})
.Where(type=>typeof(IWeinCadPlugin).IsAssignableFrom(type))
.ToList();
foreach (var assembly in types)
{
Kernel.Bind<IWeinCadPlugin>().To(assembly).InSingletonScope();
}
}
}
Now this pretty much duplicates
Kernel.Load("*.dll")
except if there are any errors exporting the types from an assembly then Kernel.Load crashes with no error handling possible. I wouldn't want the failure to load a single plugin assembly to crash my app. Is my solution the only viable way or does Ninject have some error handling available?
I think a try { } catch { } around each Kernel.Load("specificplugin.dll") should suffice.
You would still have to find all the assemblies yourself, but would have to write less code.
var codeBaseUrl = new Uri(Assembly.GetExecutingAssembly().CodeBase);
var codeBasePath = Uri.UnescapeDataString(codeBaseUrl.AbsolutePath);
var dirPath = Path.GetDirectoryName(codeBasePath);
var dllPaths = Directory.GetFiles(dirpath, "*.dll");
foreach(string dllPath in dllPaths)
{
try
{
kernel.Load(dllPath);
}
catch (Exception e)
{
this.Serilog()
.Error(e, "Failed to load assembly {assembly}", assembly);
}
}
Drawback: ninject must be adding the binding to the kernel either on .Bind() or on .To(..), because all the rest of the fluent syntax methods are optional. So if there would be an exception in the .When(), .InScope(),.. any other optional method, you would be left with a non-complete binding and thus, most likely, a faulty software.
(However i suspect that most errors will not materialize when creating the binding, but rather when activating the binding. And you are not protected against that.)
As far as i know there is no way to remove bindings from ninject once you've added them. Except for the .Rebind() - but that is always replacing a binding with a different one.
So no, i don't think there's something like "rollback on exception".
So we must be looking for an alternative solution. There is one: child kernels. https://github.com/ninject/ninject.extensions.childkernel
Load each plugin into it's own child kernel. in case the fooplugin.dll load fails, dispose the child kernel. In case it works.. well you're good to go! :)
This also has the advantage that plugin's can't influence one another. Imagine two plugins would do IBindingRoot.Bind<string>().ToConstant("some constant") ;-)
(please note that i haven't tested whether disposing the child kernel before the parent kernel works "as expected", so you should test it out first. Simple enough, right?)
Related
I have consulted code on the website http://codeproject.com with the title "Loading Assemblies from Anywhere into a New AppDomain" of Marius Bancila, but I tested the error as in the attached picture, currently, I don't know resolve, hope you help, thank you.
Link Code
https://www.codeproject.com/Articles/453778/Loading-Assemblies-from-Anywhere-into-a-New-AppDom#_articleTop
Test
public class Program
{
[STAThread]
public static void Main()
{
var project = #"D:\Github\BeyConsPlugin\BeyConsProject\bin\x64\Debug\BeyConsRevitProject.dll";//Path to assembly
var manager = new AssemblyReflectionManager();
var success = manager.LoadAssembly(project, Guid.NewGuid().ToString());
if (success)
{
var result = manager.Reflect(project, (a) =>
{
return a.GetTypes();
});
Console.WriteLine(string.Join("\n", result.Select(x => x.Name)));
}
Console.ReadKey();
manager.UnloadAssembly(project);
}
}
Error
Assembly Resolver not Set
Marius's code has a bug in AssemblyReflectionProxy with regards that the Assembly Resolver is not set if you call LoadAssembly unlike Reflect<> which does.
Depending on how a child app domain is created, when loading assemblies it might only have access to the folder as specified during creation. If you need to assembly probe elsewhere for the assembly or its dependencies you need a Assembly Resolver. When .NET is looking for an assembly for a domain, it will call your handler as specified in the assemblie's ReflectionOnlyAssemblyResolve event. If not specified or if the resolver fails to locate the assembly, it will bubble up and throw a load fail exception.
I suggest you change the code from:
public class AssemblyReflectionProxy : MarshalByRefObject
{
private string _assemblyPath;
public void LoadAssembly(String assemblyPath)
{
try
{
_assemblyPath = assemblyPath;
Assembly.ReflectionOnlyLoadFrom(assemblyPath);
}
catch (FileNotFoundException)
{
// Continue loading assemblies even if an assembly
// cannot be loaded in the new AppDomain.
}
}
...to:
public class AssemblyReflectionProxy : MarshalByRefObject
{
private string _assemblyPath;
public void LoadAssembly(String assemblyPath)
{
try
{
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve // <---- add me
+= OnReflectionOnlyResolve;
_assemblyPath = assemblyPath;
Assembly.ReflectionOnlyLoadFrom(assemblyPath);
}
catch (FileNotFoundException)
{
// Continue loading assemblies even if an assembly
// cannot be loaded in the new AppDomain.
}
}
You can see this in Sacha's original code that on which Marius based his.
Add Provision for Resolve Paths
The other problem with the code is that both assume that when loading one assembly, any dependent assemblies are in the same folder something that may not always be the case.
Alter AssemblyReflectionProxy to include a list of paths in which to probe:
public List<string> ResolvePaths { get; set; }
Then modify OnReflectionOnlyResolve to resemble the following:
private Assembly OnReflectionOnlyResolve(ResolveEventArgs args, DirectoryInfo directory)
{
Assembly loadedAssembly =
AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies()
.FirstOrDefault(
asm => string.Equals(asm.FullName, args.Name,
StringComparison.OrdinalIgnoreCase));
if (loadedAssembly != null)
{
return loadedAssembly;
}
foreach (var tryFolder in ResolvePaths)
{
var asmName = args.Name.Split(',');
var assemblyPath = Path.Combine(tryFolder, asmName[0] + ".dll");
if (!File.Exists(assemblyPath))
return null;
return Assembly.ReflectionOnlyLoadFrom(assemblyPath);
}
}
What's in a name?
Both articles neglected to point out the fine print when using ReflectionOnlyLoad. Though Sacha did at least mention his code was for a "code generator" I can't help but wonder that both articles with their "Loading Assemblies....into a New AppDomain" are perhaps somewhat subject to interpretation.
The aim of ReflectionOnlyLoad is just that - for reflection. If you load an assembly via this method you cannot execute any code in it. Additionally and somewhat surprising at first to most assembly reflector programmers including me is that calling GetCustomAttributes will also fail (because it attempts to "instantiate" types in the assembly).
If you are writng your own plug-in system in which each plug-in has its own App Domain, the Assembly reflection and load methods are useful for different stages of the plug-in system loading pipeline:
first pass - use ReflectionOnlyLoad as a way to inspect the plug-in to see if it is valid; perhaps you want to run some security checks safe in the knowledge that none of the plug-ins code can run during this phase
second pass - after verifying the plug-in, you can safely Load/LoadFrom the assembly and execute the code
I read this article, but it doesn't help me.
My goal is to find the class that has custom attributes.
since I don't need any instance or use codes, is there a way to load DLL file and search what I want in code, without solving dependencies problem? to lookup codes.
if you going to get all app domain assemblies types you can avoid that issue by catching the ReflectionTypeLoadException:
public static class AssemblyExtension
{
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly)
{
if (assembly == null) throw new ArgumentNullException(nameof(assembly));
try
{
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e)
{
return e.Types.Where(t => t != null);
}
}
}
you can use it like so:
var types = (from domainAssembly in AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic)
from assemblyType in domainAssembly.GetLoadableTypes()
select assemblyType);
if you targeting an assembly you want to load it without loading its dependencies use one of overloads:
as documented here: How to: Load Assemblies into the Reflection-Only Context
Assembly.ReflectionOnlyLoad(/*your appropriate parameter assembly name or something else*/);
I have a simple challenge. I dynamically need to figure out all methods with a specific attribute in C#. I'm going to load the assemblies dynamically from another application and need to find out the exact methods. The assemblies look like the followings:
Base.dll:
Class Base
{
[testmethod]
public void method1()
...
}
Derived.dll:
Class Derived:Base
{
[testmethod]
public void method2()
}
Now in 3rd application I dynamically like to load the above mentioned dlls and find out testmethods.
If I load Base.dll, I need to get testmethod1. If I load Drived.dll, should I get testmethod1 and testmethod2.
I found some code online which helped me to load dlls dynamically:
List<Assembly> a = new List<Assembly>();
string bin = #"Bin-Folder";
DirectoryInfo oDirectoryInfo = new DirectoryInfo(bin);
//Check the directory exists
if (oDirectoryInfo.Exists)
{
//Foreach Assembly with dll as the extension
foreach (FileInfo oFileInfo in oDirectoryInfo.GetFiles("*.dll", SearchOption.AllDirectories))
{
Assembly tempAssembly = null;
//Before loading the assembly, check all current loaded assemblies in case talready loaded
//has already been loaded as a reference to another assembly
//Loading the assembly twice can cause major issues
foreach (Assembly loadedAssembly in AppDomain.CurrentDomain.GetAssemblies())
{
//Check the assembly is not dynamically generated as we are not interested in these
if (loadedAssembly.ManifestModule.GetType().Namespace != "System.Reflection.Emit")
{
//Get the loaded assembly filename
string sLoadedFilename =
loadedAssembly.CodeBase.Substring(loadedAssembly.CodeBase.LastIndexOf('/') + 1);
//If the filenames match, set the assembly to the one that is already loaded
if (sLoadedFilename.ToUpper() == oFileInfo.Name.ToUpper())
{
tempAssembly = loadedAssembly;
break;
}
}
}
//If the assembly is not aleady loaded, load it manually
if (tempAssembly == null)
{
tempAssembly = Assembly.LoadFrom(oFileInfo.FullName);
}
a.Add(tempAssembly);
}
The above code is working fine and I can load the DLLs. However when I use the following code to find out the right method, it doesn't return any desired results. I'm wondering which part is not correct. The following code lists about 145 methods but non of them is one which I'm looking for.
public static List<string> GetTests(Type testClass)
{
MethodInfo[] methodInfos = testClass.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);
Array.Sort(methodInfos,
delegate(MethodInfo methodInfo1, MethodInfo methodInfo2)
{ return methodInfo1.Name.CompareTo(methodInfo2.Name); });
foreach (MethodInfo mi in methodInfos)
{
foreach (var item in mi.GetCustomAttributes(false))
{
if
(item.ToString().CompareTo("Microsoft.VisualStudio.TestTools.UnitTesting.TestMethodAttribute") == 0)
result.Add(mi.Name);
}
}
return result;
}
Can any one help me with this issue?
I'm not sure why but I tried to instantiate objects from above mentioned classes (Base and Derived) and the above mentioned code returns the right results. However as mentioned above if I don't have an object from base and derived classes and try to figure out the methods based on the types, it doesn't return the desired results.
Thx
The simplest approach is to use MethodInfo.IsDefined - quite possibly with LINQ as well:
var testMethods = from assembly in assemblies
from type in assembly.GetTypes()
from method in type.GetMethods()
where method.IsDefined(typeof(TestMethodAttribute))
select method;
foreach (var method in testMethods)
{
Console.WriteLine(method);
}
(I'd do all the sorting with LINQ as well. Obviously you can tune the GetMethods call etc to only return instance methods, for example.)
It's not entirely clear to me why your current approach doesn't work or why it does work when you've created instances - but without a short but complete example demonstrating the problem, it would be hard to diagnose it further. I'd definitely start with the code above :)
I am working on code for an ASP.NET MVC application that will do the following when the application is started:
Load all assemblies in the application bin directory
Get all types from each assembly that are derived from an interface (ITask)
Invoke the Execute() method on each type
Here is the current idea I came up with. This method will get called in OnApplicationStarted():
private void ExecuteTasks()
{
List<ITask> startupTasks = new List<ITask>();
Assembly asm = this.ExecutingAssembly;
// get path of executing (bin) folder
string codeBase = this.ExecutingAssembly.CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
string bin = Path.GetDirectoryName(path);
string[] assemblies = Directory.GetFiles(bin, "*.dll");
foreach (String file in assemblies)
{
try
{
if (File.Exists(file))
{
// load the assembly
asm = Assembly.LoadFrom(file);
// get all types from the assembly that inherit ITask
var query = from t in asm.GetTypes()
where t.IsClass &&
t.GetInterface(typeof(ITask).FullName) != null
select t;
// add types to list of startup tasks
foreach (Type type in query)
{
startupTasks.Add((ITask)Activator.CreateInstance(type));
}
}
}
catch (Exception ex)
{
Exceptions.LogException(ex);
}
}
// execute each startup task
foreach (ITask task in startupTasks)
{
task.Execute();
}
}
My question: is there a better way to do any of these steps? The method to get the bin directory was taken from this answer: https://stackoverflow.com/a/283917/213159. It seems like a lot of work to do something simple, but I couldn't figure out an easier approach.
Also, is using System.Activator to create instances and then subsequently invoke the Execute() method on each instance the most efficient way to perform that step?
You may be able to clean the code up, but without any extension libraries the code doesn't get all that much shorter.
About performance, I'd not worry too much about optimizing OnApplicationStarted tasks in particular, it's hopefully not called all that often and shouldn't impact your site once it's up and running.
I've searched high and low, but I can't come up with a solution for this.
I need to get all the interface types from an assembly with code like this:
IEnumerable<Type> interfaces = _assembly.GetTypes().Where(x => x.IsInterface);
The problem is, for certain assemblies I run into the following error:
Unable to load one or more of the
requested types. Retrieve the
LoaderExceptions property for more
information.
I'm completely clear on why this happens (dependant assemblies are not loaded), and how it can be worked around if I want to troubleshoot a specific assembly. In my case, I don't know the assembly up front (the user will select it).
What I'd like to know is whether there is any way to allow the code to continue past any types that can't be retrieved, and still pull the ones that don't fail.
It looks like this is a vexing API for which the exception cannot be avoided (as far as I know).
Try something like this:
IEnumerable<Type> interfaces;
try
{
interfaces = _assembly.GetTypes().Where(x => x.IsInterface);
}
catch (ReflectionTypeLoadException ex)
{
interfaces = ex.Types.Where(x => x != null && x.IsInterface);
}
UPDATE
Actually, this is so ugly that I would probably hide it somewhere. This must be a very old part of the .NET Framework, because I’m pretty sure they wouldn't design it like this nowadays.
private static IEnumerable<Type> GetTypesSafely(Assembly assembly)
{
try
{
return assembly.GetTypes();
}
catch(ReflectionTypeLoadException ex)
{
return ex.Types.Where(x => x != null);
}
}
...
IEnumberable<Type> interfaces = GetTypesSafely(_assembly).Where(x => x.IsInterface);
If you think you'll be doing this very often, then an extension method might be more appropriate.
Handle the exception in another method that takes the place of the lambda expression and catches the exception. You could also have the exceptions accumulate in some global object for inspection later.
IEnumerable<Type> interfaces = _assembly.GetTypes().Where(IsInterface);
List<string> Messages = new List<string>();
private bool IsInterface(Type x)
{
try { return x.IsInterface; }
catch (Exception e)
{
Messages.Add(e.Message);
}
return false;
}