Search code examples
c#assembly-loading

Pre-loading assembly doesn't seem to work


I am writing a test to check that all the controllers in my Unity config are registered correctly. As part of my test, I use reflection to check for all the non-abstract classes that inherit from System.Web.Mvc.Controller. However, I consistent get a System.Reflection.ReflectionTypeLoadException. I find this strange for two reasons:

  1. I have a using statement at the top of my file that should cause this particular assembly to load automatically when the test is run.
  2. I also attempted pre-loading the assembly.

My code is:

using System.Linq;
using System.Reflection;
using System.Text;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Unity;

namespace Presentation.Tests
{
    [TestClass]
    public class UnityTests
    {
        [TestMethod]
        public void UnityRegistration_ControllersInitialize()
        {
            // Initialize the container the same way we do in the code
            // but make sure that the container is disconnected from the application
            // so we don't accidentally change runtime behavior through testing
            var container = UnityRegistration.Initialize(new UnityContainer());

            // Get all the controllers in the presentation layer so we can check whether or not
            // they can all be constructed at runtime
            Assembly.Load("System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
            var assembly = Assembly.ReflectionOnlyLoad("Presentation, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");
            var ctrls = assembly.GetTypes()
                .Where(t => t.IsAssignableFrom(typeof (Controller)) && !t.IsAbstract && t.IsClass);

            var builder = new StringBuilder();
            foreach (var ctrl in ctrls)
            {
                // Check resolution on each type. If resolution fails or
                // the result is null then we should record the error
                bool isErr;
                try
                {
                    var obj = container.Resolve(ctrl);
                    isErr = obj == null;
                }
                catch
                {
                    isErr = true;
                }

                if (isErr)
                {
                    builder.AppendLine(string.Format("Controller of type {0} could not be resolved.", ctrl.Name));
                }
            }

            string errors = builder.ToString();
            if (!string.IsNullOrWhiteSpace(errors))
            {
                Assert.Fail(errors);
            }
        }
    }
}

Note that the reference to Controller here refers specifically to System.Web.Mvc.Controller.

The exception that was thrown had no inner exception and a large number of loader exceptions, which all stated Cannot resolve dependency to assembly 'System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' because it has not been preloaded. When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event.

Could someone please explain why pre-loading doesn't seem to be working in this manner and how I would properly ensure that the proper assemblies are loaded?


Solution

  • From Microsoft Docs:

    The reflection-only load context allows you to examine assemblies compiled for other platforms or for other versions of the .NET Framework. Code loaded into this context can only be examined; it cannot be executed. This means that objects cannot be created, because constructors cannot be executed. Because the code cannot be executed, dependencies are not automatically loaded. If you need to examine them, you must load them yourself.

    And in your code:

    var obj = container.Resolve(ctrl);
    

    You can't use ctrl type to do anything except just to examine it. You cannot create any objects which depends on it.