From 54328bdbd429029273d0f48603a800f1c0317fd2 Mon Sep 17 00:00:00 2001 From: Evgeny Akhtimirov Date: Thu, 18 Jan 2024 12:38:14 +0300 Subject: [PATCH 1/8] Perf | avoid boxing of SqlGuid in SqlBuffer (#2300) --- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 2 +- .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 2 +- .../Data/SqlClient/Server/ValueUtilsSmi.netfx.cs | 4 ---- .../src/Microsoft/Data/SqlClient/SqlBuffer.cs | 9 ++++++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 7e04f6b9c5..8dc72f355c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -5504,7 +5504,7 @@ internal static object GetNullSqlValue(SqlBuffer nullVal, SqlMetaDataPriv md, Sq break; case SqlDbType.UniqueIdentifier: - nullVal.SqlGuid = SqlGuid.Null; + nullVal.SetToNullOfType(SqlBuffer.StorageType.SqlGuid); break; case SqlDbType.Bit: diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 7a9bbfdfd3..10ef7f8b33 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -6310,7 +6310,7 @@ internal static object GetNullSqlValue( break; case SqlDbType.UniqueIdentifier: - nullVal.SqlGuid = SqlGuid.Null; + nullVal.SetToNullOfType(SqlBuffer.StorageType.SqlGuid); break; case SqlDbType.Bit: diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs index 53bcedf146..0196687bf1 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs @@ -152,10 +152,6 @@ private static void GetNullOutputParameterSmi(SmiMetaData metaData, SqlBuffer ta // special case SqlBinary, 'cause tds parser never sets SqlBuffer to null, just to empty! targetBuffer.SqlBinary = SqlBinary.Null; } - else if (SqlBuffer.StorageType.SqlGuid == stype) - { - targetBuffer.SqlGuid = SqlGuid.Null; - } else { targetBuffer.SetToNullOfType(stype); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs index 9cfc077da4..3783421b20 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs @@ -109,7 +109,7 @@ internal struct Storage private bool _isNull; private StorageType _type; private Storage _value; - private object _object; // String, SqlBinary, SqlCachedBuffer, SqlGuid, SqlString, SqlXml + private object _object; // String, SqlBinary, SqlCachedBuffer, SqlString, SqlXml internal SqlBuffer() { @@ -815,14 +815,17 @@ internal SqlGuid SqlGuid } else if (StorageType.SqlGuid == _type) { - return IsNull ? SqlGuid.Null : (SqlGuid)_object; + return IsNull ? SqlGuid.Null : new SqlGuid(_value._guid); } return (SqlGuid)SqlValue; // anything else we haven't thought of goes through boxing. } set { Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; + if (!value.IsNull) + { + _value._guid = value.Value; + } _type = StorageType.SqlGuid; _isNull = value.IsNull; } From 00db92d97d16e590068de674b6eaef274486eaa3 Mon Sep 17 00:00:00 2001 From: Evgeny Akhtimirov Date: Fri, 19 Jan 2024 16:50:09 +0300 Subject: [PATCH 2/8] ignored .idea working folder --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index daeaf68c7a..bb6f58f9bd 100644 --- a/.gitignore +++ b/.gitignore @@ -353,6 +353,9 @@ healthchecksdb # Backup folder for Package Reference Convert tool in Visual Studio 2017 MigrationBackup/ +# JetBrains Rider (cross platform .NET IDE) working folder +.idea/ + # Ionide (cross platform F# VS Code tools) working folder .ionide/ From 1f4676da435f7ded893b5a75c43ed1f6cdad5219 Mon Sep 17 00:00:00 2001 From: Evgeny Akhtimirov Date: Fri, 19 Jan 2024 19:07:20 +0300 Subject: [PATCH 3/8] restored SqlTypeWorkarounds.SqlGuidCtor for non .NET8 tagrets This reverts commit 288a70c7d3c3ff8d15409668a830ccae47d9f17f partially --- .../SqlTypes/SqlTypeWorkarounds.netcore.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netcore.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netcore.cs index 7ce991530a..43ed8bbee4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netcore.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netcore.cs @@ -122,5 +122,31 @@ private struct SqlBinaryCaster internal SqlBinaryLookalike Fake; } #endregion + + #region Work around inability to access SqlGuid.ctor(byte[], bool) + internal static SqlGuid SqlGuidCtor(byte[] value, bool ignored) + { + // Construct a SqlGuid without allocating/copying the byte[]. This provides + // the same behavior as SqlGuid.ctor(byte[], bool). + var c = default(SqlGuidCaster); + c.Fake._value = value; + return c.Real; + } + + [StructLayout(LayoutKind.Sequential)] + private struct SqlGuidLookalike + { + internal byte[] _value; + } + + [StructLayout(LayoutKind.Explicit)] + private struct SqlGuidCaster + { + [FieldOffset(0)] + internal SqlGuid Real; + [FieldOffset(0)] + internal SqlGuidLookalike Fake; + } + #endregion } } From f1282f47978c66c08a0548c115208f0120f99191 Mon Sep 17 00:00:00 2001 From: Evgeny Akhtimirov Date: Fri, 19 Jan 2024 16:45:27 +0300 Subject: [PATCH 4/8] Perf | avoid copying of an array for SqlGuid in TdsParser (#2308) similar how it's don for netfx --- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 8dc72f355c..86d1102ac4 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -5932,7 +5932,11 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt case TdsEnums.SQLUNIQUEID: { Debug.Assert(length == 16, "invalid length for SqlGuid type!"); +#if NET8_0_OR_GREATER value.SqlGuid = new SqlGuid(unencryptedBytes); // doesn't copy the byte array +#else + value.SqlGuid = SqlTypeWorkarounds.SqlGuidCtor(unencryptedBytes, true); // doesn't copy the byte array +#endif break; } From e269e15ea06a1f51ec75cdd0626b4d6165fd59e1 Mon Sep 17 00:00:00 2001 From: Evgeny Akhtimirov Date: Fri, 19 Jan 2024 16:40:23 +0300 Subject: [PATCH 5/8] Perf | avoid copying of an array for SqlBinary in TdsParser (#2308) follow up changes for PR #1934 --- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 86d1102ac4..a11693da19 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -5957,7 +5957,11 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt unencryptedBytes = bytes; } - value.SqlBinary = new SqlBinary(unencryptedBytes); // doesn't copy the byte array +#if NET7_0_OR_GREATER + value.SqlBinary = SqlBinary.WrapBytes(unencryptedBytes); +#else + value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(unencryptedBytes, true); // doesn't copy the byte array +#endif break; } From 52e21cbec7f6e2b9b469a5464917ff5d6cefa9b4 Mon Sep 17 00:00:00 2001 From: Evgeny Akhtimirov Date: Fri, 19 Jan 2024 16:29:08 +0300 Subject: [PATCH 6/8] Perf | avoid boxing of SqlBinary.Null in SqlBuffer (#2308) --- .../netcore/src/Microsoft/Data/SqlClient/TdsParser.cs | 2 +- .../netfx/src/Microsoft/Data/SqlClient/TdsParser.cs | 2 +- .../Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs | 5 ----- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index a11693da19..88652b95ca 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -5500,7 +5500,7 @@ internal static object GetNullSqlValue(SqlBuffer nullVal, SqlMetaDataPriv md, Sq case SqlDbType.Binary: case SqlDbType.VarBinary: case SqlDbType.Image: - nullVal.SqlBinary = SqlBinary.Null; + nullVal.SetToNullOfType(SqlBuffer.StorageType.SqlBinary); break; case SqlDbType.UniqueIdentifier: diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index 10ef7f8b33..a960004a7c 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -6306,7 +6306,7 @@ internal static object GetNullSqlValue( case SqlDbType.Binary: case SqlDbType.VarBinary: case SqlDbType.Image: - nullVal.SqlBinary = SqlBinary.Null; + nullVal.SetToNullOfType(SqlBuffer.StorageType.SqlBinary); break; case SqlDbType.UniqueIdentifier: diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs index 0196687bf1..50b50ccff7 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs @@ -147,11 +147,6 @@ private static void GetNullOutputParameterSmi(SmiMetaData metaData, SqlBuffer ta { result = DBNull.Value; } - else if (SqlBuffer.StorageType.SqlBinary == stype) - { - // special case SqlBinary, 'cause tds parser never sets SqlBuffer to null, just to empty! - targetBuffer.SqlBinary = SqlBinary.Null; - } else { targetBuffer.SetToNullOfType(stype); From 2f8c574eb544c487e35a63db8d9deb7044964a3f Mon Sep 17 00:00:00 2001 From: Evgeny Akhtimirov Date: Fri, 19 Jan 2024 17:51:41 +0300 Subject: [PATCH 7/8] Perf | avoid boxing of not null SqlBinary data and copying of binary data in SqlDataReader and TdsParser (#2308) --- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 4 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 18 ++------- .../Microsoft/Data/SqlClient/SqlDataReader.cs | 4 +- .../src/Microsoft/Data/SqlClient/TdsParser.cs | 6 +-- .../SqlClient/Server/ValueUtilsSmi.netfx.cs | 2 +- .../src/Microsoft/Data/SqlClient/SqlBuffer.cs | 38 +++++++++++-------- 6 files changed, 33 insertions(+), 39 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 5eb7d7cdeb..1c6ae364e7 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -1573,7 +1573,7 @@ override public Stream GetStream(int i) else { // Grab already read data - data = _data[i].SqlBinary.Value; + data = _data[i].ByteArray; } // If non-sequential then we just have a read-only MemoryStream @@ -2969,7 +2969,7 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met } else { - byte[] value = data.IsNull ? Array.Empty() : data.SqlBinary.Value; + byte[] value = data.IsNull ? Array.Empty() : data.ByteArray; return (T)(object)new MemoryStream(value, writable: false); } } diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs index 88652b95ca..7026302e26 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -5957,11 +5957,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt unencryptedBytes = bytes; } -#if NET7_0_OR_GREATER - value.SqlBinary = SqlBinary.WrapBytes(unencryptedBytes); -#else - value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(unencryptedBytes, true); // doesn't copy the byte array -#endif + value.ByteArray = unencryptedBytes; // doesn't copy the byte array break; } @@ -6154,11 +6150,7 @@ internal bool TryReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, int length, T } else { -#if NET7_0_OR_GREATER - value.SqlBinary = SqlBinary.WrapBytes(b); -#else - value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(b, true); // doesn't copy the byte array -#endif + value.ByteArray = b; // doesn't copy the byte array } break; @@ -6451,12 +6443,8 @@ internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length, { return false; } -#if NET7_0_OR_GREATER - value.SqlBinary = SqlBinary.WrapBytes(b); -#else - value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(b, true); -#endif + value.ByteArray = b; // doesn't copy byte array break; } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs index e85c446cd1..5722d9b26f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -1770,7 +1770,7 @@ override public Stream GetStream(int i) else { // Grab already read data - data = _data[i].SqlBinary.Value; + data = _data[i].ByteArray; } // If non-sequential then we just have a read-only MemoryStream @@ -3308,7 +3308,7 @@ private T GetFieldValueFromSqlBufferInternal(SqlBuffer data, _SqlMetaData met } else { - byte[] value = data.IsNull ? Array.Empty() : data.SqlBinary.Value; + byte[] value = data.IsNull ? Array.Empty() : data.ByteArray; return (T)(object)new MemoryStream(value, writable: false); } } diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs index a960004a7c..8742b52c6f 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs @@ -6741,7 +6741,7 @@ internal bool DeserializeUnencryptedValue(SqlBuffer value, byte[] unencryptedByt unencryptedBytes = bytes; } - value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(unencryptedBytes, true); // doesn't copy the byte array + value.ByteArray = unencryptedBytes; // doesn't copy the byte array break; } @@ -6938,7 +6938,7 @@ internal bool TryReadSqlValue(SqlBuffer value, } else { - value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(b, true); // doesn't copy the byte array + value.ByteArray = b; // doesn't copy the byte array } break; @@ -7238,8 +7238,8 @@ internal bool TryReadSqlValueInternal(SqlBuffer value, byte tdsType, int length, { return false; } - value.SqlBinary = SqlTypeWorkarounds.SqlBinaryCtor(b, true); // doesn't copy the byte array + value.ByteArray = b; // doesn't copy the byte array break; } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs index 50b50ccff7..ba5548bd21 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs @@ -181,7 +181,7 @@ SqlBuffer targetBuffer // destination case SqlDbType.Image: case SqlDbType.Timestamp: case SqlDbType.VarBinary: - targetBuffer.SqlBinary = GetSqlBinary_Unchecked(sink, getters, ordinal); + targetBuffer.ByteArray = GetByteArray_Unchecked(sink, getters, ordinal); break; case SqlDbType.Bit: targetBuffer.Boolean = GetBoolean_Unchecked(sink, getters, ordinal); diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs index 3783421b20..65a876cd41 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs @@ -109,7 +109,7 @@ internal struct Storage private bool _isNull; private StorageType _type; private Storage _value; - private object _object; // String, SqlBinary, SqlCachedBuffer, SqlString, SqlXml + private object _object; // String, byte[] for SqlBinary, SqlCachedBuffer, SqlString, SqlXml internal SqlBuffer() { @@ -179,7 +179,19 @@ internal byte[] ByteArray get { ThrowIfNull(); - return SqlBinary.Value; + + if (StorageType.SqlBinary == _type) + { + return (byte[])_object; + } + return ((SqlBinary)SqlValue).Value; // anything else we haven't thought of goes through boxing. + } + set + { + Debug.Assert(IsEmpty, "setting value a second time?"); + _object = value; + _type = StorageType.SqlBinary; + _isNull = value is null; } } @@ -653,22 +665,16 @@ internal SqlBinary SqlBinary { get { - if (StorageType.SqlBinary == _type) + if (IsNull) { - if (IsNull) - { - return SqlBinary.Null; - } - return (SqlBinary)_object; + return SqlBinary.Null; } - return (SqlBinary)SqlValue; // anything else we haven't thought of goes through boxing. - } - set - { - Debug.Assert(IsEmpty, "setting value a second time?"); - _object = value; - _type = StorageType.SqlBinary; - _isNull = value.IsNull; + +#if NET7_0_OR_GREATER + return SqlBinary.WrapBytes(ByteArray); +#else + return SqlTypeWorkarounds.SqlBinaryCtor(ByteArray, true); // doesn't copy the byte array +#endif } } From f856e2beb0d6e2b6c8de0dd2d97874d68a1be6c1 Mon Sep 17 00:00:00 2001 From: Evgeny Akhtimirov Date: Fri, 19 Jan 2024 17:10:48 +0300 Subject: [PATCH 8/8] Perf | used Array.Empty() as a buffer for null values in SqlDataReader and ValueUtilsSmi (#2308) --- .../netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs | 4 ++-- .../Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs index 5722d9b26f..f0e6e743aa 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs @@ -1720,7 +1720,7 @@ virtual public XmlReader GetXmlReader(int i) if (_data[i].IsNull) { // A 'null' stream - return SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(new MemoryStream(new byte[0], writable: false), closeInput: true, async: false); + return SqlTypeWorkarounds.SqlXmlCreateSqlXmlReader(new MemoryStream(Array.Empty(), writable: false), closeInput: true, async: false); } else { @@ -1765,7 +1765,7 @@ override public Stream GetStream(int i) if (_data[i].IsNull) { // A 'null' stream - data = new byte[0]; + data = Array.Empty(); } else { diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs index ba5548bd21..b2d3082fd9 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/Server/ValueUtilsSmi.netfx.cs @@ -68,7 +68,7 @@ internal static Stream GetStream(SmiEventSink_Default sink, ITypedGettersV3 gett if (isDbNull) { // "null" stream - data = new byte[0]; + data = Array.Empty(); } else {