How to declare decimal object in gRPC same as C#

Viewed 1885

We are converting our existing REST API service to gRPC core. While migrating existing classes understood that gRPC doesn't have a decimal datatype. We have a class in C# which is defined as

public class SalarySchedule
{
    public decimal Salary { get; set; }
    public DateTime? SalaryDate { get; set; }
}

And we implemented this in the proto file as

message SalarySchedule
{
    // TODO: How to define this double to decimal
    double Salary = 1;
    google.protobuf.Timestamp SalaryDate =2;
}

For now, we have used double for Salary datatype. But this is causing a problem in internal calculations.

Can you please guide us, How can we define it as a decimal in gRPC?

2 Answers

There was a proposed Money type that had some discussion, but hasn't gone anywhere as a "well known" protobuf type.

For now, honestly, I'd suggest just using string. I don't know whether you're using the Google implementation or protobuf-net.Grpc (which builds in it but allows "code first" usage), but if you're using the latter (protobuf-net.Grpc) and protobuf-net V3, you can use [CompatibilityLevel(...)] to specify level 300 or above, and it will treat decimal as though it were a string for serialization purposes. If you're using Google's .proto approach, I'd apply the conversion manually, making sure to use an invariant culture.

This question was answered by microsoft. Define a message DecimalValue:

// Example: 12345.6789 -> { units = 12345, nanos = 678900000 }
message DecimalValue {

    // Whole units part of the amount
    int64 units = 1;

    // Nano units of the amount (10^-9)
    // Must be same sign as units
    sfixed32 nanos = 2;
}

And then convert DecimalValue to decimal e.g. by using implicit operators:

public partial class DecimalValue {
    private const decimal NanoFactor = 1_000_000_000;
    public DecimalValue(long units, int nanos) {
        Units = units;
        Nanos = nanos;
    }

    public static implicit operator decimal(CustomTypes.DecimalValue grpcDecimal) 
        => grpcDecimal.Units + grpcDecimal.Nanos / NanoFactor; 

    public static implicit operator CustomTypes.DecimalValue(decimal value){
        var units = decimal.ToInt64(value);
        var nanos = decimal.ToInt32((value - units) * NanoFactor);
        return new CustomTypes.DecimalValue(units, nanos);
    }
}
Related