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

Support Native AOT #1348

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
<Project>
<ItemGroup Label="Libraries">
<PackageVersion Include="AWSSDK.Extensions.NETCore.Setup" Version="3.7.300" />
<PackageVersion Include="AWSSDK.SimpleNotificationService" Version="3.7.0" />
<PackageVersion Include="AWSSDK.SQS" Version="3.7.0" />
<PackageVersion Include="AWSSDK.SimpleNotificationService" Version="3.7.300" />
<PackageVersion Include="AWSSDK.SQS" Version="3.7.300" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="1.1.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="1.1.0" Condition=" '$(TargetFramework)' == 'net461' " />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.0.0" Condition=" '$(TargetFramework)' == 'netstandard2.0' " />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" Condition=" '$(TargetFramework)' == 'net8.0' " />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="8.0.0" Condition=" '$(TargetFramework)' == 'net8.0' " />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
<PackageVersion Include="StructureMap" Version="4.6.0" />
<PackageVersion Include="System.Text.Json" Version="4.6.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;
using JustSaying.Sample.Restaurant.Models;
using JustSaying.Sample.Restaurant.OrderingApi.Models;

namespace JustSaying.Sample.Restaurant.OrderingApi;

[JsonSerializable(typeof(CustomerOrderModel))]
[JsonSerializable(typeof(OrderPlacedEvent))]
[JsonSerializable(typeof(OrderReadyEvent))]
[JsonSerializable(typeof(OrderDeliveredEvent))]
[JsonSerializable(typeof(OrderOnItsWayEvent))]
public sealed partial class ApplicationJsonContext : JsonSerializerContext;
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@
</ItemGroup>

<ItemGroup>
<Content Update="appsettings.json" CopyToOutputDirectory="PreserveNewest" />
<Content Update="appsettings.json" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
</ItemGroup>

<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@HostAddress = http://localhost:5001

POST {{HostAddress}}/api/orders
Content-Type: application/json
Accept: application/json

{
"description": "A cool description"
}

###
13 changes: 13 additions & 0 deletions samples/src/JustSaying.Sample.Restaurant.OrderingApi/Program.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using System.Text.Json;
using JustSaying.Messaging;
using JustSaying.Messaging.MessageSerialization;
using JustSaying.Sample.Restaurant.Models;
using JustSaying.Sample.Restaurant.OrderingApi;
using JustSaying.Sample.Restaurant.OrderingApi.Handlers;
using JustSaying.Sample.Restaurant.OrderingApi.Models;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Serilog;
using Serilog.Events;
Expand All @@ -23,6 +26,16 @@
var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
builder.Host.UseSerilog();
builder.Services.ConfigureHttpJsonOptions(cfg =>
{
cfg.SerializerOptions.TypeInfoResolverChain.Insert(0, ApplicationJsonContext.Default);
});

builder.Services.Configure<JsonSerializerOptions>(cfg =>
{
cfg.TypeInfoResolverChain.Insert(0, ApplicationJsonContext.Default);
});

builder.Services.AddJustSaying(config =>
{
config.Client(x =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"AWSRegion": "eu-west-1",
"AWSServiceUrl": "http://localhost:4100",
"AWSServiceUrl": "http://localhost:4566",
"AllowedHosts": "*"
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
using JustSaying.Models;
using JustSaying.Naming;
using Microsoft.Extensions.DependencyInjection.Extensions;
#if NET8_0_OR_GREATER
using System.Text.Json;
using Microsoft.Extensions.Options;
using System.Diagnostics.CodeAnalysis;
#endif

namespace Microsoft.Extensions.DependencyInjection;

Expand Down Expand Up @@ -61,7 +66,7 @@

if (string.IsNullOrWhiteSpace(region))
{
throw new ArgumentException("region must not be null or empty" ,nameof(region));
throw new ArgumentException("region must not be null or empty", nameof(region));

Check warning on line 69 in src/JustSaying.Extensions.DependencyInjection.Microsoft/IServiceCollectionExtensions.cs

View check run for this annotation

Codecov / codecov/patch

src/JustSaying.Extensions.DependencyInjection.Microsoft/IServiceCollectionExtensions.cs#L69

Added line #L69 was not covered by tests
}

return services.AddJustSaying(
Expand Down Expand Up @@ -135,7 +140,11 @@
services.TryAddSingleton<IMessageContextAccessor>(serviceProvider => serviceProvider.GetRequiredService<MessageContextAccessor>());
services.TryAddSingleton<IMessageContextReader>(serviceProvider => serviceProvider.GetRequiredService<MessageContextAccessor>());

#if NET8_0_OR_GREATER
services.TryAddSingleton<IMessageSerializationFactory>(sp => new TypedSystemTextJsonSerializationFactory(sp.GetRequiredService<IOptions<JsonSerializerOptions>>().Value));

Check warning on line 144 in src/JustSaying.Extensions.DependencyInjection.Microsoft/IServiceCollectionExtensions.cs

View check run for this annotation

Codecov / codecov/patch

src/JustSaying.Extensions.DependencyInjection.Microsoft/IServiceCollectionExtensions.cs#L144

Added line #L144 was not covered by tests
martincostello marked this conversation as resolved.
Show resolved Hide resolved
#else
services.TryAddSingleton<IMessageSerializationFactory, NewtonsoftSerializationFactory>();
#endif
services.TryAddSingleton<IMessageSubjectProvider, GenericMessageSubjectProvider>();
services.TryAddSingleton<IVerifyAmazonQueues, AmazonQueueCreator>();
services.TryAddSingleton<IMessageSerializationRegister>(
Expand Down Expand Up @@ -199,7 +208,11 @@
/// <exception cref="ArgumentNullException">
/// <paramref name="services"/> is <see langword="null"/>.
/// </exception>
#if NET8_0_OR_GREATER
public static IServiceCollection AddJustSayingHandler<TMessage, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] THandler>(this IServiceCollection services)
#else
public static IServiceCollection AddJustSayingHandler<TMessage, THandler>(this IServiceCollection services)
#endif
where TMessage : Message
where THandler : class, IHandlerAsync<TMessage>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IsShipping>true</IsShipping>
<RootNamespace>Microsoft.Extensions.DependencyInjection</RootNamespace>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net461;net8.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\JustSaying\JustSaying.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Options" Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)','net8.0'))" />
</ItemGroup>
<ItemGroup>
<Using Remove="System.Net.Http" />
</ItemGroup>
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)','net8.0'))">
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
martincostello marked this conversation as resolved.
Show resolved Hide resolved
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Microsoft.Extensions.DependencyInjection.IServiceCollectionExtensions
static Microsoft.Extensions.DependencyInjection.IServiceCollectionExtensions.AddJustSaying(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.Extensions.DependencyInjection.IServiceCollectionExtensions.AddJustSaying(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, string region) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.Extensions.DependencyInjection.IServiceCollectionExtensions.AddJustSaying(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<JustSaying.MessagingBusBuilder, System.IServiceProvider> configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.Extensions.DependencyInjection.IServiceCollectionExtensions.AddJustSaying(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<JustSaying.MessagingBusBuilder> configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.Extensions.DependencyInjection.IServiceCollectionExtensions.AddJustSayingHandler<TMessage, THandler>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.Extensions.DependencyInjection.IServiceCollectionExtensions.AddJustSayingHandlers<TMessage>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Collections.Generic.IEnumerable<JustSaying.Messaging.MessageHandling.IHandlerAsync<TMessage>> handlers) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
static Microsoft.Extensions.DependencyInjection.IServiceCollectionExtensions.ConfigureJustSaying(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<JustSaying.MessagingBusBuilder> configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
2 changes: 1 addition & 1 deletion src/JustSaying.Models/JustSaying.Models.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsShipping>true</IsShipping>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net461;net8.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Using Remove="System.Net.Http" />
Expand Down
15 changes: 15 additions & 0 deletions src/JustSaying.Models/PublicAPI/net8.0/PublicAPI.Shipped.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
JustSaying.Models.Message
JustSaying.Models.Message.Conversation.get -> string
JustSaying.Models.Message.Conversation.set -> void
JustSaying.Models.Message.Id.get -> System.Guid
JustSaying.Models.Message.Id.set -> void
JustSaying.Models.Message.Message() -> void
JustSaying.Models.Message.RaisingComponent.get -> string
JustSaying.Models.Message.RaisingComponent.set -> void
JustSaying.Models.Message.SourceIp.get -> string
JustSaying.Models.Message.Tenant.get -> string
JustSaying.Models.Message.Tenant.set -> void
JustSaying.Models.Message.TimeStamp.get -> System.DateTime
JustSaying.Models.Message.TimeStamp.set -> void
JustSaying.Models.Message.Version.get -> string
virtual JustSaying.Models.Message.UniqueKey() -> string
Empty file.
4 changes: 3 additions & 1 deletion src/JustSaying/AwsTools/MessageHandling/PublishException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public PublishException(string message, Exception inner) : base(message, inner)
{
}

#if !NET8_0_OR_GREATER
martincostello marked this conversation as resolved.
Show resolved Hide resolved
protected PublishException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
#endif
}
12 changes: 11 additions & 1 deletion src/JustSaying/AwsTools/MessageHandling/SnsPolicyBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Text.Json;
using Amazon;
using JustSaying.Messaging.MessageSerialization;

namespace JustSaying.AwsTools.MessageHandling;

Expand Down Expand Up @@ -41,12 +42,21 @@ internal static string BuildPolicyJson(SnsPolicyDetails policyDetails)
""Sid"" : ""{Guid.NewGuid().ToString().Replace("-", "")}"",
""Effect"" : ""Allow"",
""Principal"" : {{
""AWS"" : {JsonSerializer.Serialize(policyDetails.AccountIds)}
""AWS"" : {SerializeAccountIds(policyDetails.AccountIds)}
}},
""Action"" : ""sns:Subscribe"",
""Resource"" : ""{policyDetails.SourceArn}""
}}
]
}}";
}

private static string SerializeAccountIds(IReadOnlyCollection<string> accountIds)
{
#if NET8_0_OR_GREATER
return JsonSerializer.Serialize(accountIds, JustSayingSerializationContext.Default.IReadOnlyCollectionString);
#else
return JsonSerializer.Serialize(accountIds);
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public ConfigurationErrorsException(string message, Exception inner) : base(mess
{
}

#if !NET8_0_OR_GREATER
protected ConfigurationErrorsException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
#endif
}
15 changes: 12 additions & 3 deletions src/JustSaying/AwsTools/QueueCreation/RedrivePolicy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public class RedrivePolicy
[JsonPropertyName("deadLetterTargetArn")]
public string DeadLetterQueue { get; set; }

#if NET8_0_OR_GREATER
[System.Text.Json.Serialization.JsonConstructor]
#endif
public RedrivePolicy(int maximumReceives, string deadLetterQueue)
{
MaximumReceives = maximumReceives;
Expand All @@ -23,11 +26,17 @@ protected RedrivePolicy()
{
}

// Cannot use System.Text.Json below as no public parameterless constructor. Change for v7?

public override string ToString()
#if NET8_0_OR_GREATER
=> System.Text.Json.JsonSerializer.Serialize(this, JustSayingSerializationContext.Default.RedrivePolicy);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this might be problematic for AoT in case it's a derived type.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's a good point, I think it'll end up serializing the base fields, but none added by the derived type.
At the moment, RedrivePolicy is only used internally, and I can't see a way that an API user could derive it and pass it through to any internal usage, but I could be missing it.

If that is the case, I don't think it would be a bad idea to internal sealed it.

#else
=> JsonConvert.SerializeObject(this);
#endif

public static RedrivePolicy ConvertFromString(string policy)
#if NET8_0_OR_GREATER
=> System.Text.Json.JsonSerializer.Deserialize(policy, JustSayingSerializationContext.Default.RedrivePolicy);
#else
=> JsonConvert.DeserializeObject<RedrivePolicy>(policy);
}
#endif
}
7 changes: 7 additions & 0 deletions src/JustSaying/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace JustSaying;

internal static class Constants
{
internal const string SerializationUnreferencedCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.";
internal const string SerializationDynamicCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed. Use the overload that takes a JsonTypeInfo or JsonSerializerContext.";
}
26 changes: 26 additions & 0 deletions src/JustSaying/Extensions/JsonSerializerOptionsExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#if NET8_0_OR_GREATER
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;

namespace JustSaying.Extensions;


internal static class JsonSerializerOptionsExtensions
{
public static JsonTypeInfo<T> GetTypeInfo<T>(this JsonSerializerOptions options)
{
foreach (var info in options.TypeInfoResolverChain)
{
Console.WriteLine(info);

Check warning on line 14 in src/JustSaying/Extensions/JsonSerializerOptionsExtensions.cs

View check run for this annotation

Codecov / codecov/patch

src/JustSaying/Extensions/JsonSerializerOptionsExtensions.cs#L14

Added line #L14 was not covered by tests
}

var typeInfo = options.GetTypeInfo(typeof(T));

Check warning on line 17 in src/JustSaying/Extensions/JsonSerializerOptionsExtensions.cs

View check run for this annotation

Codecov / codecov/patch

src/JustSaying/Extensions/JsonSerializerOptionsExtensions.cs#L17

Added line #L17 was not covered by tests
if (typeInfo is not JsonTypeInfo<T> genericTypeInfo)
{
throw new JsonException($"Could not find type info for the specified type {typeof(T).Name}");

Check warning on line 20 in src/JustSaying/Extensions/JsonSerializerOptionsExtensions.cs

View check run for this annotation

Codecov / codecov/patch

src/JustSaying/Extensions/JsonSerializerOptionsExtensions.cs#L20

Added line #L20 was not covered by tests
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could probably be a bit more informative.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It ends up being redundant anyway tbh, GetTypeInfo(Type) throws if it can't find the type info anyway.

}

return genericTypeInfo;

Check warning on line 23 in src/JustSaying/Extensions/JsonSerializerOptionsExtensions.cs

View check run for this annotation

Codecov / codecov/patch

src/JustSaying/Extensions/JsonSerializerOptionsExtensions.cs#L23

Added line #L23 was not covered by tests
}
}
#endif
4 changes: 3 additions & 1 deletion src/JustSaying/Fluent/AccountAddressProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,12 @@ public Uri GetQueueUriByConvention<T>()
/// <returns>The <see cref="Uri"/> for this queue.</returns>
public Uri GetQueueUri(string queueName)
{
#pragma warning disable CS0618 // Type or member is obsolete
var hostname = _regionEndpoint.GetEndpointForService("sqs").Hostname;
#pragma warning restore CS0618 // Type or member is obsolete
return new UriBuilder("https", hostname)
{
Path = $"{_accountId}/{queueName}"
}.Uri;
}
}
}
4 changes: 3 additions & 1 deletion src/JustSaying/Fluent/QueueAddress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ public static QueueAddress FromArn(string queueArn)
if (!Arn.TryParse(queueArn, out var arn)) throw new ArgumentException("Must be a valid ARN.", nameof(queueArn));
if (!string.Equals(arn.Service, "sqs", StringComparison.OrdinalIgnoreCase)) throw new ArgumentException("Must be an ARN for an SQS queue.", nameof(queueArn));

#pragma warning disable CS0618 // Type or member is obsolete
var hostname = RegionEndpoint.GetBySystemName(arn.Region)
.GetEndpointForService("sqs")
.Hostname;
#pragma warning restore CS0618 // Type or member is obsolete

var queueUrl = new UriBuilder("https", hostname)
{
Expand All @@ -94,4 +96,4 @@ public static QueueAddress FromArn(string queueArn)
RegionName = arn.Region
};
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Runtime.CompilerServices;
using JustSaying.AwsTools;
using JustSaying.Messaging.MessageSerialization;
using JustSaying.Messaging.Monitoring;
Expand Down Expand Up @@ -44,7 +45,22 @@
}
else if (desiredType == typeof(IMessageSerializationFactory))
{

#if NET8_0_OR_GREATER
if (RuntimeFeature.IsDynamicCodeSupported)
{
#pragma warning disable IL2026
#pragma warning disable IL3050
return new NewtonsoftSerializationFactory();
#pragma warning restore
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slightly surprised it isn't smart enough to not warn about this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll check, I think I had the check backwards when I added the #pragma warning disable's

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, even with that fixed, it still warns.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've found an issue related to this: dotnet/runtime#97273, looks like the supression in runtime checks just didn't make it into .NET 8, not sure if they plan to backport either.

It also looks like there are plans for a wider design proposal to support feature checks suppressing analyzers: dotnet/runtime#96859

else
{
throw new NotSupportedException($"Newtonsoft.Json is not supported when compiled with the 'PublishTrimmed' option. Use {nameof(TypedSystemTextJsonSerializationFactory)} instead.");

Check warning on line 59 in src/JustSaying/Fluent/ServiceResolver/DefaultServiceResolver.cs

View check run for this annotation

Codecov / codecov/patch

src/JustSaying/Fluent/ServiceResolver/DefaultServiceResolver.cs#L59

Added line #L59 was not covered by tests
}
#else
return new NewtonsoftSerializationFactory();
#endif
}
else if (desiredType == typeof(IMessageSerializationRegister))
{
Expand All @@ -63,4 +79,4 @@

return null;
}
}
}
2 changes: 2 additions & 0 deletions src/JustSaying/HandlerNotRegisteredWithContainerException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public HandlerNotRegisteredWithContainerException(string message, Exception inne
{
}

#if !NET8_0_OR_GREATER
protected HandlerNotRegisteredWithContainerException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
#endif
}
Loading
Loading