internal Task WriteSqlVariantValue(object value, int length, int offset, TdsParserStateObject stateObj, bool canAccumulate = true)
{
// handle null values
if (ADP.IsNull(value))
{
WriteInt(TdsEnums.FIXEDNULL, stateObj); //maxlen
WriteInt(TdsEnums.FIXEDNULL, stateObj); //actuallen
return null;
}
MetaType mt = MetaType.GetMetaTypeFromValue(value);
// Special case data type correction for SqlMoney inside a SqlVariant.
if ((TdsEnums.SQLNUMERICN == mt.TDSType) && (8 == length))
{
// The caller will coerce all SqlTypes to native CLR types, which means SqlMoney will
// coerce to decimal/SQLNUMERICN (via SqlMoney.Value call). In the case where the original
// value was SqlMoney the caller will also pass in the metadata length for the SqlMoney type
// which is 8 bytes. To honor the intent of the caller here we coerce this special case
// input back to SqlMoney from decimal/SQLNUMERICN.
mt = MetaType.GetMetaTypeFromValue(new SqlMoney((decimal)value));
}
if (mt.IsAnsiType)
{
length = GetEncodingCharLength((string)value, length, 0, _defaultEncoding);
}
// max and actual len are equal to
// SQLVARIANTSIZE {type (1 byte) + cbPropBytes (1 byte)} + cbPropBytes + length (actual length of data in bytes)
WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // maxLen
WriteInt(TdsEnums.SQLVARIANT_SIZE + mt.PropBytes + length, stateObj); // actualLen
// write the SQLVariant header (type and cbPropBytes)
stateObj.WriteByte(mt.TDSType);
stateObj.WriteByte(mt.PropBytes);
// now write the actual PropBytes and data
switch (mt.TDSType)
{
case TdsEnums.SQLFLT4:
WriteFloat((float)value, stateObj);
break;
case TdsEnums.SQLFLT8:
WriteDouble((double)value, stateObj);
break;
case TdsEnums.SQLINT8:
WriteLong((long)value, stateObj);
break;
case TdsEnums.SQLINT4:
WriteInt((int)value, stateObj);
break;
case TdsEnums.SQLINT2:
WriteShort((short)value, stateObj);
break;
case TdsEnums.SQLINT1:
stateObj.WriteByte((byte)value);
break;
case TdsEnums.SQLBIT:
if ((bool)value == true)
stateObj.WriteByte(1);
else
stateObj.WriteByte(0);
break;
case TdsEnums.SQLBIGVARBINARY:
{
byte[] b = (byte[])value;
WriteShort(length, stateObj); // propbytes: varlen
return stateObj.WriteByteArray(b, length, offset, canAccumulate);
}
case TdsEnums.SQLBIGVARCHAR:
{
string s = (string)value;
WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId
WriteShort(length, stateObj); // propbyte: varlen
return WriteEncodingChar(s, _defaultEncoding, stateObj, canAccumulate);
}
case TdsEnums.SQLUNIQUEID:
{
System.Guid guid = (System.Guid)value;
byte[] b = guid.ToByteArray();
Debug.Assert((length == b.Length) && (length == 16), "Invalid length for guid type in com+ object");
stateObj.WriteByteArray(b, length, 0);
break;
}
case TdsEnums.SQLNVARCHAR:
{
string s = (string)value;
WriteUnsignedInt(_defaultCollation.info, stateObj); // propbytes: collation.Info
stateObj.WriteByte(_defaultCollation.sortId); // propbytes: collation.SortId
WriteShort(length, stateObj); // propbyte: varlen
// string takes cchar, not cbyte so convert
length >>= 1;
return WriteString(s, length, offset, stateObj, canAccumulate);
}
case TdsEnums.SQLDATETIME:
{
TdsDateTime dt = MetaType.FromDateTime((DateTime)value, 8);
WriteInt(dt.days, stateObj);
WriteInt(dt.time, stateObj);
break;
}
case TdsEnums.SQLMONEY:
{
WriteCurrency((Decimal)value, 8, stateObj);
break;
}
case TdsEnums.SQLNUMERICN:
{
stateObj.WriteByte(mt.Precision); //propbytes: precision
stateObj.WriteByte((byte)((Decimal.GetBits((Decimal)value)[3] & 0x00ff0000) >> 0x10)); // propbytes: scale
WriteDecimal((Decimal)value, stateObj);
break;
}
case TdsEnums.SQLTIME:
stateObj.WriteByte(mt.Scale); //propbytes: scale
WriteTime((TimeSpan)value, mt.Scale, length, stateObj);
break;
case TdsEnums.SQLDATETIMEOFFSET:
stateObj.WriteByte(mt.Scale); //propbytes: scale
WriteDateTimeOffset((DateTimeOffset)value, mt.Scale, length, stateObj);
break;
default:
Debug.Assert(false, "unknown tds type for sqlvariant!");
break;
} // switch
// return point for accumulated writes, note: non-accumulated writes returned from their case statements
return null;
}