Prevent Assembly.GetTypes() from loading dependencies - c#

I have assembly A referencing assembly B. They are located in the same directory.
Type[] types = null;
try
{
Assembly a = Assembly.Load("A.dll");
types = a.GetTypes(); // loads B implicitly.
}
catch(ReflectionTypeLoadException ex)
{
types = ex.Types.Where(x=>x!=null);
}
How do I prevent B from being loaded? I want GetTypes() to run as if B wasn't available and to return only available types and null's for not available so I could execute
ex.Types.where(x=>x!=null);
I want to use the trick from How to prevent ReflectionTypeLoadException when calling Assembly.GetTypes()
This way I can get only types that don't depend on B and work with them.
Update:
I loaded A in the reflection as well as in the normal context. I used the reflection context A for calling GetTypes(). After getting types from the reflection context assembly, I had another problem. When I called Type.GetCustomAttributes(), I received the following exception
It is illegal to reflect on the custom attributes of a Type loaded via
ReflectionOnlyGetType (see Assembly.ReflectionOnly) -- use
CustomAttributeData instead.
I solved it by getting the same type from the normal context assembly.
//... code omited for brevity
Type reflectionType = reflectionAssembly.GetTypes()[0];
//... code omited for brevity
Type goodType = normalAssembly.GetType(reflectionType.FullName);
This way I prevented loading of B and used types from A independent from B.

look at using Assembly.ReflectionOnlyLoad() which looks to not load dependencies. not sure if it will return types that reference that dependency or not as i've not tried to do it before.

It's 2022, and .NET 6 is here, so the solution mentioned above is officially obsolete.
Here is a new solution:
https://github.com/dotnet/corefxlab/blob/master/docs/specs/typeloader.md
Apparently, this is an archived experimental repository, so you have to embed the code into your project manually. Take note of the license.

Related

Class library to utilize invoking namespace

I'm trying to understand reflection in more detail. The project I'm working on is intended to be an internal package, consumed by multiple developers. The problem, we parse a lot of data from varying departments. With documents that have varying headers, ordering, and often abbreviations of the name within the header.
Example: (Delimited Example)
Department A :
Date, Volume, Depth, Turbidity
Date and Time, Volume, Turbidity, Depth
Date, Vol., Turb., Dep.
Date, NTU, Vol, Dep ft
Department B:
Date/Time, Flow, Level, Velocity
Timestamp, Lvl, Flow, Vel.
So in the library I wanted to include a mapping file, with a method that in essence would be called GetHeaderConfigurations. Whomever references this library would be able to call the library with a user friendly name, but would pass an object with certain information.
The important piece would be their object and a namespace. Which I would recurse the namespace for classes, ones that are details about the header within the file.
The problem:
public static IEnumerable<Type> GetHeaderConfiguration(FileConfiguration configuration)
{
var assembly = Assembly.Load(configuration.HeadersNamespace);
// Some more code
}
When I call that from another application that references the library, I don't receive all of the classes. The application builds the object, then stores the namespace in the following manner "Sample.Headers.LabConfiguration". I execute the following:
var headers = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(t => t.IsClass && t.Namespace == "Sample.Headers");
The above does return all the classes within the directory in the solution, but for some odd reason when I attempt to load the assembly the code fails.
I believe the issue is when I call Assembly.Load I'm passing a namespace, not an assembly. Which leads me to the root of the question I'm hoping to understand. How can I successfully use that namespace? How can I load the internal properties?
Assembly.Load() does just that, loads a new assembly (roughly speaking, a DLL or EXE). If you just want to look for classes within the current assembly, use Assembly.GetExecutingAssembly() to get the currently executing Assembly object, and then call GetTypes() on that.
var assembly = Assembly.GetCurrentlyExecutingAssembly();
var headers = assembly.GetTypes().Where(...);
MSDN has the following note regarding performance:
For performance reasons, you should call this method only when you do not know at design time what assembly is currently executing. The recommended way to retrieve an Assembly object that represents the current assembly is to use the Type.Assembly property of a type found in the assembly, as the following example illustrates.
Unless you're running this code many times in a tight loop, there's no reason to be concerned with performance in my opinion. But if you want to follow MSDN's advice, you can replace Assembly.GetExecutingAssembly() with typeof(ThisClass).Assembly where ThisClass is the name of the class containing the code. But Assembly.GetExecutingAssembly() still works (and doesn't require coupling with the class name).

Disable proxy types for OrganizationServiceProxy

I'm working with pre-defined IOrganizationService object I cannot modify — it's out of my control.
And it seems to be by default this IOrganizationService returns resolved types as if EnableProxyTypes() was called implicitly somewhere on originating OrganizationServiceProxy.
And this ruins all business logic, since early bound entities could not be serialized that easily, since on different installations they could be resolved to different types. So even if they would be successfully serialized, cannot be 100% certain that they could be de-serialized.
To be more specific it's plugin for XrmToolBox. If hosting application loaded plugin that uses early bounding, these generated types will be discovered via reflection and applied to results of Retrieve and RetrieveMultiple calls.
For example RetrieveMultiple to account entity response will normally contain Entity[], each item of this array will have LogicalName property set to account.
But if early bound types was discovered in assembly, let's call it EarlyBouldLibrary.dll, the same RetrieveMultiple will return EarlyBouldLibrary.Account[]. Each item of this array will be derived from Entity and will contain same field as in first example, but with additional properties.
The problem is in following. If we will try to serialize EarlyBouldLibrary.Account[] to XML it will fail. Because EarlyBouldLibrary.Account is unknown.
Ok, it's possible to add types to known. But there is no guarantee, that one day another early bound types will be found in NewEarlyBouldLibrary.dll... And they will have name NewEarlyBouldLibrary.Account... Which is unknown...
Ok, let's assume serialization succeeded, no matter which types are referenced EarlyBouldLibrary.Account or NewEarlyBouldLibrary.Account, but if we will try to de-serialize, on environment where these early bound libraries not present... action will fail.
So only way will be not to use specific early bound types, that out of our control, and use standard and safe Entity. But it's impossible while system automatically resolves proxy types.
So, question: is there any way to disable resolve of early bound types?
You can redirect your proxy to another assembly containing the types you need (or even containing no early bound types at all).
The example below resets the proxy assembly:
private void ResetProxyAssembly(IOrganizationService service)
{
var serviceProxy = (OrganizationServiceProxy) service;
serviceProxy.EnableProxyTypes();
}
In certain scenarios this may not work. Instead you can explicitly select the desired proxy assembly:
private void ResetProxyAssembly(IOrganizationService service)
{
var serviceProxy = (OrganizationServiceProxy) service;
serviceProxy.EnableProxyTypes(typeof(MyEarlyBoundEntity).Assembly);
}
Could you add [assembly: Microsoft.Xrm.Sdk.Client.ProxyTypesAssembly] to the AssemblyInfo file of the EarlyBouldLibrary.dll or NewEarlyBouldLibrary.dll assembly?

Get Type of Object from String Value

I am writing a customer class which will read CS files and spit out information based on method names and their various parameters.
This essentially reads each line looking for keys (public, class, etc) and then sees what its all about. Anyway this bit works fine, what I'm having issues with is dealing with various different Types.
So what I need to do is work out whether the type is one found natively in .Net, or something I've created, I'm really not bothered which way round just as long as I have some way of telling.
I've tried Type t = Type.GetType("My.Namespace.Classname"); but this just returns null even with the full namespace and name of my custom class object. However if I was to do the same code but with System.String it works perfectly fine, but I can't really account for each possible namespace in the entire framework. This will mean I need a way to get the type without the full namespace, or know how to check my own custom objects using GetType.
Can anybody provide any suggestions on how to go about this? Even if it was creating a new instance of the objects that would be enough, but again I don't have the full namespace for .Net objects.
Edit: Bit of a background
What I'm doing is reading classes that I've created in a StreamReader, reason being that I'm creating lots of them and need to do making between objects that one system will be able to understand, and another, so this code would read everything and just create the mapping for me. And in most cases this is perfectly fine, it is only when I have custom types, so I want to identify these are mark them.
I've tried Type t = Type.GetType("My.Namespace.Classname"); but this
just returns null
You need to provide the full assembly-qualified name:
Type t = Type.GetType("My.Namespace.Classname, MyAssembly");
From MSDN:
Parameters
typeName
Type: System.String
The assembly-qualified name of the type to get. See AssemblyQualifiedName. If the type is in the currently executing
assembly or in Mscorlib.dll, it is sufficient to supply the type name
qualified by its namespace.
Anyway, if you're looking to parse C# code an analyze it, I would take a look at NRefactory - an open source C# parser -.
Here's an introduction in CodeProject to NRefactory.
I've tried Type t = Type.GetType("My.Namespace.Classname"); but this just returns null even with the full namespace and name of my custom class object.
I suspect that's because it's not in the calling assembly or mscorlib, which are the only two assemblies checked by Type.GetType for names which aren't assembly-qualified.
If you know all the assemblies involved, you could run through each of them calling Assembly.GetType(namespaceQualifiedName) on each of them.
However, if you don't even have the namespace-qualified name, you should possibly create a lookup of all types in all the relevant assemblies, based on their names. For example:
var lookup = assemblies.SelectMany(a => a.GetTypes())
.ToLookup(t => t.Name);
At that point, for each name you have(e.g. Classname in your example) you can find all the types with that name:
foreach (var type in lookup[name])
{
// Do something with type
}
Type.GetType(some_type_name) will return type object if some_type_name is name of type declared any assemblies loaded at the moment, or in Mscorlib.dll
So if your are parsing your types from .cs files and not loading assebly - it will always be null with types names from your source file

Why I can't load classes from other assemblies?

In the web.config of my MVC project, I have this:
<appSettings>
<add key="MembershipRepository" value="Repo.MembershipRepository"/>
</appSettings>
I have a code like this:
var appSettings = ConfigurationManager.AppSettings;
string membershipRepositoryClassName = appSettings["MembershipRepository"];
Type membershipRepositoryType = Type.GetType(membershipRepositoryClassName);
IMembershipRepository repository = (IMembershipRepository)Activator.CreateInstance(membershipRepositoryType);
Suppose, the web application containing the web.config is in assembly Web.
The code segment I gave is in assembly Lib.
The class MembershipRepository is in assembly Repo.
Web has reference to both Lib and Repo.
Lib does not have reference to any other assemblies (Its liekly to be referenced as dll).
Repo may or may not have reference to Lib.
I get the membershipRepositoryType to be null. I understand that perhaps I need to specify the assembly in which MembershipRepository is. One way is to specify the assembly name in configuration (like this). But I think there should be some other ways. Otherwise how the other classes are loaded geting only the class name from the config file?
For instance, MembershipProvider class is loaded just fine from other assemblies.
How can I do the same. Or if I can't, why I can't?
From the MSDN Library documentation for Type.GetType(String):
typeName: The assembly-qualified name of the type to get. See AssemblyQualifiedName. If the type is in the currently executing assembly or in Mscorlib.dll, it is sufficient to supply the type name qualified by its namespace.
That last sentence explains why it sometimes works when you specify only the class name: If the executing code is in Repo, then you can get away with just specifying MembershipProvider. But I recommend that for clarity (and performance too), you avoid this behavior and always specify the assembly name. Alternately, if you already have a reference to the Assembly that contains your type, then you can call Assembly.GetType instead.
Nevertheless, if you truly have a scenario where you don't know which assembly contains your type, then you can search all loaded assemblies in the current AppDomain and try calling Assembly.GetType on each one:
Type type = AppDomain.CurrentDomain.GetAssemblies()
.Select(assembly => assembly.GetType(typeName))
.First(t => t != null);
What you're looking for is something like this:
var types = AppDomain.CurrentDomain.GetAssemblies().ToList()
.SelectMany( s => s.GetTypes() )
.Where( p => p.Name == membershipRepositoryClassName );
That is actually going to return a list, because a type with the same name may be declared in more than one assembly. If you are sure there is only one type, you can add .FirstOrDefault() or .Single() to the end of the chain. Use .FirstOrDefault() if you aren't sure if the type exists and want it to return null if it doesn't. Use .Single() if you are sure the type should exist is and want it to throw an exception if they type isn't found.

How to reflect over assemblies that are already loaded?

I have 2 projects in the solution. Project1UI references Project2Reports
Project1UI:
MainForm.cs
Project2Reports:
BaseReport.cs // all classes below inherit from it
Report1.cs
Report2.cs
Report3.cs
From Project1UI, how can I find all the classes that inherit from BaseReport? The project1UI already references the 2nd assembly - is there a way to do it without manually loading the 2nd assembly manually (e.g. Assembly.Load) since it's already loaded.
You have to process all the types in the assembly and look for the types that implement it.
You can use something like that (written by hand right now, it may contains errors).
foreach (Type type in Assembly.GetAssembly(typeof(BaseReport)).GetTypes())
{
if (type != typeof(BaseReport) && typeof(BaseReport).IsAssignableFrom(type))
{
// we found a type, we can store it somewhere, for example, in a list and our list in a static readonly field for fast lookup in the future.
myreports.Add(type);
}
}
You can also process all the loaded assemblies.
This however is not the best way to do that, is complicated, quite obscure and quite hard to understand.
I would use a simple factory class that will give you the instance of your report as requested, when you add a report, add it through a simple .Add call.

Categories