Why I can't load classes from other assemblies? - c#

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.

Related

C# Create an instance of a class from a type FullName

Does anybody know of an easy way to create an instance of a class given the FullName of a type (namespace.classname format), but not having the AssemblyQualifiedName? The FullName of the type is only contained in a single assembly that is referenced and available at runtime, so there is no ambiguity.
I don't have the type, I have a string - the fullname only, without the assembly part. I don't know the assembly, I just know it's referenced and there is only one assembly containing the type fullname.
Thanks!
EDIT:
This is to do with deserialization of instances from a proprietary protocol. I've recieved good answers, but what I'll end up do in order to have optimal solution and not iterate through all the assemblies is to create a static dictionary with fullName key and assembly qualified name as value, given I know which exactly are the classes and they are a limited set.
You can get all of the active assemblies and then look through each one for a type with the full name you have. You'll need to prepare for the possibility that there are multiple matches, or no matches, among the assemblies.
Load all of your assemblies and select over their types, then filter on the name.
public static object CreateType(string typeName)
{
var allTypes = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany
(
a => a.GetTypes()
);
var type = allTypes.Single( t => t.Name == typeName );
return Activator.CreateInstance(type);
}
You might consider adding some validation code to check for duplicates, and to ensure there is a default constructor.

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).

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

Cannot get type by full name despite this type exists and is loaded

Weird problem, I get System.TypeLoadException "Could not load type 'Color'":
using UnityEngine;
Type.GetType(typeof(Color).FullName, true);
Of course, I cannot just use typeof(Color), the code demonstrates that this type exists and is loaded and its name is correct.
typeof(Color).FullName == "UnityEngine.Color".
I also tried:
typeof(Color).Module.GetTypes().First(t => t.Name == "Color")
works fine, but
typeof(Color).Module.GetType("Color", true, false)
throws TypeLoadException. So I make a conclusion that it's not a "fully qualified name" problem but something else.
I also tried another types from UnityEngine assembly and from another 3rd-party assembly.
I checked Mono sources but related code is in C implementation and is quite difficult to comprehend quickly.
Type.FullName doesn't include the assembly - so unless the type is in either the calling assembly or mscorlib, it won't be found.
Basically, if you're trying to load a type from an arbitrary assembly there are two simple options:
Use the assembly-qualified name within Type.GetType()
Use Assembly.GetType
If you know another type in the same assembly at compile-time, it's often simplest to use:
Type type = typeof(KnownType).Assembly.GetType("Qualified.UnknownType");

Prevent Assembly.GetTypes() from loading dependencies

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.

Categories