Closed kfrancis closed 2 years ago
Any ideas?
Not off the top of my head. Could you provide an example of how you're using the id in your classes. It looks like the id is trying to be used as a dictionary key in JSON serialization; that seems unusual...
I'll close this as it's stale, but if you're still running into it, feel free to reopen
I ran into this issue too when including a Strongly Typed ID as a dictionary key in the API request/response.
Package version: 1.0.0-beta06
See this minimal API Program.cs
for reproduction:
using StronglyTypedIds;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var response = new Dictionary<UserId, int>();
response[UserId.New()] = 1;
app.MapPost("/", (IDictionary<UserId, int> request) => request);
app.Run();
[StronglyTypedId(converters: StronglyTypedIdConverter.SystemTextJson)]
partial struct UserId
{
}
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.NotSupportedException: The type 'UserId' is not a supported dictionary key using converter of type 'UserId+UserIdSystemTextJsonConverter'. The unsupported member type is located on type 'System.Collections.Generic.Dictionary`2[UserId,System.Int32]'. Path: $ | LineNumber: 1 | BytePositionInLine: 43.
---> System.NotSupportedException: The type 'UserId' is not a supported dictionary key using converter of type 'UserId+UserIdSystemTextJsonConverter'.
at System.Text.Json.ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(Type keyType, JsonConverter converter)
at System.Text.Json.Serialization.JsonConverter`1.ReadAsPropertyName(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
at System.Text.Json.Serialization.JsonConverter`1.ReadAsPropertyNameCore(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options)
at System.Text.Json.Serialization.JsonDictionaryConverter`3.<OnTryRead>g__ReadDictionaryKey|10_0(Utf8JsonReader& reader, ReadStack& state, <>c__DisplayClass10_0& )
at System.Text.Json.Serialization.JsonDictionaryConverter`3.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TDictionary& value)
at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
--- End of inner exception stack trace ---
at System.Text.Json.ThrowHelper.ThrowNotSupportedException(ReadStack& state, Utf8JsonReader& reader, NotSupportedException ex)
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.Serialization.JsonConverter`1.ReadCoreAsObject(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonConverter jsonConverter, Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
at System.Text.Json.JsonSerializer.ReadCore[TValue](JsonReaderState& readerState, Boolean isFinalBlock, ReadOnlySpan`1 buffer, JsonSerializerOptions options, ReadStack& state, JsonConverter converterBase)
at System.Text.Json.JsonSerializer.ContinueDeserialize[TValue](ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack, JsonConverter converter, JsonSerializerOptions options)
at System.Text.Json.JsonSerializer.ReadAllAsync[TValue](Stream utf8Json, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, Type type, JsonSerializerOptions options, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, Type type, JsonSerializerOptions options, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass46_3.<<HandleRequestBodyAndCompileRequestDelegate>b__2>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
The generated System.Text.Json
converter can be updated to support this use-case now that custom converters can read & write dictionary keys (see https://github.com/dotnet/runtime/issues/46520):
using System.Text.Json;
using System.Text.Json.Serialization;
using StronglyTypedIds;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var response = new Dictionary<UserId, int>();
response[UserId.New()] = 1;
app.MapPost("/", (IDictionary<UserId, int> request) => request);
app.Run();
[StronglyTypedId(converters: StronglyTypedIdConverter.None)]
[JsonConverter(typeof(UserIdSystemTextJsonConverter))]
partial struct UserId
{
class UserIdSystemTextJsonConverter : System.Text.Json.Serialization.JsonConverter<UserId>
{
public override UserId Read(ref System.Text.Json.Utf8JsonReader reader, System.Type typeToConvert, System.Text.Json.JsonSerializerOptions options)
{
return new UserId(System.Guid.Parse(reader.GetString()));
}
public override void Write(System.Text.Json.Utf8JsonWriter writer, UserId value, System.Text.Json.JsonSerializerOptions options)
{
writer.WriteStringValue(value.Value);
}
public override void WriteAsPropertyName(Utf8JsonWriter writer, UserId value, JsonSerializerOptions options)
{
writer.WritePropertyName(value.Value.ToString());
}
public override UserId ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return new UserId(System.Guid.Parse(reader.GetString()));
}
}
}
I've not been able to test this issue with Newtonsoft so I'm not sure if it even affects that converter nor what a potential solution would be.
What are your thoughts on this @andrewlock? Is this use-case unusual and should we be using the underlying Guid
type as dictionary keys in our APIs or is this something that could be included in the library?
Ran into this issue as well. I dont see why this shouldn't be supported, seems like a good use-case to me.
@andrewlock any thoughts on reconsidering this?
Just ran into this as well. I think it should be at least implemented for string types and possibly opt in for numerics
I'm not actually using Andrews library, as I had need for a .Net 5 solution (which has since evolved to .Net 7 as of writing). To that end I created something similar, although in a different fashion using inheritance and generics instead of a source generator. Incidentally this has opened quite a few possibilities up as well I've just today stumbled upon this very issue and resolved it in that library. If you're up for trying something different out (that aims to solve the same problem), you'll find it here: https://github.com/steffenskov/StrongTypedId
Other than that I can only support the solution that NxSoftware has posted, it does indeed solve the problem :+1:
We love the idea of strongly typed ids and we're using the library.
When trying to finally get this working in a generic repository, efcore, json to client side scenario - we're getting the following:
This is defined like the follow:
We're using the StronglyTypedId library, v0.2.1