diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/DbCellValue.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/DbCellValue.cs
index 343ebeb3..64989930 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/DbCellValue.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/Contracts/DbCellValue.cs
@@ -22,6 +22,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
///
public bool IsNull { get; set; }
+ ///
+ /// Culture invariant display value for the cell, this value can later be used by the client to convert back to the original value.
+ ///
+ public string InvariantCultureDisplayValue { get; set; }
+
///
/// The raw object for the cell, for use internally
///
@@ -42,6 +47,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts
Validate.IsNotNull(nameof(other), other);
other.DisplayValue = DisplayValue;
+ other.InvariantCultureDisplayValue = InvariantCultureDisplayValue;
other.IsNull = IsNull;
other.RawObject = RawObject;
other.RowId = RowId;
diff --git a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs
index 15caa32a..23b12709 100644
--- a/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs
+++ b/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/ServiceBufferFileStreamReader.cs
@@ -151,11 +151,11 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
// Use the right read function for the type to read the data from the file
ReadMethod readFunc;
- if(!readMethods.TryGetValue(colType, out readFunc))
+ if (!readMethods.TryGetValue(colType, out readFunc))
{
// Treat everything else as a string
readFunc = readMethods[typeof(string)];
- }
+ }
FileStreamReadResult result = readFunc(currentFileOffset, rowId, column);
currentFileOffset += result.TotalLength;
results.Add(result.Value);
@@ -193,15 +193,17 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// If provided, this function will be used to determine if the value is null
///
/// Optional function to use to convert the object to a string.
+ /// Optional parameter indicates whether the culture invariant display value should be provided.
/// The expected type of the cell. Used to keep the code honest
/// The object, a display value, and the length of the value + its length
private FileStreamReadResult ReadCellHelper(long offset, long rowId,
Func convertFunc,
Func isNullFunc = null,
- Func toStringFunc = null)
+ Func toStringFunc = null,
+ bool setInvariantCultureDisplayValue = false)
{
LengthResult length = ReadLength(offset);
- DbCellValue result = new DbCellValue {RowId = rowId};
+ DbCellValue result = new DbCellValue { RowId = rowId };
if (isNullFunc == null ? length.ValueLength == 0 : isNullFunc(length.TotalLength))
{
@@ -216,6 +218,17 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
T resultObject = convertFunc(length.ValueLength);
result.RawObject = resultObject;
result.DisplayValue = toStringFunc == null ? result.RawObject.ToString() : toStringFunc(resultObject);
+ if (setInvariantCultureDisplayValue)
+ {
+ string icDisplayValue = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}", result.RawObject);
+
+ // Only set the value when it is different from the DisplayValue to reduce the size of the result
+ //
+ if (icDisplayValue != result.DisplayValue)
+ {
+ result.InvariantCultureDisplayValue = icDisplayValue;
+ }
+ }
result.IsNull = false;
}
@@ -300,7 +313,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// A single
internal FileStreamReadResult ReadSingle(long fileOffset, long rowId)
{
- return ReadCellHelper(fileOffset, rowId, length => BitConverter.ToSingle(buffer, 0));
+ return ReadCellHelper(fileOffset, rowId, length => BitConverter.ToSingle(buffer, 0), setInvariantCultureDisplayValue: true);
}
///
@@ -311,7 +324,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
/// A double
internal FileStreamReadResult ReadDouble(long fileOffset, long rowId)
{
- return ReadCellHelper(fileOffset, rowId, length => BitConverter.ToDouble(buffer, 0));
+ return ReadCellHelper(fileOffset, rowId, length => BitConverter.ToDouble(buffer, 0), setInvariantCultureDisplayValue: true);
}
///
@@ -326,7 +339,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
int[] arrInt32 = new int[(length - 3) / 4];
Buffer.BlockCopy(buffer, 3, arrInt32, 0, length - 3);
return new SqlDecimal(buffer[0], buffer[1], buffer[2] == 1, arrInt32);
- });
+ }, setInvariantCultureDisplayValue: true);
}
///
@@ -341,7 +354,7 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
int[] arrInt32 = new int[length / 4];
Buffer.BlockCopy(buffer, 0, arrInt32, 0, length);
return new decimal(arrInt32);
- });
+ }, setInvariantCultureDisplayValue: true);
}
///
@@ -402,7 +415,8 @@ namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
// DateTimeOffset is represented by DateTime.Ticks followed by TimeSpan.Ticks
// both as Int64 values
- return ReadCellHelper(offset, rowId, length => {
+ return ReadCellHelper(offset, rowId, length =>
+ {
long dtTicks = BitConverter.ToInt64(buffer, 0);
long dtOffset = BitConverter.ToInt64(buffer, 8);
return new DateTimeOffset(new DateTime(dtTicks), new TimeSpan(dtOffset));