Mvc: When running application on RC2, I am getting exception

Created on 21 Jun 2016  路  19Comments  路  Source: aspnet/Mvc

_From @keyurtechy on June 21, 2016 2:52_

Hi,
When running the application on RC2, I am getting the exception. Here is the stack trace of that exception:

Microsoft.AspNetCore.Server.Kestrel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60:Error: Connection id "0HKSPFOMGGOFR": An unhandled exception was thrown by the application.

System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
at System.Reflection.RuntimeAssembly.get_DefinedTypes()
at Microsoft.AspNetCore.Mvc.Controllers.ControllerFeatureProvider.PopulateFeature(IEnumerable1 parts, ControllerFeature feature) at Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager.PopulateFeature[TFeature](TFeature feature) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionDescriptorProvider.GetControllerTypes() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionDescriptorProvider.BuildModel() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionDescriptorProvider.GetDescriptors() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionDescriptorProvider.OnProvidersExecuting(ActionDescriptorProviderContext context) at Microsoft.AspNetCore.Mvc.Internal.ActionDescriptorCollectionProvider.GetCollection() at Microsoft.AspNetCore.Mvc.Internal.ActionDescriptorCollectionProvider.get_ActionDescriptors() at Microsoft.AspNetCore.Mvc.Internal.AttributeRoute.GetTreeRouter() at Microsoft.AspNetCore.Mvc.Internal.AttributeRoute.RouteAsync(RouteContext context) at Microsoft.AspNetCore.Routing.RouteCollection.<RouteAsync>d__9.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Builder.RouterMiddleware.<Invoke>d__4.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Cors.Infrastructure.CorsMiddleware.<Invoke>d__7.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.VisualStudio.Web.BrowserLink.Runtime.BrowserLinkMiddleware.<ExecuteWithFilter>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Server.IISIntegration.IISMiddleware.<Invoke>d__8.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Hosting.Internal.RequestServicesContainerMiddleware.<Invoke>d__3.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Server.Kestrel.Http.Frame1.d__2.MoveNext().

Need to understand why I am getting that error. Here is the project.json. Let me know whether I am doing something wrong. The same application was working file in RC1

{
  "dependencies": {
    "Microsoft.NETCore.App": {
      "version": "1.0.0-rc2-3002702",
      "type": "platform"
    },
    "Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",

    "Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Server.Kestrel":"1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final",
    "Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final",
    "Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final",
    "Microsoft.ApplicationInsights.AspNetCore": "1.0.0-rc2-final",
    "Microsoft.Legal.MatterCenter.Repository": "1.0.0-*",
    "Microsoft.Legal.MatterCenter.Models": "1.0.0-*",
    "Microsoft.Legal.MatterCenter.Utility": "1.0.0-*",
    "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0-rc2-final",
    "Microsoft.VisualStudio.Web.BrowserLink.Loader": "14.0.0-rc2-final",
    "Microsoft.AspNetCore.Diagnostics": "1.0.0-rc2-final",
    "Microsoft.AspNetCore.Http.Abstractions": "1.0.0-rc2-final",
    "Microsoft.IdentityModel.Tokens": "5.0.0-rc2-305061149",
    "Microsoft.Extensions.Configuration.Binder": "1.0.0-rc2-final",
    "System.Globalization": "4.0.11-rc2-24027",
    "System.Runtime": "4.1.0-rc2-24027",
    "System.Threading.Tasks": "4.0.11-rc2-24027",
    "System.IO": "4.1.0-rc2-24027",
    "System.Text.Encoding": "4.0.11-rc2-24027",
    "System.Xml.XmlDocument": "4.0.1-rc2-24027",
    "Newtonsoft.Json": "8.0.3"
  },
  "tools": {
    "Microsoft.AspNetCore.Server.IISIntegration.Tools": {
      "version": "1.0.0-preview1-final"
    }

  },

  "frameworks": {
    "netcoreapp1.0": {
      "imports": [
        "dnxcore50",
        "net451"        
      ],
      "dependencies": {
        "Swashbuckle.SwaggerUi": "6.0.0-beta9",
        "Swashbuckle.SwaggerGen": "6.0.0-beta9",
        "Microsoft.SharePointOnline.CSOM": "16.1.5026.1200",
        "Microsoft.Exchange.WebServices": "2.2.0 "        
      }
    }
  },

  "buildOptions": {
    "emitEntryPoint": true,
    "preserveCompilationContext": true
  },

  "runtimeOptions": {
    "gcServer": true
  },

  "publishOptions": {
    "include": [
      "wwwroot",
      "Views",
      "appsettings.json",
      "web.config"
    ]
  },

  "scripts": {
    "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
  }
}

_Copied from original issue: aspnet/KestrelHttpServer#936_

bug

All 19 comments

@keyurtechy It looks like Assembly.DefinedTypes is throwing a ReflectionTypeLoadException when MVC is trying to search for Controller types which is fairly unusual. Unfortunately the error message leaves a bit to be desired. As the exception message suggests, you really want to see the LoaderExceptions to figure out what's really going on.

I think MVC should have special logging for ReflectionTypeLoadExceptions like SignalR does. SignalR takes it a step further by using any Hub types found in the Types property of the exception and carrying on as if there was no exception at all (aside from logging). I'm not sure MVC has to take it that far.

In the short term, you can probably get more detail into the cause of the exception my adding some custom logging middleware before calling UseMvc. You could add something like the fallowing in your Startup.cs file:

using System.Reflection;

// ..

public void Configure(IApplicationBuilder app)
{
    // ..
    app.UseMiddleware<ReflectionTypeLoadExceptionLoggingMiddleware>();
    app.UseMvc();

// ..

public class ReflectionTypeLoadExceptionLoggingMiddleware
{
    private readonly RequestDelegate _next;

    public ReflectionTypeLoadExceptionLoggingMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public Task Invoke(HttpContext httpContext)
    {
        try
        {
            return _next(httpContext);
        }
        catch (ReflectionTypeLoadException ex)
        {
            Console.WriteLine("ReflectionTypeLoadException!!! {0}", ex);

            if (ex.LoaderExceptions != null)
            {
                Console.WriteLine("Loader exceptions messages: ");

                foreach (var exception in ex.LoaderExceptions)
                {
                    Console.WriteLine(exception);
                    Console.WriteLine();
                }
            }

            throw;
        }
    }
}

@halter73 - perhaps the error page middleware would be a better fit for the exception unwrapping code ?

@pranavkm I didn't try it, because I didn't what to figure out how to create a ReflectionTypeLoadException, but are you sure the error page middleware will show you ReflectionTypeLoadException.LoaderExceptions?

Given no reference to this exception type and no use of reflection (including json encoding) in DeveloperExceptionPage.DisplayRuntimeException(), I doubt the error page will show info visible only by reading a property specific to the exception type.

This is another reason that MVC should probably handle this exception type explicitly. Perhaps it could just throw a new exception with more details from the LoaderExceptions in the main Message.

Nope - I was implying we should have the exception unwrapping code in the diagnostics middleware. I don't think it exists as of now.

MVC 1 - 5 originally had some logic in it for this exact exception. I'm guessing that's where SignalR got its logic from 馃槃 I would think in the new world perhaps it is best to catch the exception, handle all the types that _did_ load, and log all the errors? The "annoying" thing about this exception is that it can sometimes be innocuous. E.g. sometimes apps have DLLs that reference other DLLs that aren't deployed because perhaps they're used only in other scenarios (e.g. design-time or test-time only), but Reflection will throw an exception if it can't resolve the base class of a type, even though that type is not of any interest to the scenario in question (e.g. discovering controllers).

@ajaybhargavb please discuss with @pranavkm to make sure all 3 of us are on the same page here before we code this up.

The "annoying" thing about this exception is that it can sometimes be innocuous. E.g. sometimes apps have DLLs that reference other DLLs that aren't deployed because perhaps they're used only in other scenarios (e.g. design-time or test-time only)

Is this still a relevant concern in the project.json/dotnet world?

Type-load exceptions are probably the number one way people discover they have a package mismatch. Putting it in the log means that most won't notice it until they notice that one of their controllers is missing, and we've turned something obvious into a silent failure. VS users today don't really see the console log today.

This just seems like a workaround for something that shouldn't be common in a package-centric workflow.

I agree with @rynowak , Logging isn't a great source of discovery of issues. Unwrapping the exception as part of displaying it in Diagnostics seems like something we could do pretty easily and it has higher value than logs. Even in your scenario of missing types, seems like it would be more useful to see the name of the assembly rather than a generic TypeLoadException.

+1 on a diagnostics page to show something special for these types of exceptions. Though it won't cover all of the cases though for e.g. The hosting dll can't be loaded

@davidfowl would that be too early for the Startup error page to show anything?

@pranavkm How is this related to the Startup error page? Isn't the exception being thrown during the first request routed to MVC? Or did you think "it won't cover all of the cases" was would instead?

@halter73 I was looking at @davidfowl's example where he says the Hosting dlls couldn't be loaded. We should replicate the code in Hosting, but there's likely a class of these errors that happen before hosting is bootstrapped.

If Hosting dlls can't be loaded the process would crash as soon as new WebHostBuilder() is called in Program.Main, right? I don't see how this affects logging exceptions thrown from accessing Assembly.DefinedTypes in ControllerFeatureProvider.PopulateFeature.

It has nothing to do with where the exception is thrown, it's that if the exception is thrown in some cases (like before ControllerFeatureProvider.PopulateFeature) it won't show up anywhere. We may want some extra code to unwrap the exception and print out LoaderExceptions if it happens before the error page is available (Maybe the Hosting.dll is there but maybe some other dll isn't).

It's the same reason we code in DNX https://github.com/aspnet/dnx/blob/199bc294b6c0958336a4a6d5c9eb5b9d31582fa2/src/Microsoft.Dnx.Host/RuntimeBootstrapper.cs#L44-L51.

If only we still had a runtime bootstrapper. The point about being unable to load Hosting, is that the error that's surfaced when the process crashes is out of our control. As soon as we start executing startup code, you have a point.

Sure, that was kinda the point, Hosting.dll is a bad one because you at least need our code to run.

Now I also think we should request this functionality from the CLR host.

Now I also think we should request this functionality from the CLR host.

That sounds nice to have. It'll cover scenarios that the diagnostics middleware cannot catch. Of course getting to it (in particular when working from inside VS) is still not super intuitive, but it's a start.

Discussed with @Eilon, there is no change needed in MVC. The changes will be in Hosting (https://github.com/aspnet/Hosting/issues/819) and Diagnostics (https://github.com/aspnet/Diagnostics/issues/315).

Was this page helpful?
0 / 5 - 0 ratings