Search code examples
c#enumstype-conversiongrpc

Mapping a gRPC repeated field of type enum to a c# record list property with the same enum type


I have a gRPC proto file with a message schema like this:

message FilterRequestDto {
    string AccountId = 1;
    repeated ComponentType ComponentTypes = 2;
    repeated string Inverters = 3;
}

where "ComponentType" is an gRPC enum like this:

enum ComponentType {
    INVERTER = 0;
    BATTERYSTORAGE = 1;
}

In my .net application, I would like to map an incoming gRPC request of type message FilterRequestDto to a record of type FilterDto, which looks like this:

public record FilterDto
{
    public string AccountId { get; init; }
    public List<string>? Inverters { get; init; }
    public List<ComponentType>? ComponentTypes { get; init; }
}

where "ComponentType" is an c# enum like this:

public enum ComponentType
{
    Inverter,
    BatteryStorage,
}

For the Inverters i managed the parsing, but unfortunately i can't figure out the conversion for the ComponentTypes.

My mapping function looks like this:

public static FilterDto ToFilterDto(this FilterRequest request)
{
    return new FilterDto
    {
        AccountId = request.AccountId,
        Inverters = Enumerable.ToList<string>(request.Inverters).ToList(),
        ComponentTypes = ????
}

Thanks in advance.


Solution

  • It should work with just:

    public static FilterDto ToFilterDto(this FilterRequestDto request)
        => new FilterDto {
                AccountId = request.AccountId,
                Inverters = request.Inverters.ToList(),
                ComponentTypes = request.ComponentTypes.Select(
                     static comp => (ComponentType)comp).ToList(),
            };
    

    The static comp => (ComponentType)comp is a no-op conversion that effectively just changes the declared enum type, since the values match.

    As an aside: if you wanted to skip this step, your existing model should also be wire-compatible with the code-first gRPC approach using protobuf-net, just by adding some decoration:

        [ProtoContract]
        public record FilterDto
        {
            [ProtoMember(1)]
            public string AccountId { get; init; }
            [ProtoMember(2)]
            public List<string>? Inverters { get; init; }
            [ProtoMember(3)]
            public List<ComponentType>? ComponentTypes { get; init; }
        }
    

    you'd have to use a code-first service, too; more info - but something like:

    [Service("Some.Qualified.ServiceName")]
    public interface ISomeService {
        async Task<SomeResponseType> SomeMethodAsync(FilterDto request,
            CallContext context = default);
    }
    class MyService : ISomeService {...your server implementation...}