Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore MvcJsonOptions to ASP.NET Core 3.0 as a type forward? #8254

Closed
martincostello opened this issue Mar 6, 2019 · 26 comments
Closed

Restore MvcJsonOptions to ASP.NET Core 3.0 as a type forward? #8254

martincostello opened this issue Mar 6, 2019 · 26 comments
Assignees
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates bug This issue describes a behavior which is not expected - a bug.

Comments

@martincostello
Copy link
Member

Problem

As originally brought up in #7220, the removal of Newtonsoft.Json as a core dependency is causing breaking application changes for applications exposing Swagger/Open API documentation using existing popular libraries such as Swashbuckle.AspNetCore (domaindrivendev/Swashbuckle.AspNetCore#1030) and NSwag (RicoSuter/NSwag#1961 (comment)).

These projects (and possibly others) have been built to rely on the MvcJsonOptions type present in earlier versions of ASP.NET Core and its presence in the DI system to access JsonSerializerSettings for JSON serialization purposes.

These errors manifest as TypeLoadException in ASP.NET Core 3.0 preview 3, and cannot easily be resolved without binary breaking changes to such libraries.

System.TypeLoadException: Could not load type 'Microsoft.AspNetCore.Mvc.MvcJsonOptions' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=3.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
   at System.Signature.GetSignature(Void* pCorSig, Int32 cCorSig, RuntimeFieldHandleInternal fieldHandle, IRuntimeMethodInfo methodHandle, RuntimeType declaringType)
   at System.Signature..ctor(IRuntimeMethodInfo methodHandle, RuntimeType declaringType)
   at System.Reflection.RuntimeConstructorInfo.get_Signature()
   at System.Reflection.RuntimeConstructorInfo.GetParametersNoCopy()
   at System.Reflection.RuntimeConstructorInfo.GetParameters()
   at Microsoft.Extensions.Internal.ActivatorUtilities.ConstructorMatcher..ctor(ConstructorInfo constructor)
   at Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass4_0.<UseMiddleware>b__0(RequestDelegate next)
   at Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder.Build()
   at Microsoft.AspNetCore.Hosting.Internal.GenericWebHostService.StartAsync(CancellationToken cancellationToken)

Suggested Solution

To provide an improved migration path for such applications where the presence of Newtonsoft.Json as a dependency is not an issue, could the MvcJsonOptions class be restored as type-forwarded from the Microsoft.AspNetCore.Mvc.Formatters.Json assembly to Microsoft.AspNetCore.Mvc.NewtonsoftJson?

The forwarded type could be marked as [Obsolete] to discourage use and removed in ASP.NET Core 4.0, while providing a "crutch" of sort to prevent blockers and/or delays to adoption of ASP.NET Core 3.0 in existing ASP.NET Core 2.x applications due to the extensive usage of Newtonsoft.Json in non-Microsoft libraries.

@muratg muratg added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Mar 6, 2019
@mkArtakMSFT mkArtakMSFT added bug This issue describes a behavior which is not expected - a bug. PRI: 1 - Required labels Mar 6, 2019
@mkArtakMSFT mkArtakMSFT added this to the 3.0.0-preview4 milestone Mar 6, 2019
@mkArtakMSFT
Copy link
Member

Thanks for contacting us, @martincostello.
@pranavkm we should investigate this and see whether there are options available to fix or mitigate this.

@JohnGalt1717
Copy link

This is a blocker. It basically makes any API layer unusable in .NET Core 3 Preview 3 because they'll all depend on this working if they have swagger documention which is essentially all of them.

@martincostello
Copy link
Member Author

I'm going to do a quick attempt at something for this now and submit a draft PR for discussion.

martincostello added a commit to martincostello/aspnetcore that referenced this issue Mar 12, 2019
Restore the MvcJsonOptions type as a type-forwarded proxy for backwards compatibility with libraries such as NSwag and Swashbuckle.AspNetCore.
Relates to dotnet#8254.
@martincostello
Copy link
Member Author

See #8437.

@pranavkm
Copy link
Contributor

Thanks for the PR @martincostello. I basically came up with the sort of solution to what your PR sent. Here's a couple of my notes from it:

  1. TypeForwardedToAttribute requires an instance of Type, so I had to reference Mvc.NewtonsoftJson from Mvc.Formatters.Json for this to work.
    • We’d have to careful not to accidentally use any APIs that aren’t in the shared fx (Microsoft.AspNetCore.App) as a consequence of this reference.
    • KoreBuild explicitly disallows referencing assemblies that aren't part of the shared fx from an assembly that ships as part of the shared fx. This is done for a good reason, and we have to figure out if this affects the the ref assemblies or targeting pack in any way.
  2. I had to change the name of options type back to the way it was in 2.x. In master, the options type in the Mvc.NewtonsoftJson package is called MvcNewtonsoftJsonOptions. Also means the options type for configuring the System.Text.Json based formatters cannot use this name.
  3. Accessing MvcJsonOptions works as long as the application references the Mvc.Newtonsoft.son package. If the package reference is missing, you get a TypeLoadException when attempting to access MvcJsonOptions. More importantly, when the package reference is missing, iterating on the assembly attributes for Mvc.Formatters.Json throws (typeof(SytemTextJsonInputFormatter).Assembly.GetCustomAttributes().ToList()) -> FileNotFoundException).
    This bit is problematic for any code that scans assemblies in the app for the presence of attributes.
  4. We'd have to keep the TypeForwardedTo attribute around in perpetuity even when libraries have migrated to support the new package.
  5. TypeForwardedTo attribute helps the immediate problem of making existing libraries compatible with the ASP.NET Core 3.0, but does not resolve the problem of adding support for the new set of JSON formatters (Add a System.Text.Json based input \ output formatter #7256) that MVC would enable by default in 3.0.
  6. TypeForwardedTo only helps making the assembly binary compatible, but not source compatible. Recompiling would require referencing the package.

We have a meeting planned to talk about this soon. I'll update the issue once we've figured out how to proceed with this.

@martincostello
Copy link
Member Author

Awesome - thanks @pranavkm!

@RicoSuter
Copy link

RicoSuter commented Mar 13, 2019

I think we need to add a new target framework (.NET Core 3) in NSwag anyway which will reference ASP.NET Core 3.0 (which I really don't like as it means you cannot use ASP.NET Core 2.2 on .NET Core 3). With this new target framework we can easily fix the renaming issue...

But I'd still like to see the type forward (at least in the previews) as it means that we don't have to directly support the preview version in NSwag (yet) :-)

@pranavkm
Copy link
Contributor

We had a meeting to talk about this. This issue with the TypeForwardedTo solution seemed to be the most vexxing:

More importantly, when the package reference is missing, iterating on the assembly attributes for Mvc.Formatters.Json throws (typeof(SytemTextJsonInputFormatter).Assembly.GetCustomAttributes().ToList()) -> FileNotFoundException).

While it's not super common, requiring the package reference to be present just to be able to iterate through all the assemblies in the App seems really problematic. @davidfowl is going to figure out if we can get some support from the CLR to solve this.

In the meanwhile, we'll work with our Swagger \ Open API partners to adopt the 3.0 changes. Like @RSuter pointed out, it's might relatively straightforward since we could pivot on the TFM.

@discostu105
Copy link

Running into the same problem. As a user, is there a workaround for me? Or do I need to wait for a fix in preview4?

@martincostello
Copy link
Member Author

With preview3 there isn't currently a workaround other than using locally compiled forks of the affected assemblies which are changed to use 3.0 preview 3.

Otherwise you need to wait for preview4, roll back to preview 2, or wait for libraries to publish versions that are compiled against preview3.

@mkArtakMSFT
Copy link
Member

Thanks for contacting us.
We think that this issue is fixed now. If you are still facing this problem, please file a new issue.

@RicoSuter
Copy link

NSwag now uses reflection to use the new type when available... should be fixed there.

@discostu105
Copy link

I'm actually still running into this (or a similar) problem with preview 4. The callstack is slightly different though. Is this a different problem? How can I find out which assembly causes the troubling reference? What can I do about it?

Csproj: https://github.com/Dynatrace/superdump/blob/netcore3/src/SuperDumpService/SuperDumpService.csproj

Exception:

System.TypeLoadException
  HResult=0x80131522
  Message=Could not load type 'Microsoft.AspNetCore.Mvc.MvcJsonOptions' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=3.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
  Source=System.Private.CoreLib
  StackTrace:
   at System.Signature.GetSignature(Void* pCorSig, Int32 cCorSig, RuntimeFieldHandleInternal fieldHandle, IRuntimeMethodInfo methodHandle, RuntimeType declaringType)
   at System.Reflection.RuntimeConstructorInfo.get_Signature()
   at System.Reflection.RuntimeConstructorInfo.GetParametersNoCopy()
   at System.Reflection.RuntimeConstructorInfo.GetParameters()
   at Microsoft.Extensions.Internal.ActivatorUtilities.ConstructorMatcher..ctor(ConstructorInfo constructor)
   at Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass4_0.<UseMiddleware>b__0(RequestDelegate next)
   at Microsoft.AspNetCore.Builder.Internal.ApplicationBuilder.Build()
   at Microsoft.AspNetCore.Hosting.Internal.GenericWebHostService.<StartAsync>d__31.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
   at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>d__9.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.<RunAsync>d__4.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.<RunAsync>d__4.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
   at SuperDumpService.Program.Main(String[] args) in C:\workspaces\pub\superdump.git\src\SuperDumpService\Program.cs:line 14

@martincostello
Copy link
Member Author

Are you using a version of NSwag or Swashbuckle.AspNetCore that is compatible with preview 3/4?

@discostu105
Copy link

Oh, thanks for that question. I checked and found out I was referencing only Swashbuckle Version="6.0.0-beta902", but not Swashbuckle.AspNetCore. So I removed my Swashbuckle reference and added

    <PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0-rc2" />
    <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="5.0.0-rc2" />

instead. Now the error is resolved. Thank you!

@jsantanders
Copy link

Seems to be that Ocelot have the same problem.

System.TypeLoadException
  HResult=0x80131522
  Message=Could not load type 'Microsoft.Extensions.DependencyInjection.MvcJsonMvcCoreBuilderExtensions' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=3.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.
  Source=Ocelot
  StackTrace:
   at Ocelot.DependencyInjection.OcelotBuilder..ctor(IServiceCollection services, IConfiguration configurationRoot)
   at Ocelot.DependencyInjection.ServiceCollectionExtensions.AddOcelot(IServiceCollection services, IConfiguration configuration)
   at OcelotApiGw.Startup.ConfigureServices(IServiceCollection services) in <path to source code file> line 73
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.InvokeCore(Object instance, IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass9_0.<Invoke>g__Startup|0(IServiceCollection serviceCollection)
   at Microsoft.AspNetCore.Hosting.StartupLoader.ConfigureServicesDelegateBuilder`1.<>c__DisplayClass15_0.<BuildStartupServicesFilterPipeline>g__RunPipeline|0(IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.Invoke(Object instance, IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass8_0.<Build>b__0(IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.StartupLoader.ConfigureServicesDelegateBuilder`1.<>c__DisplayClass14_0.<ConfigureServices>g__ConfigureServicesWithContainerConfiguration|0(IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)

@rynowak
Copy link
Member

rynowak commented Aug 28, 2019

@jsantanders - thanks for bringing this up. I"m following up on the Ocelot repo.

@rui-ktei
Copy link

rui-ktei commented Oct 3, 2019

I'm having the similar issue after upgrading to .net core 3.0

Could not load type 'Microsoft.Extensions.DependencyInjection.MvcJsonMvcCoreBuilderExtensions' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=3.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.'

Why is this issue closed? This issue is not fixed at all. Any suggestions? Thank you very much

@Zero-Xiong
Copy link

Zero-Xiong commented Oct 4, 2019

I'm having the similar issue after upgrading to .net core 3.0

Could not load type 'Microsoft.Extensions.DependencyInjection.MvcJsonMvcCoreBuilderExtensions' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=3.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.'

Why is this issue closed? This issue is not fixed at all. Any suggestions? Thank you very much

I got the same issue as you because I just tried to upgrade my app to v3.0 this morning.

@NewteqDeveloper
Copy link

I upgraded to dotnet core today, and run into the same problem. I did a little more searching and found this: https://stackoverflow.com/a/58084490

The answer talks about installing the latest Swagger package, which at the time of writing this is rc4.

I installed RC4 of Swashbuckle
Install-Package Swashbuckle.AspNetCore -Version 5.0.0-rc4
And everything is working now.

@DeeptiKrishnan
Copy link

Has this issue been resolved?

I am trying to upgrade my Asp .Net Core 2.2 MVC Web API project from 2.2 to 3.0 but I am facing an issue. I am using Swashbuckle AspNetCore for my Swagger UI API documentation. But on upgrade to 3.0 I get this error -
System.TypeLoadException: 'Could not load type 'Microsoft.AspNetCore.Mvc.MvcJsonOptions' from assembly 'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=3.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60'.'

@martincostello
Copy link
Member Author

The issue as described in the title was resolved.

You're experiencing a wider issue caused by the breaking changes made in ASP.NET Core 3.0 for consuming Newtonsoft.Json. Only 5.0.0-rc2 and later of Swashbuckle.AspNetCore support ASP.NET Core 3.0.

See domaindrivendev/Swashbuckle.AspNetCore#1061.

@Zero-Xiong
Copy link

Zero-Xiong commented Oct 7, 2019

The issue is still there if you are using Ocelot as apigateway

===================================

   at Ocelot.DependencyInjection.OcelotBuilder..ctor(IServiceCollection services, IConfiguration configurationRoot)
   at Ocelot.DependencyInjection.ServiceCollectionExtensions.AddOcelot(IServiceCollection services, IConfiguration configuration)
   at XXXXX.ApiGateway.Startup.ConfigureServices(IServiceCollection services) in D:\XXXXX\XXXXX\XXXXX\Infrastructure\XXXXX.ApiGateway\Startup.cs:line 93
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.InvokeCore(Object instance, IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass9_0.<Invoke>g__Startup|0(IServiceCollection serviceCollection)
   at Microsoft.AspNetCore.Hosting.StartupLoader.ConfigureServicesDelegateBuilder`1.<>c__DisplayClass15_0.<BuildStartupServicesFilterPipeline>g__RunPipeline|0(IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.Invoke(Object instance, IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass8_0.<Build>b__0(IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.StartupLoader.ConfigureServicesDelegateBuilder`1.<>c__DisplayClass14_0.<ConfigureServices>g__ConfigureServicesWithContainerConfiguration|0(IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)
   at Microsoft.AspNetCore.Hosting.WebHost.EnsureApplicationServices()
   at Microsoft.AspNetCore.Hosting.WebHost.Initialize()
   at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()

@davidfowl
Copy link
Member

ThreeMammals/Ocelot#1001

@sommmen
Copy link

sommmen commented Oct 16, 2019

Swashbuckle 5.0.0-rc4 worked for me. In vs - make sure you tick the 'include prerelease' right next to the search box of the package manager.

@pranavkm
Copy link
Contributor

We are aware that this is a breaking change for libraries and individual libraries have to reconcile this either by using reflection or by cross-compiling (https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.0&tabs=visual-studio#migrate-libraries-via-multi-targeting).

The most recent releases of Swahbuckle.AspNetCore and NSwag.AspNetCore have resolved this. Ocelot is aware of this change and as of writing there's an ongoing PR to address this.

Closing this as resolved as there's nothing further to do here. If you think there's further work for us to do, please file a new issue.

@dotnet dotnet locked as resolved and limited conversation to collaborators Oct 16, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates bug This issue describes a behavior which is not expected - a bug.
Projects
None yet
Development

No branches or pull requests