Can't use struct in protobuf-net

70 views Asked by At
public struct Candle //88 bytes struct
{
    [DataMember(Order = 1)] public long TimeStamp { get;  }
    [DataMember(Order = 2)] public decimal Open { get;  }
    [DataMember(Order = 3)] public decimal High { get;  }
    [DataMember(Order = 4)] public decimal Low { get;  }
    [DataMember(Order = 5)] public decimal Close { get;  }
    [DataMember(Order = 6)] public decimal Volume { get;  }
}

I have such a model, I can pass it in the form Task<IEnumerable<Candle>>

But I can’t pass it as IAsyncEnumerable<Candle>

but as soon as I change struct to class everything starts working, but I can't use it because of allocations

> System.TypeInitializationException : The type initializer for
> 'DefaultProxyCache`1' threw an exception.   ---->
> System.InvalidOperationException : Error obtaining client-helper
> 'ServerStreamingAsync' (from: 'IODataModule.Shared.GetRangeArgument',
> to: 'Domain.Candle'): GenericArguments[1], 'Domain.Candle', on
> 'System.Collections.Generic.IAsyncEnumerable`1[TResponse]
> ServerStreamingAsync[TRequest,TResponse](ProtoBuf.Grpc.CallContext
> ByRef, Grpc.Core.CallInvoker, Grpc.Core.Method`2[TRequest,TResponse],
> TRequest, System.String)' violates the constraint of type 'TResponse'.

I am try, to create wrapper like

[DataContract]
public class CandleDto
{
    [DataMember(Order = 1)]
    public ReadOnlyMemory<Candle> CandleBatch { get; set; }
}

and send it by IAsyncEnumerable

but it throw

> System.TypeInitializationException : The type initializer for
> 'DefaultProxyCache`1' threw an exception.   ---->
> System.Reflection.TargetInvocationException : Exception has been
> thrown by the target of an invocation.   ---->
> System.InvalidOperationException : No marshaller available for
> IODataModule.Shared.CandleDto

but it work for

[DataContract]
public class CandleDto
{
    [DataMember(Order = 1)]
    public ReadOnlyMemory<byte> CandleBatch { get; set; }
}

this is my contract

[ServiceContract]
public interface ICandleAccessServiceGrpc
{
    [OperationContract]
    IAsyncEnumerable<CandleDto> GetRangeStream(GetRangeArgument argument, CallContext context = default);
}
1

There are 1 answers

0
Marc Gravell On

This comes from an annoying : class constraint in the base gRPC bits; I have tried multiple times to have this constraint relaxed (I even did the impact analysis and code changes at least once), but; no joy yet. Protobuf-net could in theory add a fake box layer to work around it, but: to date I simply haven't had time to implement everything I would like to. Questions like this are very helpful for helping me prioritize.

For the second one: I'm not quite sure what you're trying to do with the ROM-byte - if this is intended as "punning", then maybe that's something we could do more efficiently for you inside the library, but: punning isn't exactly 100% pure protobuf, ans won't have the same correctness guarantees (especially endianness between CPU architectures). However, IIRC it should work - check whether you're using the correct V3 version of protobuf-net (it won't work in V2).