1
votes

I'm trying to use StructureMap to perform runtime type registration, and I'm having trouble locating types in the referenced assemblies using Type.GetType(). Here is the full code of my test app:

using System;
using System.Collections.Generic;
using System.Linq;
using StructureMap;
using StructureMap.Configuration.DSL;
using StructureMap.Graph;

namespace ConsoleApplication1 {
    class Program {
        static void Main(string[] args) {
            ObjectFactory.Initialize(r => r.Scan(
                s => {
                    s.AssembliesFromPath(
                        @"c:\Path\To\MyClasses\bin\Debug");
                    s.Convention<MyConvention>();
                }
                                              ));

            ObjectFactory.AssertConfigurationIsValid();

            foreach (var name in MyConvention.assemblyQualifiedTypenames) {
                Console.WriteLine(name);
                Console.WriteLine("Type found for reflection by assembly qualified name: {0}", Type.ReflectionOnlyGetType(name, false, false) != null);
                Console.WriteLine("Type found by assembly qualified name: {0}", Type.GetType(name) != null);
                Console.WriteLine("Type found by search: {0}",
                    AppDomain.CurrentDomain.GetAssemblies().ToList()
                    .SelectMany(assembly => assembly.GetTypes().Where(t => t.AssemblyQualifiedName == name))
                    .FirstOrDefault() != null
                );
            }
            Type type = null;
            foreach (var name in MyConvention.fullnames) {
                Console.WriteLine(name);
                Console.WriteLine("Type found by full name: {0}", Type.GetType(name) != null);
                Console.WriteLine("Type found by search: {0}",
                    AppDomain.CurrentDomain.GetAssemblies().ToList()
                    .SelectMany(assembly => assembly.GetTypes().Where(t => t.FullName == name))
                    .FirstOrDefault() != null
                );
            }
            foreach (var name in MyConvention.typenames) {
                Console.WriteLine(name);
                Console.WriteLine("Type found by name: {0}", Type.GetType(name) != null);
                Console.WriteLine("Type found by search: {0}",
                    (type = AppDomain.CurrentDomain.GetAssemblies().ToList()
                    .SelectMany(assembly => assembly.GetTypes().Where(t => t.Name == name))
                    .FirstOrDefault()) != null
                );
            }
            Console.ReadLine();
            var instance = ObjectFactory.GetInstance(type);
            Console.WriteLine("Type instance retrieved from StructureMap: {0}", instance != null);
            Console.ReadLine();
            ObjectFactory.WhatDoIHave()
                .Split(new [] { Environment.NewLine}, StringSplitOptions.None)
                .Where(s => s.Contains("Class2")).ToList()
                .ForEach(Console.WriteLine);
            Console.ReadLine();
        }

        private class MyConvention : IRegistrationConvention {
            public static List<string> typenames = new List<string>();
            public static List<string> fullnames = new List<string>(); 
            public static List<string> assemblyQualifiedTypenames = new List<string>(); 
            public void Process(Type type, Registry registry) {
                if (type.GetCustomAttributes(false).Any(o => o.GetType().Name == "MyClassAttribute")) {
                    registry.For(type).Use(type);
                    typenames.Add(type.Name);
                    fullnames.Add(type.FullName);
                    assemblyQualifiedTypenames.Add(type.AssemblyQualifiedName);
                }
            }
        }
    }
}

The test class definitions:

namespace MyAttributes {
    public class MyClassAttribute : Attribute { }
}

namespace MyClasses {
    public class MyClass1 { }

    [MyClass]
    public class MyClass2 { }
}

The output from running the console app:

MyClasses.Class2, MyClasses, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Type found for reflection by assembly qualified name: False
Type found by assembly qualified name: False
Type found by search: True
MyClasses.Class2
Type found by full name: False
Type found by search: True
Class2
Type found by name: False
Type found by search: True

Type instance retrieved from StructureMap: True

Class2 (MyClasses.Class2)                60eb30ad-ae50-413d-884f-04743ea3148c
  Configured Instance of MyClasses.Class2, MyClasses, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
                                         60eb30ad-ae50-413d-884f-04743ea3148c
  Configured Instance of MyClasses.Class2, MyClasses, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

I've done all of the testing based on this SO post - am I missing something fundamental?

2

2 Answers

1
votes

The problem is that you are trying to get the type using Type.GetType(name). Therefore .net will try and locate the assembly itself - either in the GAC or the same directory as the executing assembly. Since it is in a different directory, it cannot find it. You can confirm this by copying the executable containing MyClasses into the same directory as the console app - then it works.

For similar reasons, it will not be in AppDomain.CurrentDomain.GetAssemblies() as nothing in the current domain uses MyClasses.

You either need to load the assembly returned from MyConvention.assemblyQualifiedTypenames and then use assembly.GetType(name), or use the version of Type.GetType which allows you to specify your own assembly resolver as documented here (which will only work if the class includes the assembly name).

1
votes

Try this piece of code. This avoids using Assembly Qualified Name, hence no hardcoding for versioning.

const string myClass = "<Namespace.Class>";
const string dllName = "<DllName>,"; //Notice the ','

Type myType = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.ToString().StartsWith(dllName)).Select(a => a.GetType(myClass )).FirstOrDefault();