Skip to content

Commit 010d522

Browse files
authored
Add NativeAOT compatibility for ASP.Net Core in .NET 8 (#1124)
1 parent ac38114 commit 010d522

24 files changed

+743
-16
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
<AnalysisMode>Recommended</AnalysisMode>
2727
<Nullable>enable</Nullable>
2828
<IsPackable>true</IsPackable>
29-
<NoWarn>$(NoWarn);IDE0056;IDE0057;ASP0014</NoWarn> <!-- Index/Range operators, UseRouting -->
29+
<NoWarn>$(NoWarn);IDE0056;IDE0057;ASP0014;CA1510;CA1513</NoWarn> <!-- Index/Range operators, UseRouting, throw helpers -->
3030

3131
<AssemblyName>GraphQL.Server.$(MSBuildProjectName)</AssemblyName>
3232
<RootNamespace>GraphQL.Server.$(MSBuildProjectName)</RootNamespace>

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,15 @@ via the `multipart/form-data` content type as attached files. If you wish to al
10041004
allow clients to send files as base-64 encoded strings, you can write a custom scalar
10051005
better suited to your needs.
10061006

1007+
### Native AOT support
1008+
1009+
GraphQL.NET Server fully supports Native AOT publishing with .NET 8.0 and later.
1010+
See [ASP.NET Core support for Native AOT](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/native-aot)
1011+
for a list of features supported by .NET 8.0. However, GraphQL.NET only provides limited
1012+
support for Native AOT publishing due to its extensive use of reflection. Please see
1013+
[GraphQL.NET Ahead-of-time compilation](https://github.com/graphql-dotnet/graphql-dotnet?tab=readme-ov-file#ahead-of-time-compilation)
1014+
for more information.
1015+
10071016
## Samples
10081017

10091018
The following samples are provided to show how to integrate this project with various

src/Authorization.AspNetCore/Authorization.AspNetCore.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0</TargetFrameworks>
4+
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0;net8.0</TargetFrameworks>
55
<Description>Integration of GraphQL.NET validation subsystem into ASP.NET Core</Description>
66
<PackageTags>GraphQL;authentication;authorization;validation</PackageTags>
7+
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>
78
</PropertyGroup>
89

910
<ItemGroup>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// <auto-generated/>
2+
#pragma warning disable
3+
#nullable enable annotations
4+
#if !NET5_0_OR_GREATER
5+
6+
// Licensed to the .NET Foundation under one or more agreements.
7+
// The .NET Foundation licenses this file to you under the MIT license.
8+
9+
namespace System.Diagnostics.CodeAnalysis
10+
{
11+
/// <summary>
12+
/// Specifies the types of members that are dynamically accessed.
13+
///
14+
/// This enumeration has a <see cref="global::System.FlagsAttribute"/> attribute that allows a
15+
/// bitwise combination of its member values.
16+
/// </summary>
17+
[global::System.Flags]
18+
internal enum DynamicallyAccessedMemberTypes
19+
{
20+
/// <summary>
21+
/// Specifies no members.
22+
/// </summary>
23+
None = 0,
24+
25+
/// <summary>
26+
/// Specifies the default, parameterless public constructor.
27+
/// </summary>
28+
PublicParameterlessConstructor = 0x0001,
29+
30+
/// <summary>
31+
/// Specifies all public constructors.
32+
/// </summary>
33+
PublicConstructors = 0x0002 | global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicParameterlessConstructor,
34+
35+
/// <summary>
36+
/// Specifies all non-public constructors.
37+
/// </summary>
38+
NonPublicConstructors = 0x0004,
39+
40+
/// <summary>
41+
/// Specifies all public methods.
42+
/// </summary>
43+
PublicMethods = 0x0008,
44+
45+
/// <summary>
46+
/// Specifies all non-public methods.
47+
/// </summary>
48+
NonPublicMethods = 0x0010,
49+
50+
/// <summary>
51+
/// Specifies all public fields.
52+
/// </summary>
53+
PublicFields = 0x0020,
54+
55+
/// <summary>
56+
/// Specifies all non-public fields.
57+
/// </summary>
58+
NonPublicFields = 0x0040,
59+
60+
/// <summary>
61+
/// Specifies all public nested types.
62+
/// </summary>
63+
PublicNestedTypes = 0x0080,
64+
65+
/// <summary>
66+
/// Specifies all non-public nested types.
67+
/// </summary>
68+
NonPublicNestedTypes = 0x0100,
69+
70+
/// <summary>
71+
/// Specifies all public properties.
72+
/// </summary>
73+
PublicProperties = 0x0200,
74+
75+
/// <summary>
76+
/// Specifies all non-public properties.
77+
/// </summary>
78+
NonPublicProperties = 0x0400,
79+
80+
/// <summary>
81+
/// Specifies all public events.
82+
/// </summary>
83+
PublicEvents = 0x0800,
84+
85+
/// <summary>
86+
/// Specifies all non-public events.
87+
/// </summary>
88+
NonPublicEvents = 0x1000,
89+
90+
/// <summary>
91+
/// Specifies all interfaces implemented by the type.
92+
/// </summary>
93+
Interfaces = 0x2000,
94+
95+
/// <summary>
96+
/// Specifies all members.
97+
/// </summary>
98+
All = ~global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.None
99+
}
100+
}
101+
102+
#endif
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// <auto-generated/>
2+
#pragma warning disable
3+
#nullable enable annotations
4+
#if !NET5_0_OR_GREATER
5+
6+
// Licensed to the .NET Foundation under one or more agreements.
7+
// The .NET Foundation licenses this file to you under the MIT license.
8+
9+
namespace System.Diagnostics.CodeAnalysis
10+
{
11+
/// <summary>
12+
/// Indicates that certain members on a specified <see cref="global::System.Type"/> are accessed dynamically,
13+
/// for example through <see cref="global::System.Reflection"/>.
14+
/// </summary>
15+
/// <remarks>
16+
/// This allows tools to understand which members are being accessed during the execution
17+
/// of a program.
18+
///
19+
/// This attribute is valid on members whose type is <see cref="global::System.Type"/> or <see cref="string"/>.
20+
///
21+
/// When this attribute is applied to a location of type <see cref="string"/>, the assumption is
22+
/// that the string represents a fully qualified type name.
23+
///
24+
/// When this attribute is applied to a class, interface, or struct, the members specified
25+
/// can be accessed dynamically on <see cref="global::System.Type"/> instances returned from calling
26+
/// <see cref="object.GetType"/> on instances of that class, interface, or struct.
27+
///
28+
/// If the attribute is applied to a method it's treated as a special case and it implies
29+
/// the attribute should be applied to the "this" parameter of the method. As such the attribute
30+
/// should only be used on instance methods of types assignable to System.Type (or string, but no methods
31+
/// will use it there).
32+
/// </remarks>
33+
[global::System.AttributeUsage(
34+
global::System.AttributeTargets.Field |
35+
global::System.AttributeTargets.ReturnValue |
36+
global::System.AttributeTargets.GenericParameter |
37+
global::System.AttributeTargets.Parameter |
38+
global::System.AttributeTargets.Property |
39+
global::System.AttributeTargets.Method |
40+
global::System.AttributeTargets.Class |
41+
global::System.AttributeTargets.Interface |
42+
global::System.AttributeTargets.Struct,
43+
Inherited = false)]
44+
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
45+
[global::System.Diagnostics.Conditional("MULTI_TARGETING_SUPPORT_ATTRIBUTES")]
46+
internal sealed class DynamicallyAccessedMembersAttribute : global::System.Attribute
47+
{
48+
/// <summary>
49+
/// Initializes a new instance of the <see cref="global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute"/> class
50+
/// with the specified member types.
51+
/// </summary>
52+
/// <param name="memberTypes">The types of members dynamically accessed.</param>
53+
public DynamicallyAccessedMembersAttribute(global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes memberTypes)
54+
{
55+
MemberTypes = memberTypes;
56+
}
57+
58+
/// <summary>
59+
/// Gets the <see cref="global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes"/> which specifies the type
60+
/// of members dynamically accessed.
61+
/// </summary>
62+
public global::System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes MemberTypes { get; }
63+
}
64+
}
65+
66+
#endif

src/Transports.AspNetCore/Extensions/GraphQLBuilderExtensions.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
13
namespace GraphQL;
24

35
/// <summary>
@@ -12,7 +14,9 @@ public static class ServerGraphQLBuilderExtensions
1214
/// Requires <see cref="IHttpContextAccessor"/> to be registered within the dependency injection framework
1315
/// if calling <see cref="DocumentExecuter.ExecuteAsync(ExecutionOptions)"/> directly.
1416
/// </summary>
15-
public static IGraphQLBuilder AddUserContextBuilder<TUserContextBuilder>(this IGraphQLBuilder builder)
17+
public static IGraphQLBuilder AddUserContextBuilder<
18+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TUserContextBuilder>(
19+
this IGraphQLBuilder builder)
1620
where TUserContextBuilder : class, IUserContextBuilder
1721
{
1822
builder.Services.Register<IUserContextBuilder, TUserContextBuilder>(DI.ServiceLifetime.Singleton);
@@ -107,7 +111,9 @@ private static async Task<ExecutionResult> SetAndExecuteAsync(ExecutionOptions o
107111
/// Registers <typeparamref name="TWebSocketAuthenticationService"/> with the dependency injection framework
108112
/// as a singleton of type <see cref="IWebSocketAuthenticationService"/>.
109113
/// </summary>
110-
public static IGraphQLBuilder AddWebSocketAuthentication<TWebSocketAuthenticationService>(this IGraphQLBuilder builder)
114+
public static IGraphQLBuilder AddWebSocketAuthentication<
115+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TWebSocketAuthenticationService>(
116+
this IGraphQLBuilder builder)
111117
where TWebSocketAuthenticationService : class, IWebSocketAuthenticationService
112118
{
113119
builder.Services.Register<IWebSocketAuthenticationService, TWebSocketAuthenticationService>(DI.ServiceLifetime.Singleton);

src/Transports.AspNetCore/Extensions/GraphQLHttpApplicationBuilderExtensions.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
13
namespace Microsoft.AspNetCore.Builder;
24

35
/// <summary>
@@ -70,7 +72,9 @@ public static IApplicationBuilder UseGraphQL<TSchema>(this IApplicationBuilder b
7072
/// <param name="path">The path to the GraphQL endpoint which defaults to '/graphql'</param>
7173
/// <param name="args">The arguments to pass to the middleware type instance's constructor.</param>
7274
/// <returns>The <see cref="IApplicationBuilder"/> received as parameter</returns>
73-
public static IApplicationBuilder UseGraphQL<TMiddleware>(this IApplicationBuilder builder, string path = "/graphql", params object[] args)
75+
public static IApplicationBuilder UseGraphQL<
76+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)] TMiddleware>(
77+
this IApplicationBuilder builder, string path = "/graphql", params object[] args)
7478
where TMiddleware : GraphQLHttpMiddleware
7579
=> builder.UseGraphQL<TMiddleware>(new PathString(path), args);
7680

@@ -82,7 +86,9 @@ public static IApplicationBuilder UseGraphQL<TMiddleware>(this IApplicationBuild
8286
/// <param name="path">The path to the GraphQL endpoint</param>
8387
/// <param name="args">The arguments to pass to the middleware type instance's constructor.</param>
8488
/// <returns>The <see cref="IApplicationBuilder"/> received as parameter</returns>
85-
public static IApplicationBuilder UseGraphQL<TMiddleware>(this IApplicationBuilder builder, PathString path, params object[] args)
89+
public static IApplicationBuilder UseGraphQL<
90+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)] TMiddleware>(
91+
this IApplicationBuilder builder, PathString path, params object[] args)
8692
where TMiddleware : GraphQLHttpMiddleware
8793
{
8894
return builder.UseWhen(

src/Transports.AspNetCore/Extensions/GraphQLHttpEndpointRouteBuilderExtensions.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#if !NETSTANDARD2_0 && !NETCOREAPP2_1
22

3+
using System.Diagnostics.CodeAnalysis;
4+
35
namespace Microsoft.AspNetCore.Builder;
46

57
/// <summary>
@@ -47,7 +49,9 @@ public static GraphQLEndpointConventionBuilder MapGraphQL<TSchema>(this IEndpoin
4749
/// <param name="pattern">The route pattern.</param>
4850
/// <param name="args">The arguments to pass to the middleware type instance's constructor.</param>
4951
/// <returns>The <see cref="IApplicationBuilder"/> received as parameter</returns>
50-
public static GraphQLEndpointConventionBuilder MapGraphQL<TMiddleware>(this IEndpointRouteBuilder endpoints, string pattern = "graphql", params object[] args)
52+
public static GraphQLEndpointConventionBuilder MapGraphQL<
53+
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods)] TMiddleware>(
54+
this IEndpointRouteBuilder endpoints, string pattern = "graphql", params object[] args)
5155
where TMiddleware : GraphQLHttpMiddleware
5256
{
5357
var requestDelegate = endpoints.CreateApplicationBuilder().UseMiddleware<TMiddleware>(args).Build();

src/Transports.AspNetCore/Transports.AspNetCore.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
4+
<TargetFrameworks>netstandard2.0;netcoreapp2.1;netcoreapp3.1;net5.0;net6.0;net8.0</TargetFrameworks>
55
<Description>HTTP middleware for GraphQL</Description>
66
<PackageTags>GraphQL;middleware</PackageTags>
7+
<IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>
78
</PropertyGroup>
89

910
<ItemGroup>

src/Ui.Altair/Internal/AltairPageModel.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
using System.Text;
2+
#if NET7_0_OR_GREATER
3+
using System.Text.Json.Serialization;
4+
#endif
25

36
namespace GraphQL.Server.Ui.Altair.Internal;
47

@@ -58,12 +61,26 @@ private static string StringEncode(string value) => value
5861
.Replace("'", "\\'") // encode ' as \'
5962
.Replace("\"", "\\\""); // encode " as \"
6063

61-
private static string JsonSerialize(object? value)
64+
#nullable disable
65+
private static string JsonSerialize(Dictionary<string, object> value)
66+
#nullable restore
6267
{
6368
#if NETSTANDARD2_0
6469
return Newtonsoft.Json.JsonConvert.SerializeObject(value);
70+
#elif NET7_0_OR_GREATER
71+
return System.Text.Json.JsonSerializer.Serialize(value, SourceGenerationContext.Default.DictionaryStringObject);
6572
#else
6673
return System.Text.Json.JsonSerializer.Serialize(value);
6774
#endif
6875
}
6976
}
77+
78+
#if NET7_0_OR_GREATER
79+
[JsonSerializable(typeof(Dictionary<string, object>))]
80+
[JsonSerializable(typeof(bool))]
81+
[JsonSerializable(typeof(int))]
82+
[JsonSerializable(typeof(string))]
83+
internal partial class SourceGenerationContext : JsonSerializerContext
84+
{
85+
}
86+
#endif

0 commit comments

Comments
 (0)