diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index a1b1e230ba..4675d6c795 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -537,6 +537,7 @@
+
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
new file mode 100644
index 0000000000..17959f3dc1
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netcore.cs
@@ -0,0 +1,152 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Data.SqlTypes;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.Data.SqlTypes
+{
+ ///
+ /// This type provides workarounds for the separation between System.Data.Common
+ /// and Microsoft.Data.SqlClient. The latter wants to access internal members of the former, and
+ /// this class provides ways to do that. We must review and update this implementation any time the
+ /// implementation of the corresponding types in System.Data.Common change.
+ ///
+ internal static partial class SqlTypeWorkarounds
+ {
+ #region Work around inability to access SqlMoney.ctor(long, int) and SqlMoney.ToSqlInternalRepresentation
+ ///
+ /// Constructs a SqlMoney from a long value without scaling. The ignored parameter exists
+ /// only to distinguish this constructor from the constructor that takes a long.
+ /// Used only internally.
+ ///
+ internal static SqlMoney SqlMoneyCtor(long value, int ignored)
+ {
+ var c = default(SqlMoneyCaster);
+
+ // Same behavior as the internal SqlMoney.ctor(long, bool) overload
+ c.Fake._fNotNull = true;
+ c.Fake._value = value;
+
+ return c.Real;
+ }
+
+ internal static long SqlMoneyToSqlInternalRepresentation(SqlMoney money)
+ {
+ var c = default(SqlMoneyCaster);
+ c.Real = money;
+
+ // Same implementation as the internal SqlMoney.ToSqlInternalRepresentation implementation
+ if (money.IsNull)
+ {
+ throw new SqlNullValueException();
+ }
+ return c.Fake._value;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct SqlMoneyLookalike // exact same shape as SqlMoney, but with accessible fields
+ {
+ internal bool _fNotNull;
+ internal long _value;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ private struct SqlMoneyCaster
+ {
+ [FieldOffset(0)]
+ internal SqlMoney Real;
+ [FieldOffset(0)]
+ internal SqlMoneyLookalike Fake;
+ }
+ #endregion
+
+ #region Work around inability to access SqlDecimal._data1/2/3/4
+ internal static void SqlDecimalExtractData(SqlDecimal d, out uint data1, out uint data2, out uint data3, out uint data4)
+ {
+ // Extract the four data elements from SqlDecimal.
+ var c = default(SqlDecimalCaster);
+ c.Real = d;
+ data1 = c.Fake._data1;
+ data2 = c.Fake._data2;
+ data3 = c.Fake._data3;
+ data4 = c.Fake._data4;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct SqlDecimalLookalike // exact same shape as SqlDecimal, but with accessible fields
+ {
+ internal byte _bStatus;
+ internal byte _bLen;
+ internal byte _bPrec;
+ internal byte _bScale;
+ internal uint _data1;
+ internal uint _data2;
+ internal uint _data3;
+ internal uint _data4;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ private struct SqlDecimalCaster
+ {
+ [FieldOffset(0)]
+ internal SqlDecimal Real;
+ [FieldOffset(0)]
+ internal SqlDecimalLookalike Fake;
+ }
+ #endregion
+
+ #region Work around inability to access SqlBinary.ctor(byte[], bool)
+ internal static SqlBinary SqlBinaryCtor(byte[] value, bool ignored)
+ {
+ // Construct a SqlBinary without allocating/copying the byte[]. This provides
+ // the same behavior as SqlBinary.ctor(byte[], bool).
+ var c = default(SqlBinaryCaster);
+ c.Fake._value = value;
+ return c.Real;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private struct SqlBinaryLookalike
+ {
+ internal byte[] _value;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ private struct SqlBinaryCaster
+ {
+ [FieldOffset(0)]
+ internal SqlBinary Real;
+ [FieldOffset(0)]
+ 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
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index c89c24b8ed..b51ed5c2fc 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -505,6 +505,7 @@
+
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netfx.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netfx.cs
new file mode 100644
index 0000000000..8550fd06b2
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.netfx.cs
@@ -0,0 +1,343 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Data.SqlTypes;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Runtime.Serialization;
+using Microsoft.Data.SqlClient;
+
+namespace Microsoft.Data.SqlTypes
+{
+ ///
+ /// This type provides workarounds for the separation between System.Data.Common
+ /// and Microsoft.Data.SqlClient. The latter wants to access internal members of the former, and
+ /// this class provides ways to do that. We must review and update this implementation any time the
+ /// implementation of the corresponding types in System.Data.Common change.
+ ///
+ internal static partial class SqlTypeWorkarounds
+ {
+ #region Work around inability to access SqlMoney.ctor(long, int) and SqlMoney.ToSqlInternalRepresentation
+ private static readonly Func s_sqlMoneyfactory = CtorHelper.CreateFactory(); // binds to SqlMoney..ctor(long, int) if it exists
+
+ ///
+ /// Constructs a SqlMoney from a long value without scaling. The ignored parameter exists
+ /// only to distinguish this constructor from the constructor that takes a long.
+ /// Used only internally.
+ ///
+ internal static SqlMoney SqlMoneyCtor(long value, int ignored)
+ {
+ SqlMoney val;
+ if (s_sqlMoneyfactory is not null)
+ {
+ val = s_sqlMoneyfactory(value);
+ }
+ else
+ {
+ // SqlMoney is a long internally. Dividing by 10,000 gives us the decimal representation
+ val = new SqlMoney(((decimal)value) / 10000);
+ }
+
+ return val;
+ }
+
+ internal static long SqlMoneyToSqlInternalRepresentation(SqlMoney money)
+ {
+ return SqlMoneyHelper.s_sqlMoneyToLong(ref money);
+ }
+
+ private static class SqlMoneyHelper
+ {
+ internal delegate long SqlMoneyToLongDelegate(ref SqlMoney @this);
+ internal static readonly SqlMoneyToLongDelegate s_sqlMoneyToLong = GetSqlMoneyToLong();
+
+ internal static SqlMoneyToLongDelegate GetSqlMoneyToLong()
+ {
+ SqlMoneyToLongDelegate del = null;
+ try
+ {
+ del = GetFastSqlMoneyToLong();
+ }
+ catch
+ {
+ // If an exception occurs for any reason, swallow & use the fallback code path.
+ }
+
+ return del ?? FallbackSqlMoneyToLong;
+ }
+
+ private static SqlMoneyToLongDelegate GetFastSqlMoneyToLong()
+ {
+ MethodInfo toSqlInternalRepresentation = typeof(SqlMoney).GetMethod("ToSqlInternalRepresentation",
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.ExactBinding,
+ null, CallingConventions.Any, new Type[] { }, null);
+
+ if (toSqlInternalRepresentation is not null && toSqlInternalRepresentation.ReturnType == typeof(long))
+ {
+ // On Full Framework, invoking the MethodInfo first before wrapping
+ // a delegate around it will produce better codegen. We don't need
+ // to inspect the return value; we just need to call the method.
+
+ _ = toSqlInternalRepresentation.Invoke(new SqlMoney(0), new object[0]);
+
+ // Now create the delegate. This is an open delegate, meaning the
+ // "this" parameter will be provided as arg0 on each call.
+
+ var del = (SqlMoneyToLongDelegate)toSqlInternalRepresentation.CreateDelegate(typeof(SqlMoneyToLongDelegate), target: null);
+
+ // Now we can cache the delegate and invoke it over and over again.
+ // Note: the first parameter to the delegate is provided *byref*.
+
+ return del;
+ }
+
+ SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.GetFastSqlMoneyToLong | Info | SqlMoney.ToSqlInternalRepresentation() not found. Less efficient fallback method will be used.");
+ return null; // missing the expected method - cannot use fast path
+ }
+
+ // Used in case we can't use a [Serializable]-like mechanism.
+ private static long FallbackSqlMoneyToLong(ref SqlMoney value)
+ {
+ if (value.IsNull)
+ {
+ return default;
+ }
+ else
+ {
+ decimal data = value.ToDecimal();
+ return (long)(data * 10000);
+ }
+ }
+ }
+ #endregion
+
+ #region Work around inability to access SqlDecimal._data1/2/3/4
+ internal static void SqlDecimalExtractData(SqlDecimal d, out uint data1, out uint data2, out uint data3, out uint data4)
+ {
+ SqlDecimalHelper.s_decompose(d, out data1, out data2, out data3, out data4);
+ }
+
+ private static class SqlDecimalHelper
+ {
+ internal delegate void Decomposer(SqlDecimal value, out uint data1, out uint data2, out uint data3, out uint data4);
+ internal static readonly Decomposer s_decompose = GetDecomposer();
+
+ private static Decomposer GetDecomposer()
+ {
+ Decomposer decomposer = null;
+ try
+ {
+ decomposer = GetFastDecomposer();
+ }
+ catch
+ {
+ // If an exception occurs for any reason, swallow & use the fallback code path.
+ }
+
+ return decomposer ?? FallbackDecomposer;
+ }
+
+ private static Decomposer GetFastDecomposer()
+ {
+ // This takes advantage of the fact that for [Serializable] types, the member fields are implicitly
+ // part of the type's serialization contract. This includes the fields' names and types. By default,
+ // [Serializable]-compliant serializers will read all the member fields and shove the data into a
+ // SerializationInfo dictionary. We mimic this behavior in a manner consistent with the [Serializable]
+ // pattern, but much more efficiently.
+ //
+ // In order to make sure we're staying compliant, we need to gate our checks to fulfill some core
+ // assumptions. Importantly, the type must be [Serializable] but cannot be ISerializable, as the
+ // presence of the interface means that the type wants to be responsible for its own serialization,
+ // and that member fields are not guaranteed to be part of the serialization contract. Additionally,
+ // we need to check for [OnSerializing] and [OnDeserializing] methods, because we cannot account
+ // for any logic which might be present within them.
+
+ if (!typeof(SqlDecimal).IsSerializable)
+ {
+ SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | SqlDecimal isn't Serializable. Less efficient fallback method will be used.");
+ return null; // type is not serializable - cannot use fast path assumptions
+ }
+
+ if (typeof(ISerializable).IsAssignableFrom(typeof(SqlDecimal)))
+ {
+ SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | SqlDecimal is ISerializable. Less efficient fallback method will be used.");
+ return null; // type contains custom logic - cannot use fast path assumptions
+ }
+
+ foreach (MethodInfo method in typeof(SqlDecimal).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
+ {
+ if (method.IsDefined(typeof(OnDeserializingAttribute)) || method.IsDefined(typeof(OnDeserializedAttribute)))
+ {
+ SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | SqlDecimal contains custom serialization logic. Less efficient fallback method will be used.");
+ return null; // type contains custom logic - cannot use fast path assumptions
+ }
+ }
+
+ // GetSerializableMembers filters out [NonSerialized] fields for us automatically.
+
+ FieldInfo fiData1 = null, fiData2 = null, fiData3 = null, fiData4 = null;
+ foreach (MemberInfo candidate in FormatterServices.GetSerializableMembers(typeof(SqlDecimal)))
+ {
+ if (candidate is FieldInfo fi && fi.FieldType == typeof(uint))
+ {
+ if (fi.Name == "m_data1")
+ { fiData1 = fi; }
+ else if (fi.Name == "m_data2")
+ { fiData2 = fi; }
+ else if (fi.Name == "m_data3")
+ { fiData3 = fi; }
+ else if (fi.Name == "m_data4")
+ { fiData4 = fi; }
+ }
+ }
+
+ if (fiData1 is null || fiData2 is null || fiData3 is null || fiData4 is null)
+ {
+ SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.SqlDecimalHelper.GetFastDecomposer | Info | Expected SqlDecimal fields are missing. Less efficient fallback method will be used.");
+ return null; // missing one of the expected member fields - cannot use fast path assumptions
+ }
+
+ Type refToUInt32 = typeof(uint).MakeByRefType();
+ DynamicMethod dm = new(
+ name: "sqldecimal-decomposer",
+ returnType: typeof(void),
+ parameterTypes: new[] { typeof(SqlDecimal), refToUInt32, refToUInt32, refToUInt32, refToUInt32 },
+ restrictedSkipVisibility: true); // perf: JITs method at delegate creation time
+
+ ILGenerator ilGen = dm.GetILGenerator();
+ ilGen.Emit(OpCodes.Ldarg_1); // eval stack := [UInt32&]
+ ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal]
+ ilGen.Emit(OpCodes.Ldfld, fiData1); // eval stack := [UInt32&] [UInt32]
+ ilGen.Emit(OpCodes.Stind_I4); // eval stack :=
+ ilGen.Emit(OpCodes.Ldarg_2); // eval stack := [UInt32&]
+ ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal]
+ ilGen.Emit(OpCodes.Ldfld, fiData2); // eval stack := [UInt32&] [UInt32]
+ ilGen.Emit(OpCodes.Stind_I4); // eval stack :=
+ ilGen.Emit(OpCodes.Ldarg_3); // eval stack := [UInt32&]
+ ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal]
+ ilGen.Emit(OpCodes.Ldfld, fiData3); // eval stack := [UInt32&] [UInt32]
+ ilGen.Emit(OpCodes.Stind_I4); // eval stack :=
+ ilGen.Emit(OpCodes.Ldarg_S, (byte)4); // eval stack := [UInt32&]
+ ilGen.Emit(OpCodes.Ldarg_0); // eval stack := [UInt32&] [SqlDecimal]
+ ilGen.Emit(OpCodes.Ldfld, fiData4); // eval stack := [UInt32&] [UInt32]
+ ilGen.Emit(OpCodes.Stind_I4); // eval stack :=
+ ilGen.Emit(OpCodes.Ret);
+
+ return (Decomposer)dm.CreateDelegate(typeof(Decomposer), null /* target */);
+ }
+
+ // Used in case we can't use a [Serializable]-like mechanism.
+ private static void FallbackDecomposer(SqlDecimal value, out uint data1, out uint data2, out uint data3, out uint data4)
+ {
+ if (value.IsNull)
+ {
+ data1 = default;
+ data2 = default;
+ data3 = default;
+ data4 = default;
+ }
+ else
+ {
+ int[] data = value.Data; // allocation
+ data4 = (uint)data[3]; // write in reverse to avoid multiple bounds checks
+ data3 = (uint)data[2];
+ data2 = (uint)data[1];
+ data1 = (uint)data[0];
+ }
+ }
+ }
+ #endregion
+
+ #region Work around inability to access SqlBinary.ctor(byte[], bool)
+ private static readonly Func s_sqlBinaryfactory = CtorHelper.CreateFactory(); // binds to SqlBinary..ctor(byte[], bool) if it exists
+
+ internal static SqlBinary SqlBinaryCtor(byte[] value, bool ignored)
+ {
+ SqlBinary val;
+ if (s_sqlBinaryfactory is not null)
+ {
+ val = s_sqlBinaryfactory(value);
+ }
+ else
+ {
+ val = new SqlBinary(value);
+ }
+
+ return val;
+ }
+ #endregion
+
+ #region Work around inability to access SqlGuid.ctor(byte[], bool)
+ private static readonly Func s_sqlGuidfactory = CtorHelper.CreateFactory(); // binds to SqlGuid..ctor(byte[], bool) if it exists
+
+ internal static SqlGuid SqlGuidCtor(byte[] value, bool ignored)
+ {
+ SqlGuid val;
+ if (s_sqlGuidfactory is not null)
+ {
+ val = s_sqlGuidfactory(value);
+ }
+ else
+ {
+ val = new SqlGuid(value);
+ }
+
+ return val;
+ }
+ #endregion
+
+ private static class CtorHelper
+ {
+ // Returns null if .ctor(TValue, TIgnored) cannot be found.
+ // Caller should have fallback logic in place in case the API doesn't exist.
+ internal unsafe static Func CreateFactory() where TInstance : struct
+ {
+ try
+ {
+ ConstructorInfo fullCtor = typeof(TInstance).GetConstructor(
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.ExactBinding,
+ null, new[] { typeof(TValue), typeof(TIgnored) }, null);
+ if (fullCtor is not null)
+ {
+ // Need to use fnptr rather than delegate since MulticastDelegate expects to point to a MethodInfo,
+ // not a ConstructorInfo. The convention for invoking struct ctors is that the caller zeros memory,
+ // then passes a ref to the zeroed memory as the implicit arg0 "this". We don't need to worry
+ // about keeping this pointer alive; the fact that we're instantiated over TInstance will do it
+ // for us.
+ //
+ // On Full Framework, creating a delegate to InvocationHelper before invoking it for the first time
+ // will cause the delegate to point to the pre-JIT stub, which has an expensive preamble. Instead,
+ // we invoke InvocationHelper manually with a captured no-op fnptr. We'll then replace it with the
+ // real fnptr before creating a new delegate (pointing to the real codegen, not the stub) and
+ // returning that new delegate to our caller.
+
+ static void DummyNoOp(ref TInstance @this, TValue value, TIgnored ignored)
+ { }
+
+ IntPtr fnPtr;
+ TInstance InvocationHelper(TValue value)
+ {
+ TInstance retVal = default; // ensure zero-inited
+ ((delegate* managed[)fnPtr)(ref retVal, value, default);
+ return retVal;
+ }
+
+ fnPtr = (IntPtr)(delegate* managed][)(&DummyNoOp);
+ InvocationHelper(default); // no-op to trigger JIT
+
+ fnPtr = fullCtor.MethodHandle.GetFunctionPointer(); // replace before returning to caller
+ return InvocationHelper;
+ }
+ }
+ catch
+ {
+ }
+
+ SqlClientEventSource.Log.TryTraceEvent("SqlTypeWorkarounds.CtorHelper.CreateFactory | Info | {0}..ctor({1}, {2}) not found. Less efficient fallback method will be used.", typeof(TInstance).Name, typeof(TValue).Name, typeof(TIgnored).Name);
+ return null; // factory not found or an exception occurred
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs
index 8bf15ead0e..26b474a608 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlTypeWorkarounds.cs
@@ -7,7 +7,6 @@
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using System.Xml;
using Microsoft.Data.SqlClient;
@@ -19,12 +18,12 @@ namespace Microsoft.Data.SqlTypes
/// this class provides ways to do that. We must review and update this implementation any time the
/// implementation of the corresponding types in System.Data.Common change.
///
- internal static class SqlTypeWorkarounds
+ internal static partial class SqlTypeWorkarounds
{
#region Work around inability to access SqlXml.CreateSqlXmlReader
- private static readonly XmlReaderSettings s_defaultXmlReaderSettings = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment };
- private static readonly XmlReaderSettings s_defaultXmlReaderSettingsCloseInput = new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true };
- private static readonly XmlReaderSettings s_defaultXmlReaderSettingsAsyncCloseInput = new XmlReaderSettings() { Async = true, ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true };
+ private static readonly XmlReaderSettings s_defaultXmlReaderSettings = new() { ConformanceLevel = ConformanceLevel.Fragment };
+ private static readonly XmlReaderSettings s_defaultXmlReaderSettingsCloseInput = new() { ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true };
+ private static readonly XmlReaderSettings s_defaultXmlReaderSettingsAsyncCloseInput = new() { Async = true, ConformanceLevel = ConformanceLevel.Fragment, CloseInput = true };
internal const SqlCompareOptions SqlStringValidSqlCompareOptionMask =
SqlCompareOptions.IgnoreCase | SqlCompareOptions.IgnoreWidth |
@@ -91,139 +90,5 @@ internal static DateTime SqlDateTimeToDateTime(int daypart, int timepart)
private static Exception ThrowOverflowException() => throw SQL.DateTimeOverflow();
#endregion
-
- #region Work around inability to access SqlMoney.ctor(long, int) and SqlMoney.ToSqlInternalRepresentation
- ///
- /// Constructs a SqlMoney from a long value without scaling. The ignored parameter exists
- /// only to distinguish this constructor from the constructor that takes a long.
- /// Used only internally.
- ///
- internal static SqlMoney SqlMoneyCtor(long value, int ignored)
- {
- var c = default(SqlMoneyCaster);
-
- // Same behavior as the internal SqlMoney.ctor(long, bool) overload
- c.Fake._fNotNull = true;
- c.Fake._value = value;
-
- return c.Real;
- }
-
- internal static long SqlMoneyToSqlInternalRepresentation(SqlMoney money)
- {
- var c = default(SqlMoneyCaster);
- c.Real = money;
-
- // Same implementation as the internal SqlMoney.ToSqlInternalRepresentation implementation
- if (money.IsNull)
- {
- throw new SqlNullValueException();
- }
- return c.Fake._value;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- private struct SqlMoneyLookalike // exact same shape as SqlMoney, but with accessible fields
- {
- internal bool _fNotNull;
- internal long _value;
- }
-
- [StructLayout(LayoutKind.Explicit)]
- private struct SqlMoneyCaster
- {
- [FieldOffset(0)]
- internal SqlMoney Real;
- [FieldOffset(0)]
- internal SqlMoneyLookalike Fake;
- }
- #endregion
-
- #region Work around inability to access SqlDecimal._data1/2/3/4
- internal static void SqlDecimalExtractData(SqlDecimal d, out uint data1, out uint data2, out uint data3, out uint data4)
- {
- // Extract the four data elements from SqlDecimal.
- var c = default(SqlDecimalCaster);
- c.Real = d;
- data1 = c.Fake._data1;
- data2 = c.Fake._data2;
- data3 = c.Fake._data3;
- data4 = c.Fake._data4;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- private struct SqlDecimalLookalike // exact same shape as SqlDecimal, but with accessible fields
- {
- internal byte _bStatus;
- internal byte _bLen;
- internal byte _bPrec;
- internal byte _bScale;
- internal uint _data1;
- internal uint _data2;
- internal uint _data3;
- internal uint _data4;
- }
-
- [StructLayout(LayoutKind.Explicit)]
- private struct SqlDecimalCaster
- {
- [FieldOffset(0)]
- internal SqlDecimal Real;
- [FieldOffset(0)]
- internal SqlDecimalLookalike Fake;
- }
- #endregion
-
- #region Work around inability to access SqlBinary.ctor(byte[], bool)
- internal static SqlBinary SqlBinaryCtor(byte[] value, bool ignored)
- {
- // Construct a SqlBinary without allocating/copying the byte[]. This provides
- // the same behavior as SqlBinary.ctor(byte[], bool).
- var c = default(SqlBinaryCaster);
- c.Fake._value = value;
- return c.Real;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- private struct SqlBinaryLookalike
- {
- internal byte[] _value;
- }
-
- [StructLayout(LayoutKind.Explicit)]
- private struct SqlBinaryCaster
- {
- [FieldOffset(0)]
- internal SqlBinary Real;
- [FieldOffset(0)]
- 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
}
}
]