dotnet / orleans

Cloud Native application framework for .NET
https://docs.microsoft.com/dotnet/orleans
MIT License
10.08k stars 2.03k forks source link

Codec not found even though IConverter created #8244

Open zpxp opened 1 year ago

zpxp commented 1 year ago

Im getting a CodecNotFoundException when trying to serialize a generic struct type from an external lib, OneOf<GameCardModel, GamePlayerModel> https://www.nuget.org/packages/OneOf

Here is my IConverter

namespace Grains.Surrogates;

[GenerateSerializer]
public struct OneOfSurrogate
{
    [Id(0)] public GameCardModel? V0;
    [Id(1)] public GamePlayerModel? V1;
}

// Implement the IConverter 
[RegisterConverter]
public sealed class OneOfSurrogateConverter : IConverter<global::OneOf.OneOf<global::Game.Core.GameCardModel, global::Game.Core.GamePlayerModel>, OneOfSurrogate>
{
    public OneOf<GameCardModel, GamePlayerModel> ConvertFromSurrogate(in OneOfSurrogate surrogate)
        => surrogate.V0 != null
            ? OneOf<GameCardModel, GamePlayerModel>.FromT0(surrogate.V0)
            : OneOf<GameCardModel, GamePlayerModel>.FromT1(surrogate.V1!);

    public OneOfSurrogate ConvertToSurrogate(in OneOf<GameCardModel, GamePlayerModel> value)
        => new()
        {
            V0 = value.IsT0 ? value.AsT0 : null,
            V1 = value.IsT1 ? value.AsT1 : null,
        };
}

And here is the generated orleans code where the error originates

            _codec0 = OrleansGeneratedCodeHelper.GetService<global::Orleans.Serialization.Codecs.IFieldCodec<global::OneOf.OneOf<global::Game.Core.GameCardModel, global::Game.Core.GamePlayerModel>>>(this, codecProvider);

The IConverter is in the same assembly as the grains and the sdk is installed

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.DataProtection.EntityFrameworkCore" Version="7.0.1" />
    <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="7.0.1" />
    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
    <PackageReference Include="Microsoft.Orleans.BroadcastChannel" Version="7.0.0" />
    <PackageReference Include="Microsoft.Orleans.Core.Abstractions" Version="7.0.0" />
    <PackageReference Include="Microsoft.Orleans.Reminders" Version="7.0.0" />
    <PackageReference Include="Microsoft.Orleans.Sdk" Version="7.0.0" />
    <PackageReference Include="Microsoft.Orleans.Serialization.SystemTextJson" Version="7.0.0" />
    <PackageReference Include="Microsoft.Orleans.Streaming" Version="7.0.0" />
    <PackageReference Include="Orleans.Reminders.Redis" Version="7.0.0" />
    <PackageReference Include="Polly" Version="7.2.3" />
    <PackageReference Include="PuppeteerSharp" Version="8.0.0" />
    <PackageReference Include="Automapper" Version="12.0.0" />
    <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" />
    <PackageReference Include="EFCore.NamingConventions" Version="7.0.0" />
    <PackageReference Include="Hangfire.AspNetCore" Version="1.7.32" />
    <PackageReference Include="Hangfire.Core" Version="1.7.32" />
    <PackageReference Include="Hangfire.PostgreSql" Version="1.9.9" />
    <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
    <PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" Version="7.0.1" />
    <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="7.0.1" />
    <PackageReference Include="StackExchange.Redis" Version="2.6.86" />
  </ItemGroup>

I have another surrogate in the code base which works fine for a non generic struct type so im not sure what the issue is here.

zpxp commented 1 year ago

Both the GameCardModel and GamePlayerModel are poco classes decorated with [GenerateSerializer(GenerateFieldIds = GenerateFieldIds.PublicProperties)]

zpxp commented 1 year ago

Update: If i change the converter and surrogates to use generics it now works

namespace Grains.Surrogates;

[GenerateSerializer]
public struct OneOfSurrogate<T0,T1>
{
    [Id(0)] public T0? V0;
    [Id(1)] public T1? V1;
}

// Implement the IConverter and IPopulator interfaces on the converter.

[RegisterConverter]
public sealed class OneOfSurrogateConverter<T0,T1> : IConverter<OneOf<T0,T1>, OneOfSurrogate<T0,T1>>
{
    public OneOf<T0, T1> ConvertFromSurrogate(in OneOfSurrogate<T0, T1> surrogate)
        => surrogate.V0 != null
            ? OneOf<T0, T1>.FromT0(surrogate.V0)
            : OneOf<T0, T1>.FromT1(surrogate.V1!);

    public OneOfSurrogate<T0, T1> ConvertToSurrogate(in OneOf<T0, T1> value)
        => new()
        {
            V0 = value.IsT0 ? value.AsT0 : default,
            V1 = value.IsT1 ? value.AsT1 : default,
        };
}
ReubenBond commented 1 year ago

This is a bug, we should support this and you're not the first to hit it. The generic approach which you ended up with is indeed a better approach. Also, we should consider creating an OrleansContrib repo to host serializers for various packages, such as this one.