dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.38k stars 10k forks source link

Using an out parameter in a controller leads to Could not load type 'System.Char&' from assembly 'System.Private.CoreLib, Version=7.0.0.0 ...' #43256

Closed ladenedge closed 2 years ago

ladenedge commented 2 years ago

I am upgrading a web project to .NET 7 Preview 7 and Visual Studio 2022 Preview 17.4. On startup, I receive the following exception:

TypeLoadException: Could not load type 'System.Char&' from assembly 'System.Private.CoreLib, Version=7.0.0.0 ...'

It seems like I've set up .NET 7 incorrectly, but how?

dotnet --list-sdks shows the following row:

7.0.100-preview.7.22377.5 [C:\Program Files\dotnet\sdk]

The "Framework" for my project shows the proper version:

image

The full exception is shown here:

System.TypeLoadException: Could not load type 'System.Char&' from assembly 'System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'.
   at System.RuntimeTypeHandle.MakeByRef()
   at System.RuntimeType.MakeByRefType()
   at Microsoft.AspNetCore.Http.ParameterBindingMethodCache.<FindTryParseMethod>g__Finder|15_0(Type type)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.AspNetCore.Mvc.ModelBinding.ModelMetadata.InitializeTypeInformation()
   at Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadata..ctor(IModelMetadataProvider provider, ICompositeMetadataDetailsProvider detailsProvider, DefaultMetadataDetails details, DefaultModelBindingMessageProvider modelBindingMessageProvider)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreateModelMetadata(DefaultMetadataDetails entry)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.CreateCacheEntry(ModelMetadataIdentity key)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Metadata.DefaultModelMetadataProvider.GetMetadataForParameter(ParameterInfo parameter, Type modelType)
   at Microsoft.AspNetCore.Mvc.ApplicationModels.DefaultApplicationModelProvider.CreateParameterModel(ParameterInfo parameterInfo)
   at Microsoft.AspNetCore.Mvc.ApplicationModels.DefaultApplicationModelProvider.OnProvidersExecuting(ApplicationModelProviderContext context)
   at Microsoft.AspNetCore.Mvc.ApplicationModels.ApplicationModelFactory.CreateApplicationModel(IEnumerable`1 controllerTypes)
   at Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerActionDescriptorProvider.GetDescriptors()
   at Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerActionDescriptorProvider.OnProvidersExecuting(ActionDescriptorProviderContext context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.DefaultActionDescriptorCollectionProvider.UpdateCollection()
   at Microsoft.AspNetCore.Mvc.Infrastructure.DefaultActionDescriptorCollectionProvider.Initialize()
   at Microsoft.AspNetCore.Mvc.Routing.ActionEndpointDataSourceBase.<>c__DisplayClass11_0.<Subscribe>b__0()
   at Microsoft.Extensions.Primitives.ChangeToken.OnChange(Func`1 changeTokenProducer, Action changeTokenConsumer)
   at Microsoft.AspNetCore.Mvc.Routing.ActionEndpointDataSourceBase.Subscribe()
   at Microsoft.AspNetCore.Builder.ControllerEndpointRouteBuilderExtensions.GetOrCreateDataSource(IEndpointRouteBuilder endpoints)
   at Microsoft.AspNetCore.Builder.ControllerEndpointRouteBuilderExtensions.MapControllers(IEndpointRouteBuilder endpoints)
   at MyApp.Startup.<>c.<Configure>b__7_0(IEndpointRouteBuilder endpoints) in C:\Source\MyApp\Startup.cs:line 81
   at Microsoft.AspNetCore.Builder.EndpointRoutingApplicationBuilderExtensions.UseEndpoints(IApplicationBuilder builder, Action`1 configure)
   at MyApp.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env) in C:\Source\MyApp\Startup.cs:line 79
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodInvoker.Invoke(Object obj, IntPtr* args, BindingFlags invokeAttr)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)

This occurs in some very boilerplate web startup code:

public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
   app.UseRouting();
   app.UseMiddleware<AddHostHeader>();
   app.UseEndpoints(endpoints =>
   {
       endpoints.MapControllers();      // <-- Exception occurs in here.
       endpoints.MapBlazorHub();
       endpoints.MapFallbackToPage("/_Host");
   });
   // .. snip ..
}

What am I doing wrong?

Okay, this does seem to be a bug. (And the behavior is definitely different from .NET 5 and 6.)

Reproduction steps are actually really simple:

  1. Create new .NET 7 Preview web project. (eg. dotnet new blazorserver)
  2. Add the following code anywhere:
public class TestController
{
    public void OutMethod(out char tone) => tone = default;
}
  1. Run the project.

image

The problem appears to be the out variable in a controller method. It is not particular to char, either. It seems to occur with any type, simple or complex.

Note that my original project, above, has many "Controller" classes that are not part of the web project proper (ie. they are not intended to be mapped by ASP.NET). I probably should find a way to disable the scanning for "Controller" classes (help?), but in the meantime this a breaking change for my particular code.

davidfowl commented 2 years ago

Did you upgrade everything to preview7? Mixing versions of previews doesn't work so everything needs to be updated in unison. That means all of the packages as well.

ladenedge commented 2 years ago

Thanks for the response. I saw the release notes suggested something similar:

Update all Microsoft.AspNetCore. package references to 7.0.0-preview.7.*. Update all Microsoft.Extensions. package references to 7.0.0-preview.7.*.

I also went back to the preview 1 notes, which said basically the same thing. (I am upgrading from .NET 6, btw, not an earlier preview.)

I did upgrade the very few Microsoft.* and System.* packages that are explicitly included via NuGet:

Project 'MyApp' has the following package references
   [net7.0-windows7.0]:
   Top-level Package                                            Requested                 Resolved
   > Microsoft.AspNetCore.SignalR.Client                        7.0.0-preview.7.22376.6   7.0.0-preview.7.22376.6
   > System.Diagnostics.PerformanceCounter                      7.0.0-preview.7.22375.6   7.0.0-preview.7.22375.6

Project 'MyApp.Tests' has the following package references
   [net7.0-windows7.0]:
   Top-level Package                                Requested                    Resolved
   > Microsoft.NET.Test.Sdk                         17.4.0-preview-20220726-02   17.4.0-preview-20220726-02
   > System.Configuration.ConfigurationManager      7.0.0-preview.7.22375.6      7.0.0-preview.7.22375.6

However, I am using the Web "Sdk" option for my project:

<Project Sdk="Microsoft.NET.Sdk.Web">

And doesn't that by itself manage all of the Microsoft.* packages? When I look at the versions, they all appear to refer to the new .NET 7 versions:

image

image

... and so on. I didn't do anything in particular to "upgrade" these packages -- they just appear correct.

Unfortunately, I don't see any other possibilities for upgrade other than third-party packages such as Serilog, etc. Could they somehow be responsible? Does the error indicate at all where I should be looking for whatever I've missed?

davidfowl commented 2 years ago

Show your csproj?

Could they somehow be responsible?

Possible if they target an old preview of .NET 7

ladenedge commented 2 years ago

I don't believe any of my third-party libraries target .NET 7. I am not using any prerelease third-party packages, at least. (And I don't think I've upgraded any since they worked under .NET 6.)

Hopefully it's okay if I snip some parts of my project file. Please let me know if there is something specific you'd like to see:

<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net7.0-windows7.0</TargetFramework>
                ....
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Autofac" Version="6.4.0" />
        <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
        <PackageReference Include="Autofac.Extras.DynamicProxy" Version="6.0.1" />
        <PackageReference Include="Autofac.Extras.Ordering" Version="4.0.0" />
        <PackageReference Include="AutofacSerilogIntegration" Version="5.0.0" />
        <PackageReference Include="AutoFixture" Version="4.17.0" />
        <PackageReference Include="AutoMapper" Version="11.0.1" />
        <PackageReference Include="AWSSDK.Lex" Version="3.7.1.186" />
        <PackageReference Include="AWSSDK.Polly" Version="3.7.7.6" />
        <PackageReference Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
        <PackageReference Include="Destructurama.Attributed" Version="3.0.0" />
        <PackageReference Include="DtmfDetection.NAudio" Version="1.2.2" />
        <PackageReference Include="FpmlBrain" Version="1.1.0" />
        <PackageReference Include="GitVersion.MsBuild" Version="5.10.3">
            <PrivateAssets>all</PrivateAssets>
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
        <PackageReference Include="Google.Cloud.Speech.V1" Version="3.0.0" />
        <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.21.0" />
        <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="7.0.0-preview.7.22376.6" />
        <PackageReference Include="NAudio" Version="[1.10.0]" />
        <PackageReference Include="Nito.AsyncEx.Coordination" Version="5.1.2" />
        <PackageReference Include="Rebus.Autofac" Version="8.0.0" />
        <PackageReference Include="Rebus.Protobuf" Version="4.0.0" />
        <PackageReference Include="Rebus.RabbitMQ" Version="7.4.2" />
        <PackageReference Include="Rebus.Serilog" Version="7.0.0" />
        <PackageReference Include="Serilog" Version="2.11.0" />
        <PackageReference Include="Serilog.AspNetCore" Version="6.0.1" />
        <PackageReference Include="Serilog.Enrichers.Environment" Version="2.2.0" />
        <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
        <PackageReference Include="Serilog.Expressions" Version="3.4.0" />
        <PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
        <PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0" />
        <PackageReference Include="Serilog.Settings.Configuration" Version="3.3.0" />
        <PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
        <PackageReference Include="Serilog.Sinks.EventLog" Version="3.1.0" />
        <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
        <PackageReference Include="Serilog.Sinks.Grafana.Loki" Version="8.0.0" />
        <PackageReference Include="Serilog.Sinks.Http" Version="8.0.0" />
        <PackageReference Include="Serilog.Sinks.PeriodicBatching" Version="2.3.1" />
        <PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.0" />
        <PackageReference Include="SoftwarePotential.Configuration.Local.MultiUser-32079" Version="4.1.2056.6" />
        <PackageReference Include="SoftwarePotential.Licensing-Saga_20" Version="1.0.1.8" />
        <PackageReference Include="SoftwarePotential.Protection-32079" Version="4.1.2056.6" />
        <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="7.0.0-preview.7.22375.6" />
        <PackageReference Include="System.IO.Abstractions" Version="17.0.24" />
        <PackageReference Include="System.Linq.Async" Version="6.0.1" />
        <PackageReference Include="System.Management" Version="6.0.0" />
        <PackageReference Include="WebRtcVadSharp" Version="1.3.2" />
        <PackageReference Include="WildcardConfigurationProvider" Version="1.0.0" />
    </ItemGroup>

        ...

</Project>
ladenedge commented 2 years ago

Whoops, I just spotted System.Management at 6.0.1! However, I just upgraded that one and I'm receiving the same error:

        <PackageReference Include="System.Management" Version="7.0.0-preview.7.22375.6" />
wtgodbe commented 2 years ago

Could you show us what your .csproj looked like before you updated? Out of curiosity, does changing TargetFramework to net7.0 change anything?

ladenedge commented 2 years ago

Thanks for the response. Here is my pre-upgrade csproj (trimmed, again):

<Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
        <TargetFramework>net6.0-windows7.0</TargetFramework>
                ....
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Autofac" Version="6.4.0" />
        <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
        <PackageReference Include="Autofac.Extras.DynamicProxy" Version="6.0.1" />
        <PackageReference Include="Autofac.Extras.Ordering" Version="4.0.0" />
        <PackageReference Include="AutofacSerilogIntegration" Version="5.0.0" />
        <PackageReference Include="AutoFixture" Version="4.17.0" />
        <PackageReference Include="AutoMapper" Version="11.0.1" />
        <PackageReference Include="AWSSDK.Lex" Version="3.7.1.186" />
        <PackageReference Include="AWSSDK.Polly" Version="3.7.7.6" />
        <PackageReference Include="Castle.Core.AsyncInterceptor" Version="2.1.0" />
        <PackageReference Include="Destructurama.Attributed" Version="3.0.0" />
        <PackageReference Include="DtmfDetection.NAudio" Version="1.2.2" />
        <PackageReference Include="FpmlBrain" Version="1.1.0" />
        <PackageReference Include="GitVersion.MsBuild" Version="5.10.3">
            <PrivateAssets>all</PrivateAssets>
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
        </PackageReference>
        <PackageReference Include="Google.Cloud.Speech.V1" Version="3.0.0" />
        <PackageReference Include="Microsoft.ApplicationInsights.WorkerService" Version="2.21.0" />
        <PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="6.0.7" />
        <PackageReference Include="NAudio" Version="[1.10.0]" />
        <PackageReference Include="Nito.AsyncEx.Coordination" Version="5.1.2" />
        <PackageReference Include="Rebus.Autofac" Version="8.0.0" />
        <PackageReference Include="Rebus.Protobuf" Version="4.0.0" />
        <PackageReference Include="Rebus.RabbitMQ" Version="7.4.2" />
        <PackageReference Include="Rebus.Serilog" Version="7.0.0" />
        <PackageReference Include="Serilog" Version="2.11.0" />
        <PackageReference Include="Serilog.AspNetCore" Version="6.0.1" />
        <PackageReference Include="Serilog.Enrichers.Environment" Version="2.2.0" />
        <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
        <PackageReference Include="Serilog.Expressions" Version="3.4.0" />
        <PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
        <PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0" />
        <PackageReference Include="Serilog.Settings.Configuration" Version="3.3.0" />
        <PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
        <PackageReference Include="Serilog.Sinks.EventLog" Version="3.1.0" />
        <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
        <PackageReference Include="Serilog.Sinks.Grafana.Loki" Version="8.0.0" />
        <PackageReference Include="Serilog.Sinks.Http" Version="8.0.0" />
        <PackageReference Include="Serilog.Sinks.PeriodicBatching" Version="2.3.1" />
        <PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.0" />
        <PackageReference Include="SoftwarePotential.Configuration.Local.MultiUser-32079" Version="4.1.2056.6" />
        <PackageReference Include="SoftwarePotential.Licensing-Saga_20" Version="1.0.1.8" />
        <PackageReference Include="SoftwarePotential.Protection-32079" Version="4.1.2056.6" />
        <PackageReference Include="System.Diagnostics.PerformanceCounter" Version="6.0.1" />
        <PackageReference Include="System.IO.Abstractions" Version="17.0.24" />
        <PackageReference Include="System.Linq.Async" Version="6.0.1" />
        <PackageReference Include="System.Management" Version="6.0.0" />
        <PackageReference Include="WebRtcVadSharp" Version="1.3.2" />
        <PackageReference Include="WildcardConfigurationProvider" Version="1.0.0" />
    </ItemGroup>

        ...

</Project>

The application works and tests fine with these packages and target.

My diff shows only the two .NET packages have been changed:

Reference .NET 6 Version .NET 7 Version
Microsoft.AspNetCore.SignalR.Client 6.0.7 7.0.0-preview.7.22376.6
System.Management 6.0.0 7.0.0-preview.7.22375.6

Trying the net7.0 target will take a bit, as I'll have to comment a bunch of stuff out. But I don't believe the web component uses any windows7.0 stuff, so it should still be a pretty sound test.

wtgodbe commented 2 years ago

Do you get the same error when doing dotnet build from the command line with the preview7 SDK? If so, could you run dotnet build /bl and share the generated msbuild.binlog file?

ladenedge commented 2 years ago

Oh, actually they are all just warnings, so no commenting necessary to at least start up the app.

I made only these changes:

  1. Changed to net7.0 target.
  2. Upgraded System.* and Microsoft.* to available .NET 7 preview versions.

And we're back to this on startup:

image

Note that this is a runtime error, not a build-time error. Does a build log still matter?

wtgodbe commented 2 years ago

A build log would still help us to see what references are getting resolved/passed to the compiler, which may be informative

ladenedge commented 2 years ago

Okay, here is a build log (with the net7.0 target, so lots of warnings).

Unfortunately I must step away for a couple hours, but I'll return to this ASAP. Thank you again for your time and attention!

ladenedge commented 2 years ago

I was wondering to myself whether MapControllers() perhaps does something strange with some of my custom types, but as far as the actual controller in my app goes, it's really very simple. There is only one controller with two actions, both of which only use simple types:

[Route("api/[controller]")]
public class StatusController : ControllerBase
{
    [HttpGet]
    public async Task<IActionResult> Get(string server = null)
    {
      ...
    }

    [HttpPost]
    public async Task<IActionResult> Post(bool online, string server = null)
    {
      ...
    }
}

If you can think of some other way my setup might be messed up, please let me know!

ladenedge commented 2 years ago

Okay, this does seem to be a bug. (And the behavior is definitely different from .NET 5 and 6.)

Reproduction steps are actually really simple:

  1. Create new .NET 7 Preview web project. (eg. dotnet new blazorserver)
  2. Add the following code anywhere:
public class TestController
{
    public void OutMethod(out char tone) => tone = default;
}
  1. Run the project.

image

The problem appears to be the out variable in a controller method. It is not particular to char, either. It seems to occur with any type, simple or complex.

Note that my original project, above, has many "Controller" classes that are not part of the web project proper (ie. they are not intended to be mapped by ASP.NET). I probably should find a way to disable the scanning for "Controller" classes (help?), but in the meantime this a breaking change for my particular code.

wtgodbe commented 2 years ago

I see, that does seem like it could be a bug in 7. While we investigate, why do you have an out param in a Controller? Is it in a helper method you could make private (as a workaround)?

ladenedge commented 2 years ago

The "controllers" in question are not part of the web site. They are just part of a generic MVC-like pattern located elsewhere in the code.

Anyway, yes, I'm sure I can work around this in the short term. Long term, I guess I should either disable the convention-based routing (?), or if necessary rename my non-web controllers.

Thanks again for your attention!

wtgodbe commented 2 years ago

I updated the name & original issue description to reflect the root cause - @rafikiassumani-msft can you have someone from your team take a look since this appears to be a bug in controllers?

brunolins16 commented 2 years ago

I was able to repro the issue and looks like it is failing during a check for try/parse, introduced in .Net 7. Specifically when we call MakeByRefType().

https://github.com/dotnet/aspnetcore/blob/ca2238e75173d1f04ff0664c53dc443716a01b9d/src/Shared/ParameterBindingMethodCache.cs#L152

I will take a further look.

brunolins16 commented 2 years ago

@ladenedge If I understood correctly, these classes with the Controller suffix are not expected to be MVC Controllers, right? If so, while we are investigating, probably you are already aware of, you can work around the issue adding the [NonController] attribute or just changing the suffix and maybe that could be a long-term change for you to avoid those classes to be registered.

This doc describes the basic rules for discovering a controller: https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/actions?view=aspnetcore-6.0#what-is-a-controller

ladenedge commented 2 years ago

Great. That's exactly the sort of hint I was looking for, thank you!