Exemplo n.º 1
0
        // Note: the options can *currently* be null because we're not using them, but
        // every call site should check that it could provide options if they become required.
        internal Value ToProtobufValue(object value, SpannerConversionOptions options)
        {
            if (value == null || value is DBNull)
            {
                return(Value.ForNull());
            }

            // If we add any other special values, we can create an interface to delegate to instead.
            if (value is CommitTimestamp ts)
            {
                return(ts.ToProtobufValue(this));
            }

            switch (TypeCode)
            {
            case TypeCode.Bytes:
                if (value is string s)
                {
                    return(new Value {
                        StringValue = s
                    });
                }
                if (value is byte[] bArray)
                {
                    return(new Value {
                        StringValue = Convert.ToBase64String(bArray)
                    });
                }
                throw new ArgumentException("TypeCode.Bytes only supports string and byte[]", nameof(value));

            case TypeCode.Bool:
                return(new Value {
                    BoolValue = Convert.ToBoolean(value)
                });

            case TypeCode.String:
                if (value is DateTime dateTime)
                {
                    // If the value is a DateTime, we always convert using XmlConvert.
                    // This allows us to convert back to a datetime reliably from the
                    // resulting string (so roundtrip works properly if the developer uses
                    // a string as a backing field for a datetime for whatever reason).
                    return(new Value {
                        StringValue = XmlConvert.ToString(dateTime, XmlDateTimeSerializationMode.Utc)
                    });
                }
                // All the other conversions will fail naturally, but let's make sure we don't convert structs
                // to strings.
                if (value is SpannerStruct)
                {
                    throw new ArgumentException("SpannerStruct cannot be used for string parameters", nameof(value));
                }
                return(new Value {
                    StringValue = Convert.ToString(value, InvariantCulture)
                });

            case TypeCode.Int64:
                return(new Value {
                    StringValue = Convert.ToInt64(value, InvariantCulture)
                                  .ToString(InvariantCulture)
                });

            case TypeCode.Float64:
                return(new Value {
                    NumberValue = Convert.ToDouble(value, InvariantCulture)
                });

            case TypeCode.Timestamp:
                return(new Value
                {
                    StringValue = XmlConvert.ToString(Convert.ToDateTime(value, InvariantCulture), XmlDateTimeSerializationMode.Utc)
                });

            case TypeCode.Date:
                return(new Value
                {
                    StringValue = StripTimePart(
                        XmlConvert.ToString(Convert.ToDateTime(value, InvariantCulture), XmlDateTimeSerializationMode.Utc))
                });

            case TypeCode.Array:
                if (value is IEnumerable enumerable)
                {
                    return(Value.ForList(
                               enumerable.Cast <object>()
                               .Select(x => ArrayElementType.ToProtobufValue(x, options)).ToArray()));
                }
                throw new ArgumentException("The given array instance needs to implement IEnumerable.");

            case TypeCode.Struct:
                if (value is SpannerStruct spannerStruct)
                {
                    return(new Value
                    {
                        ListValue = new ListValue
                        {
                            Values = { spannerStruct.Select(f => f.Type.ToProtobufValue(f.Value, options)) }
                        }
                    });
                }
                throw new ArgumentException("Struct parameters must be of type SpannerStruct");

            case TypeCode.Numeric:
                if (value is SpannerNumeric spannerNumeric)
                {
                    return(Value.ForString(spannerNumeric.ToString()));
                }
                if (value is string str)
                {
                    return(Value.ForString(SpannerNumeric.Parse(str).ToString()));
                }
                if (value is float || value is double || value is decimal)
                {
                    // We throw if there's a loss of precision. We could use
                    // LossOfPrecisionHandling.Truncate but GoogleSQL documentation requests to
                    // use half-away-from-zero rounding but the SpannerNumeric implementation
                    // truncates instead.
                    return(Value.ForString(SpannerNumeric.FromDecimal(
                                               Convert.ToDecimal(value, InvariantCulture), LossOfPrecisionHandling.Throw).ToString()));
                }
                if (value is sbyte || value is short || value is int || value is long)
                {
                    SpannerNumeric numericValue = Convert.ToInt64(value, InvariantCulture);
                    return(Value.ForString(numericValue.ToString()));
                }
                if (value is byte || value is ushort || value is uint || value is ulong)
                {
                    SpannerNumeric numericValue = Convert.ToUInt64(value, InvariantCulture);
                    return(Value.ForString(numericValue.ToString()));
                }
                throw new ArgumentException("Numeric parameters must be of type SpannerNumeric or string");

            default:
                throw new ArgumentOutOfRangeException(nameof(TypeCode), TypeCode, null);
            }
        }
Exemplo n.º 2
0
        internal Value ToProtobufValue(object value)
        {
            if (value == null || value is DBNull)
            {
                return(Value.ForNull());
            }

            switch (TypeCode)
            {
            case TypeCode.Bytes:
                if (value is string s)
                {
                    return(new Value {
                        StringValue = s
                    });
                }
                if (value is byte[] bArray)
                {
                    return(new Value {
                        StringValue = Convert.ToBase64String(bArray)
                    });
                }
                throw new ArgumentException("TypeCode.Bytes only supports string and byte[]", nameof(value));

            case TypeCode.Bool:
                return(new Value {
                    BoolValue = Convert.ToBoolean(value)
                });

            case TypeCode.String:
                if (value is DateTime dateTime)
                {
                    // If the value is a DateTime, we always convert using XmlConvert.
                    // This allows us to convert back to a datetime reliably from the
                    // resulting string (so roundtrip works properly if the developer uses
                    // a string as a backing field for a datetime for whatever reason).
                    return(new Value {
                        StringValue = XmlConvert.ToString(dateTime, XmlDateTimeSerializationMode.Utc)
                    });
                }
                return(new Value {
                    StringValue = Convert.ToString(value, InvariantCulture)
                });

            case TypeCode.Int64:
                return(new Value {
                    StringValue = Convert.ToInt64(value, InvariantCulture)
                                  .ToString(InvariantCulture)
                });

            case TypeCode.Float64:
                return(new Value {
                    NumberValue = Convert.ToDouble(value, InvariantCulture)
                });

            case TypeCode.Timestamp:
                return(new Value
                {
                    StringValue = XmlConvert.ToString(Convert.ToDateTime(value, InvariantCulture), XmlDateTimeSerializationMode.Utc)
                });

            case TypeCode.Date:
                return(new Value
                {
                    StringValue = StripTimePart(
                        XmlConvert.ToString(Convert.ToDateTime(value, InvariantCulture), XmlDateTimeSerializationMode.Utc))
                });

            case TypeCode.Array:
                if (value is IEnumerable enumerable)
                {
                    return(Value.ForList(
                               enumerable.Cast <object>()
                               .Select(x => ArrayElementType.ToProtobufValue(x)).ToArray()));
                }
                throw new ArgumentException("The given array instance needs to implement IEnumerable.");

            case TypeCode.Struct:
                if (value is IDictionary dictionary)
                {
                    var structValue = new Struct();
                    foreach (var key in dictionary.Keys)
                    {
                        string keyString = Convert.ToString(key, InvariantCulture);
                        if (!StructMembers.ContainsKey(keyString))
                        {
                            throw new ArgumentException("The given struct instance has members not defined in the Struct.", nameof(value));
                        }
                        structValue.Fields[keyString] = StructMembers[keyString].ToProtobufValue(
                            dictionary[key]);
                    }
                    return(Value.ForStruct(structValue));
                }
                throw new ArgumentException("The given struct instance needs to implement IDictionary.");

            default:
                throw new ArgumentOutOfRangeException(nameof(TypeCode), TypeCode, null);
            }
        }