Searching type in assemblies
Asked Answered
B

5

6

I had an issue that code Type.GetType(myTypeName) was returning null because assembly with that type is not current executing assembly.

The solution I found for this issue is next:

var assemblies = AppDomain.CurrentDomain.GetAssemblies();
Type myType = assemblies.SelectMany(a => a.GetTypes())
                        .Single(t => t.FullName == myTypeName);

The problem is that the first run of this code causes exception "Sequence contains no matching element". When I call this part of code again - everything is ok and needed Type is loaded.

Can anyone explain such behavior? Why in the scope of first call no needed assembly/type found?

Boisleduc answered 14/9, 2012 at 10:32 Comment(6)
Could this be a race condition? Are you requesting the type before it got loaded?Sophy
Yes. This is the reason. What is the best practice to force somehow the loading of assemblies.Boisleduc
@Boisleduc I'm not sure, but doesn't Assembly.Load help you do that ?Murchison
@Murchison Krishna. I have references to that assemblies. Why do I have to load them explicitly?Boisleduc
@Boisleduc there is a question already on SO regarding forcing the assembly loading thing hope this helpsMurchison
You need to force load them otherwise they will only be loaded when the types that they contain are actually needed in the running code.Razor
H
4

Problem you are facing is caused by design of GetAssemblies method of AppDomain class - according to documentation this method:

Gets the assemblies that have been loaded into the execution context of this application domain.

So when in your program type fails to be found first time - its assembly isn't obviously loaded by the application yet. And afterwards - when some functionality from assembly that contains type in question had been used - assembly is already loaded, and same code can already find missing type.

Please try loading assemblies directly. Instead of using:

var assemblies = AppDomain.CurrentDomain.GetAssemblies();

you can use:

List<Assembly> assemblies = Assembly.GetEntryAssembly().GetReferencedAssemblies().Select(assembly => Assembly.LoadFrom(assembly.Name)).ToList();
Hoopoe answered 14/9, 2012 at 11:7 Comment(4)
Yes, but now I see that I have to keep in mind that not all needed assemblies are loaded at the momentBoisleduc
I don't know which assembly I need and which of them should be loaded.)) I don't know which assembly contains type I needBoisleduc
Please see my comment above, you need to force load them.Razor
@Boisleduc Please see updated answer - I have taken missing part from answer of Jon Skeet referenced by Justing Harvey.Hoopoe
R
2

It is possible that the type is in an assembly that has not yet been loaded. Later in your program it then is. If you look at the output window this should give you sn idea of when the assemblies are being loaded.

Razor answered 14/9, 2012 at 10:34 Comment(2)
Yes. The reason that assemblies are loading couple of seconds (~5-10). What is the best practice to force somehow the loading of assemblies?Boisleduc
Yes, have a look at this #2385092Razor
Z
1

Another answer shows the best way for obtaining (at runtime) a Type defined in an Assembly that might not be loaded:

var T1 = Type.GetType("System.Web.Configuration.IAssemblyCache, " + 
                      "System.Web, " + 
                      "Version=4.0.0.0, " + 
                      "Culture=neutral, " + 
                      "PublicKeyToken=b03f5f7f11d50a3a");

As you can see, unfortunately that method requires you to supply the full AssemblyQualifiedName of the Type and will not work with any of the abbreviated forms of the assembly name that I tried. This somewhat defeats our main purposes here. If you knew that much detail about the assembly, it wouldn't be that much harder to just load it yourself:

var T2 = Assembly.Load("System.Web, " + 
                       "Version=4.0.0.0, " + 
                       "Culture=neutral, " + 
                       "PublicKeyToken=b03f5f7f11d50a3a")
                 .GetType("System.Web.Configuration.IAssemblyCache");

Although these two examples look similar, they execute very different code paths; note for example, that the latter version calls an instance overload on Assembly.GetType versus the static call Type.GetType. Because of this, the second version is probably faster or more efficient. Either way, though, you seem to end up at the following CLR internal method, and with the second argument set to false, and this is why neither method goes to the trouble of searching for the required assembly on your behalf.

[System.Runtime.Remoting.RemotingServices]
private static RuntimeType LoadClrTypeWithPartialBindFallback(
                  String typeName,
                  bool partialFallback);

A tiny step forward from these inconveniences would be to instead call this CLR method yourself, but with the partialFallback parameter set to true. In this mode, the function will accept a truncated version of the AssemblyQualifiedName and will locate and load the relevant assembly, as necessary:

static Func<String, bool, TypeInfo> LoadClrTypeWithPartialBindFallback =
    typeof(RemotingServices)
    .GetMethod("LoadClrTypeWithPartialBindFallback", (BindingFlags)0x28)
    .CreateDelegate(typeof(Func<String, bool, TypeInfo>))
    as Func<String, bool, TypeInfo>;

// ...

var T3 = LoadClrTypeWithPartialBindFallback(
            "System.Web.Configuration.IAssemblyCache, System.Web",
            true);    //  <-- enables searching for the assembly

This works as shown, and also continues to support specifying the full AssemblyQualifiedName as in the earlier examples. This is a small improvement, but it's still not a fully unqualified namespace search, because you do still have to specify the short name of the assembly, even if it might be deduced from the namespace that appears in the type name itself.

Zeist answered 25/5, 2017 at 0:24 Comment(0)
L
0

And if you exlucde mscorlib assembly?? , you can try this:

  var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(asb=>!asb.FullName.StartsWith("mscorlib")).ToList();

Type myType = assemblies.SelectMany(a => a.GetTypes())
                        .Single(t => t.FullName == myTypeName);
Legist answered 14/9, 2012 at 11:6 Comment(0)
E
0

BTW if you do know the FullName of the assembly containing the type (or an assembly containing a TypeForwardedToAttribute for the type) you can use Type.GetType. Specifically, Type.GetType(Assembly.CreateQualifiedName(assembly.FullName, myTypeName)) which will look something like:

Type.GetType("Some.Complete.Namespace.myTypeName, Some.Assembly.Name, Version=1.2.3.4, Culture=neutral, PublicKeyToken=ffffffffffffffff");

This will load the assembly if it is not already, assuming the framework can resolve the location of the assembly.

Here's my sample LinqPad query confirming the parenthetic TypeForwardedToAttribute remark:

var u = (from a in AppDomain.CurrentDomain.GetAssemblies()
         let t = a.GetType("System.Lazy`2")
         where t != null
         select t).FirstOrDefault();
(u?.AssemblyQualifiedName).Dump();
u = Type.GetType("System.Lazy`2, System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
(u?.AssemblyQualifiedName).Dump();

Output:

null
System.Lazy`2, System.ComponentModel.Composition, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Exceptionable answered 14/9, 2016 at 11:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.