Console application wont load assembly by name - c#

I have a console application, which references some of my other projects in the solution. On startup, I need to do some reflection work and get the assemblies by name. Apparently, this does not work as expected.
I have a project (assembly) called MyApplication.Domain which I from my .NET MVC project can get by calling GetAssembly("MyApplication.Domain") using this method:
public static Assembly GetAssembly(string name)
{
return AppDomain.CurrentDomain.GetAssemblies().SingleOrDefault(assembly => assembly.GetName().Name == name);
}
But running this on my console application, just returns null. The assembly is not even in the AppDomain.CurrentDomain.GetAssemblies() list.
Then if I try calling this method, using a class in the MyApplication.Domain assembly, I get the assembly back just like I wish to do using the other method?
public static Assembly[] GetAssembliesOf<T>() where T : class
{
var assemblies = from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
where typeof (T).IsAssignableFrom(type)
where !type.IsAbstract
select assembly;
return assemblies.ToArray();
}
// Usage: GetAssemblyOf<MyClassInDomainAssembly>();
Things I have checked:
The project or assembly is referenced
It is set to Copy Local
The build is running with Any CPU like the assembly
The target framework the same for all projects and assemblies (4.5.1)
Is there some funky stuff I need to know about loading assemblies in console application?
As a side note the console application is actually a Self-Host Web API 2.

Your observations are correct and expected. .Net is lazy and the collection in question will be filled as your assemblies get loaded and they are loaded only when they are used. The fact that you can observe this in console application and not in mvc is just because your console application did less before you executed that line.
More over when you have reference to an assembly finding it by name is just pointless as you can do typeof(AnyTypeFromThatAssembly).Assembly. And if you really want to load an assembly by name you can executed Assembly.Load but this requires fully qualified assembly name which you can get again by typeof(AnyTypeFromThatAssembly).Assembly.FullName.ToString().

Related

How to get all the referenced assemblies using reflection ? (.net4)

I've made a piece of code that gives me the referenced assemblies of one assembly (in .net5) and it was working greatly, it gave me for exemple if I use the File.WriteAllText method the assembly "System.IO.FileSystem" (this is by using the Assembly.GetReferencedAssemblies method).
But now I need to get this code to work on .NET4 (for Unity Engine). But i've seen that the myAssembly.GetReferencedAssemblies do not give the same output as in .NET5.
It now gives me only: myAssembly.dll and mscorlib.dll
And I cannot make it work to give me like previously all the referenced assemblies (for exemple System or System.IO ...)
Here's a simple example:
using System.IO;
public class Plugin {
static Plugin() {
// Just use the File class to keep the System.IO assembly reference
File.WriteAllText("test", string.Empty);
}
}
public static void Test() {
string myPluginPath = "myAssembly.dll";
// Load the assembly
Assembly assembly = Assembly.LoadFrom(myPluginPath);
// Get all the referenced assemblies
foreach (AssemblyName name in assembly.GetReferencedAssemblies()) {
Console.WriteLine(name.Name);
// Different outputs:
//
// .NET 5
// - System.IO.FileSystem
// - ....
// .NET 4
// - mscorlib
// - Plugin
}
}
Any idea how to make the myAssembly.GetReferencedAssemblies work on .net4 ? Thanks!
Any idea how to make the myAssembly.GetReferencedAssemblies work on .net4?
System.IO.File resides in mscorlib.dll in .NET Framework so both versions work correctly. Why do you need the actual assembly names? You cannot rely on them across platforms.
On the other hand, if you need this to resolve assembly qualified type names, then you can do it by using the legacy assembly identities so Type.GetType("System.IO.File, mscorlib, Version=4.0.0.0") works also in .NET 5.
It's because even on the newer platforms there is an mscorlib.dll, which contains nothing but a bunch of [assembly:TypeForwardedTo(...)] attributes that provide a redirect to the new location.
But it will not work the other way around so do not expect that you can resolve the type File from a System.IO.FileSystem.dll in .NET Framework 4.0
Update after the comment:
In .NET Framework the best way for handling potentially harmful plugins is creating separate AppDomains for them with restricted permissions. Here is an example from my unit tests to create such a sandbox domain and here is an example usage. The AppDomains can even be unloaded along with their referenced assemblies.
The bad news is that this will not work in .NET 5 because starting with .NET Core you cannot create AppDomains anymore (not quite a problem if you already have a solution for .NET 5). But for the sake of completeness: starting with .NET Core 3.0 the AssemblyLoadContext type is the recommended way for handling plugins. Though it does not create a restricted environment the same way as AppDomain, you can use AssemblyDependencyResolver to control the assembly loading requests of the plugins.

Assembly being optimized out of build

I have an assembly entirely filled with classes that are implementing interfaces in another assembly. For instance:
Main Assembly (Reference to both assemblies)
Shared Assembly
-----IModule
Class Assembly (Reference to shared assembly)
-----unknownType : IModule
-----unknownType2 : IModule
The Main assembly has no direct reference to any of the types in the Class assembly. I am looking for the types like so:
// Load all referenced assemblies:
Assembly.GetExecutingAssembly().GetReferencedAssemblies()
.Except(AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName()))
.Distinct()
.ToList()
.ForEach(act => Assembly.Load(act));
// Find all instances of IModule in loaded assemblies:
var modules = from asm in AppDomain.CurrentDomain.GetAssemblies()
from provider in asm.GetTypes()
where typeof(IModule).IsAssignableFrom(provider)
...instantiate type etc...
If I have a reference to just an arbitrary type in the Class Assembly, then it shows up in GetReferencedAssemblies, gets loaded, and returned correctly - but as soon as i remove the reference to the type, it does not get transferred to the build directory or show up as a referenced assembly causing the load to fail.
Is there any way to force VS to include this assembly in the output directory? The main application should have no knowledge of any of the types within the class assembly.
I think I get your problem, but can you clarify your question? Are you expecting an assembly that you've added as a reference to your project to be copied to your output folder with your application? How did you add the reference? Is it a .dll or .exe that you browsed to in a file dialog, or is it a COM or GAC assembly that you picked from a list in the Add References dialog?
If you browsed to it, and it's not in the GAC directory, this kind of optimization is expected. Select your reference in the Solution Explorer, and make sure "Copy Local" in the properties window is set to true. You might try toggling this setting, even if it is. Your .vsproj file might just need to be rebuilt to include the reference.
If you're trying to reference a .dll from a file name stored in a string, or selected at runtime, then Visual Studio will have no way of knowing that your application is using it, nonetheless to copy it to your output directory. It shouldn't be too hard to do that with File.Copy, though, if you have the path of the .dll.

How can I get the web application assembly in ASP.NET without Global.asax?

I want to dynamically get the assembly for the currently executing web application from any code referenced by that application and executed in the same AppDomain.
I want exactly what this question asks: Get web application assembly name, regardless of current executing assembly
However, all replies in that question use the Global.asax file to get the web app assembly. That doesn't work if the application doesn't have a global.asax file, since in this case the type returned is HttpApplication, which is in the System.Web assembly.
One way I thought about going is to filter the assemblies in the current AppDomain by something, but I don't know what this something could be. Is there any difference between the assembly of the web app and another normal DLL? I know that the projects in Visual Studio have a GUID to signify the type of project (ASP.NET Web App, class library, MVC app, etc.) Is this info somehow present in the compiled assembly?
Another approach could be to use the ApplicationHost from the System.Web.Hosting namespace, but that only has static data and nothing about assemblies.
A third approach would be to include a type in the web app project and use that type, but for that my code which needs to get the web assembly name needs a reference to that type (and that would be a circular reference).
Any ideas?
As a very ugly hack, you could find an ASPX file in the app, then get the assembly containing its compiled type:
var filePath = Directory.EnumerateFiles(HttpRuntime.AppDomainAppPath, "*.aspx")
.FirstOrDefault();
var assembly = BuildManger.GetCompiledType(filePath).Assembly;
If there aren't any ASPX files, you could try ASHX or ASMX or CSHTML.
This will not work correctly for a web site project, where each file gets its own assembly.

Dynamically loading assemblies in asp.net

I'm currently trying to dynamically load an assembly from within a asp.net httphandler. I have a dll that is built as part of a seperate library and my project contains a reference to said DLL and is deployed along with the service with CopyLocal true. I create a throwaway object to get the assembly path and I have confirmed the existence of the dll within the Temporary ASP.NET Files folder, but calling GetTypes() throws an exception.
I do something like:
string assemblyPath = new SomeClassInAssembly().GetType().Assembly.Location;
Type[] types = System.Reflection.Assembly.LoadFrom(assemblyPath).GetTypes();
I cannot add the assembly to the GAC since that would defeat what I am trying to do with the service (think sandbox service that loads assemblies when necessary) and I cannot find anything that has been able to fix my problem thus far.
For reference I'm using VS 2008.
Since you compile your web application with a reference to the assembly i don't see your need to load it using Assembly.LoadFrom. The GetTypes should be available using:
Type[] types = typeof(SomeClassInAssembly).Assembly.GetTypes();
Doh, I finally hooked up to the exception and looked at the LoaderMessage and I was missing a referenced assembly.

How can I get an assembly that isn't referenced in my project?

I'm trying to get a list of assemblies for NHibernate to scan. I'm using some sample code from a tutorial application:
IEnumerable<AssemblyName> enumerable = GetType().Assembly.GetReferencedAssemblies()
.Where(name => name.Name.StartsWith(assemblyPrefix));
foreach (var assemblyName in enumerable)
x.Assembly(assemblyName.Name);
So there are 3 projects: ProjectName, ProjectName.Services, and ProjectName.Data.
The problem is that the assembly that is configuring NHibernate doesn't actually use the Services project. I thought just adding a project reference would work, but it doesn't.
Is there any way to make my project reference Services, or should I just hardcode the assembly names in the configuration?
How about AppDomain.CurrentDomain.GetAssemblies() (docs)? That should return all the loaded assemblies, which would include ProjectName.Services even if the assembly you're calling this from doesn't use it, as long as it's used somewhere else (I think it additionally must already be loaded too).

Categories

Resources