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 b5e1dc903c..1db7c3ab1d 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -489,7 +489,9 @@
-
+
+ Microsoft\Data\SqlClient\SqlStatistics.cs
+
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 2ceeed013b..e36768383b 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -477,7 +477,9 @@
-
+
+ Microsoft\Data\SqlClient\SqlStatistics.cs
+
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs
index c0dd91e2d7..1116e94e8f 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlConnection.cs
@@ -2869,11 +2869,11 @@ public IDictionary RetrieveStatistics()
if (null != Statistics)
{
UpdateStatistics();
- return Statistics.GetHashtable();
+ return Statistics.GetDictionary();
}
else
{
- return new SqlStatistics().GetHashtable();
+ return new SqlStatistics().GetDictionary();
}
}
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStatistics.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStatistics.cs
deleted file mode 100644
index 7c72711cd2..0000000000
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlStatistics.cs
+++ /dev/null
@@ -1,221 +0,0 @@
-// 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.Collections;
-using System.Diagnostics;
-using Microsoft.Data.Common;
-
-namespace Microsoft.Data.SqlClient
-{
- internal sealed class SqlStatistics
- {
-
- static internal SqlStatistics StartTimer(SqlStatistics statistics)
- {
- if ((null != statistics) && !statistics.RequestExecutionTimer())
- {
- // we're re-entrant -- don't bother.
- statistics = null;
- }
- return statistics;
- }
-
- static internal void StopTimer(SqlStatistics statistics)
- {
- if (null != statistics)
- {
- statistics.ReleaseAndUpdateExecutionTimer();
- }
- }
-
- // internal values that are not exposed through properties
- internal long _closeTimestamp;
- internal long _openTimestamp;
- internal long _startExecutionTimestamp;
- internal long _startFetchTimestamp;
- internal long _startNetworkServerTimestamp;
-
- // internal values that are exposed through properties
- internal long _buffersReceived;
- internal long _buffersSent;
- internal long _bytesReceived;
- internal long _bytesSent;
- internal long _connectionTime;
- internal long _cursorOpens;
- internal long _executionTime;
- internal long _iduCount;
- internal long _iduRows;
- internal long _networkServerTime;
- internal long _preparedExecs;
- internal long _prepares;
- internal long _selectCount;
- internal long _selectRows;
- internal long _serverRoundtrips;
- internal long _sumResultSets;
- internal long _transactions;
- internal long _unpreparedExecs;
-
- // these flags are required if statistics is turned on/off in the middle of command execution
- private bool _waitForDoneAfterRow;
- private bool _waitForReply;
-
-
- internal bool WaitForDoneAfterRow
- {
- get
- {
- return _waitForDoneAfterRow;
- }
- set
- {
- _waitForDoneAfterRow = value;
- }
- }
-
- internal bool WaitForReply
- {
- get
- {
- return _waitForReply;
- }
- }
-
- internal SqlStatistics()
- {
- }
-
- internal void ContinueOnNewConnection()
- {
- _startExecutionTimestamp = 0;
- _startFetchTimestamp = 0;
- _waitForDoneAfterRow = false;
- _waitForReply = false;
- }
-
- internal IDictionary GetHashtable()
- {
- Hashtable ht = new Hashtable(18);
-
- ht.Add("BuffersReceived", _buffersReceived);
- ht.Add("BuffersSent", _buffersSent);
- ht.Add("BytesReceived", _bytesReceived);
- ht.Add("BytesSent", _bytesSent);
- ht.Add("CursorOpens", _cursorOpens);
- ht.Add("IduCount", _iduCount);
- ht.Add("IduRows", _iduRows);
- ht.Add("PreparedExecs", _preparedExecs);
- ht.Add("Prepares", _prepares);
- ht.Add("SelectCount", _selectCount);
- ht.Add("SelectRows", _selectRows);
- ht.Add("ServerRoundtrips", _serverRoundtrips);
- ht.Add("SumResultSets", _sumResultSets);
- ht.Add("Transactions", _transactions);
- ht.Add("UnpreparedExecs", _unpreparedExecs);
-
- ht.Add("ConnectionTime", ADP.TimerToMilliseconds(_connectionTime));
- ht.Add("ExecutionTime", ADP.TimerToMilliseconds(_executionTime));
- ht.Add("NetworkServerTime", ADP.TimerToMilliseconds(_networkServerTime));
-
- return ht;
- }
-
- internal bool RequestExecutionTimer()
- {
- if (_startExecutionTimestamp == 0)
- {
- _startExecutionTimestamp = ADP.TimerCurrent();
- return true;
- }
- return false;
- }
-
- internal void RequestNetworkServerTimer()
- {
- Debug.Assert(_startExecutionTimestamp != 0, "No network time expected outside execution period");
- if (_startNetworkServerTimestamp == 0)
- {
- _startNetworkServerTimestamp = ADP.TimerCurrent();
- }
- _waitForReply = true;
- }
-
- internal void ReleaseAndUpdateExecutionTimer()
- {
- if (_startExecutionTimestamp > 0)
- {
- _executionTime += (ADP.TimerCurrent() - _startExecutionTimestamp);
- _startExecutionTimestamp = 0;
- }
- }
-
- internal void ReleaseAndUpdateNetworkServerTimer()
- {
- if (_waitForReply && _startNetworkServerTimestamp > 0)
- {
- _networkServerTime += (ADP.TimerCurrent() - _startNetworkServerTimestamp);
- _startNetworkServerTimestamp = 0;
- }
- _waitForReply = false;
- }
-
- internal void Reset()
- {
- _buffersReceived = 0;
- _buffersSent = 0;
- _bytesReceived = 0;
- _bytesSent = 0;
- _connectionTime = 0;
- _cursorOpens = 0;
- _executionTime = 0;
- _iduCount = 0;
- _iduRows = 0;
- _networkServerTime = 0;
- _preparedExecs = 0;
- _prepares = 0;
- _selectCount = 0;
- _selectRows = 0;
- _serverRoundtrips = 0;
- _sumResultSets = 0;
- _transactions = 0;
- _unpreparedExecs = 0;
- _waitForDoneAfterRow = false;
- _waitForReply = false;
- _startExecutionTimestamp = 0;
- _startNetworkServerTimestamp = 0;
- }
-
- internal void SafeAdd(ref long value, long summand)
- {
- if (long.MaxValue - value > summand)
- {
- value += summand;
- }
- else
- {
- value = long.MaxValue;
- }
- }
-
- internal long SafeIncrement(ref long value)
- {
- if (value < long.MaxValue)
- value++;
- return value;
- }
-
- internal void UpdateStatistics()
- {
- // update connection time
- if (_closeTimestamp >= _openTimestamp && long.MaxValue > _closeTimestamp - _openTimestamp)
- {
- _connectionTime = _closeTimestamp - _openTimestamp;
- }
- else
- {
- _connectionTime = long.MaxValue;
- }
- }
- }
-}
-
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs
index af7390b2a9..d1cdf1157f 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.Designer.cs
@@ -1824,6 +1824,42 @@ internal static string ADP_VersionDoesNotSupportDataType {
}
}
+ ///
+ /// Looks up a localized string similar to Destination array is not long enough to copy all the items in the collection. Check array index and length..
+ ///
+ internal static string Arg_ArrayPlusOffTooSmall {
+ get {
+ return ResourceManager.GetString("Arg_ArrayPlusOffTooSmall", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Only single dimensional arrays are supported for the requested action..
+ ///
+ internal static string Arg_RankMultiDimNotSupported {
+ get {
+ return ResourceManager.GetString("Arg_RankMultiDimNotSupported", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cannot remove the specified item because it was not found in the specified Collection..
+ ///
+ internal static string Arg_RemoveArgNotFound {
+ get {
+ return ResourceManager.GetString("Arg_RemoveArgNotFound", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Non-negative number required..
+ ///
+ internal static string ArgumentOutOfRange_NeedNonNegNum {
+ get {
+ return ResourceManager.GetString("ArgumentOutOfRange_NeedNonNegNum", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The validation of an attestation token failed. The token signature does not match the signature omputed using a public key retrieved from the attestation public key endpoint at '{0}'. Verify the DNS apping for the endpoint. If correct, contact Customer Support Services..
///
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx
index d1b907c6e9..bd8efd2529 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Resources/Strings.resx
@@ -4617,4 +4617,16 @@
'{0}' is not less than '{1}'; '{2}' cannot be greater than '{3}'.
-
+
+ Destination array is not long enough to copy all the items in the collection. Check array index and length.
+
+
+ Only single dimensional arrays are supported for the requested action.
+
+
+ Cannot remove the specified item because it was not found in the specified Collection.
+
+
+ Non-negative number required.
+
+
\ No newline at end of file
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStatistics.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs
similarity index 91%
rename from src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStatistics.cs
rename to src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs
index 4aeeb28322..7a5e37198b 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlStatistics.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlStatistics.cs
@@ -30,6 +30,11 @@ internal static void StopTimer(SqlStatistics statistics)
}
}
+ internal static ValueSqlStatisticsScope TimedScope(SqlStatistics statistics)
+ {
+ return new ValueSqlStatisticsScope(statistics);
+ }
+
// internal values that are not exposed through properties
internal long _closeTimestamp;
internal long _openTimestamp;
@@ -61,30 +66,17 @@ internal static void StopTimer(SqlStatistics statistics)
private bool _waitForDoneAfterRow;
private bool _waitForReply;
-
- internal bool WaitForDoneAfterRow
+ internal SqlStatistics()
{
- get
- {
- return _waitForDoneAfterRow;
- }
- set
- {
- _waitForDoneAfterRow = value;
- }
}
- internal bool WaitForReply
+ internal bool WaitForDoneAfterRow
{
- get
- {
- return _waitForReply;
- }
+ get => _waitForDoneAfterRow;
+ set => _waitForDoneAfterRow = value;
}
- internal SqlStatistics()
- {
- }
+ internal bool WaitForReply => _waitForReply;
internal void ContinueOnNewConnection()
{
@@ -203,7 +195,9 @@ internal void SafeAdd(ref long value, long summand)
internal long SafeIncrement(ref long value)
{
if (value < long.MaxValue)
+ {
value++;
+ }
return value;
}
@@ -289,13 +283,21 @@ private void CopyValues(Array array, int arrayIndex)
private void ValidateCopyToArguments(Array array, int arrayIndex)
{
if (array == null)
+ {
throw new ArgumentNullException(nameof(array));
+ }
if (array.Rank != 1)
+ {
throw new ArgumentException(Strings.Arg_RankMultiDimNotSupported);
+ }
if (arrayIndex < 0)
+ {
throw new ArgumentOutOfRangeException(nameof(arrayIndex), Strings.ArgumentOutOfRange_NeedNonNegNum);
+ }
if (array.Length - arrayIndex < Count)
+ {
throw new ArgumentException(Strings.Arg_ArrayPlusOffTooSmall);
+ }
}
private sealed class Collection : ICollection
@@ -335,4 +337,16 @@ void ICollection.CopyTo(Array array, int arrayIndex)
}
}
}
+
+ // This is a ref struct to prevent it being included in async closures accidentally.
+ // Async functions should manage the timer directly using the Start and Stop method
+ // in their invoke and completion functions
+ internal readonly ref struct ValueSqlStatisticsScope // : IDisposable // ref structs cannot implement interfaces but the compiler will use pattern matching to allow use of using on them
+ {
+ private readonly SqlStatistics _statistics;
+
+ public ValueSqlStatisticsScope(SqlStatistics statistics) => _statistics = SqlStatistics.StartTimer(statistics);
+
+ public void Dispose() => SqlStatistics.StopTimer(_statistics);
+ }
}