/mcs/class/referencesource/System.Data/System/Data/SqlClient/SqlConnection.cs
http://github.com/mono/mono · C# · 2598 lines · 1981 code · 318 blank · 299 comment · 444 complexity · 6cebdf85c2005c2c7b8e5723a5995d48 MD5 · raw file
Large files are truncated click here to view the full file
- //------------------------------------------------------------------------------
- // <copyright file="SqlConnection.cs" company="Microsoft">
- // Copyright (c) Microsoft Corporation. All rights reserved.
- // </copyright>
- // <owner current="true" primary="true">Microsoft</owner>
- // <owner current="true" primary="false">Microsoft</owner>
- //------------------------------------------------------------------------------
- [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("System.Data.DataSetExtensions, PublicKey="+AssemblyRef.EcmaPublicKeyFull)] // DevDiv Bugs 92166
- namespace System.Data.SqlClient
- {
- using System;
- using System.Collections;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Configuration.Assemblies;
- using System.ComponentModel;
- using System.Data;
- using System.Data.Common;
- using System.Data.ProviderBase;
- using System.Data.Sql;
- using System.Data.SqlTypes;
- using System.Diagnostics;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Runtime.CompilerServices;
- using System.Runtime.ConstrainedExecution;
- using System.Runtime.InteropServices;
- using System.Runtime.Remoting;
- using System.Runtime.Serialization.Formatters;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Security;
- using System.Security.Permissions;
- using System.Reflection;
- using System.Runtime.Versioning;
-
- using Microsoft.SqlServer.Server;
- using System.Security.Principal;
- using System.Diagnostics.CodeAnalysis;
- [DefaultEvent("InfoMessage")]
- public sealed partial class SqlConnection: DbConnection, ICloneable {
- static private readonly object EventInfoMessage = new object();
- // System column encryption key store providers are added by default
- static private readonly Dictionary<string, SqlColumnEncryptionKeyStoreProvider> _SystemColumnEncryptionKeyStoreProviders
- = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(capacity: 1, comparer: StringComparer.OrdinalIgnoreCase)
- {
- {SqlColumnEncryptionCertificateStoreProvider.ProviderName, new SqlColumnEncryptionCertificateStoreProvider()},
- {SqlColumnEncryptionCngProvider.ProviderName, new SqlColumnEncryptionCngProvider()},
- {SqlColumnEncryptionCspProvider.ProviderName, new SqlColumnEncryptionCspProvider()}
- };
- /// <summary>
- /// Custom provider list should be provided by the user. We shallow copy the user supplied dictionary into a ReadOnlyDictionary.
- /// Custom provider list can only supplied once per application.
- /// </summary>
- static private ReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider> _CustomColumnEncryptionKeyStoreProviders;
- // Lock to control setting of _CustomColumnEncryptionKeyStoreProviders
- static private readonly Object _CustomColumnEncryptionKeyProvidersLock = new Object();
- /// <summary>
- /// Dictionary object holding trusted key paths for various SQL Servers.
- /// Key to the dictionary is a SQL Server Name
- /// IList contains a list of trusted key paths.
- /// </summary>
- static private readonly ConcurrentDictionary<string, IList<string>> _ColumnEncryptionTrustedMasterKeyPaths
- = new ConcurrentDictionary<string, IList<string>>(concurrencyLevel: 4 * Environment.ProcessorCount /* default value in ConcurrentDictionary*/,
- capacity: 1,
- comparer: StringComparer.OrdinalIgnoreCase);
- [
- DefaultValue(null),
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.TCE_SqlConnection_TrustedColumnMasterKeyPaths),
- ]
- static public IDictionary<string, IList<string>> ColumnEncryptionTrustedMasterKeyPaths
- {
- get
- {
- return _ColumnEncryptionTrustedMasterKeyPaths;
- }
- }
- /// <summary>
- /// Defines whether query metadata caching is enabled.
- /// </summary>
- static private bool _ColumnEncryptionQueryMetadataCacheEnabled = true;
- [
- DefaultValue(null),
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.TCE_SqlConnection_ColumnEncryptionQueryMetadataCacheEnabled),
- ]
- static public bool ColumnEncryptionQueryMetadataCacheEnabled
- {
- get
- {
- return _ColumnEncryptionQueryMetadataCacheEnabled;
- }
- set
- {
- _ColumnEncryptionQueryMetadataCacheEnabled = value;
- }
- }
- /// <summary>
- /// Defines whether query metadata caching is enabled.
- /// </summary>
- static private TimeSpan _ColumnEncryptionKeyCacheTtl = TimeSpan.FromHours(2);
- [
- DefaultValue(null),
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.TCE_SqlConnection_ColumnEncryptionKeyCacheTtl),
- ]
- static public TimeSpan ColumnEncryptionKeyCacheTtl
- {
- get
- {
- return _ColumnEncryptionKeyCacheTtl;
- }
- set
- {
- _ColumnEncryptionKeyCacheTtl = value;
- }
- }
- /// <summary>
- /// This function should only be called once in an app. This does shallow copying of the dictionary so that
- /// the app cannot alter the custom provider list once it has been set.
- ///
- /// Example:
- ///
- /// Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customKeyStoreProviders = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>();
- /// MySqlClientHSMProvider myProvider = new MySqlClientHSMProvider();
- /// customKeyStoreProviders.Add(@"HSM Provider", myProvider);
- /// SqlConnection.RegisterColumnEncryptionKeyStoreProviders(customKeyStoreProviders);
- /// </summary>
- /// <param name="customProviders">Custom column encryption key provider dictionary</param>
- static public void RegisterColumnEncryptionKeyStoreProviders(IDictionary<string, SqlColumnEncryptionKeyStoreProvider> customProviders)
- {
- // Return when the provided dictionary is null.
- if (customProviders == null)
- {
- throw SQL.NullCustomKeyStoreProviderDictionary();
- }
- // Validate that custom provider list doesn't contain any of system provider list
- foreach (string key in customProviders.Keys)
- {
- // Validate the provider name
- //
- // Check for null or empty
- if (string.IsNullOrWhiteSpace(key))
- {
- throw SQL.EmptyProviderName();
- }
- // Check if the name starts with MSSQL_, since this is reserved namespace for system providers.
- if (key.StartsWith(ADP.ColumnEncryptionSystemProviderNamePrefix, StringComparison.InvariantCultureIgnoreCase))
- {
- throw SQL.InvalidCustomKeyStoreProviderName(key, ADP.ColumnEncryptionSystemProviderNamePrefix);
- }
- // Validate the provider value
- if (customProviders[key] == null)
- {
- throw SQL.NullProviderValue(key);
- }
- }
- lock (_CustomColumnEncryptionKeyProvidersLock)
- {
- // Provider list can only be set once
- if (_CustomColumnEncryptionKeyStoreProviders != null)
- {
- throw SQL.CanOnlyCallOnce();
- }
- // Create a temporary dictionary and then add items from the provided dictionary.
- // Dictionary constructor does shallow copying by simply copying the provider name and provider reference pairs
- // in the provided customerProviders dictionary.
- Dictionary<string, SqlColumnEncryptionKeyStoreProvider> customColumnEncryptionKeyStoreProviders =
- new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>(customProviders, StringComparer.OrdinalIgnoreCase);
- // Set the dictionary to the ReadOnly dictionary.
- _CustomColumnEncryptionKeyStoreProviders = new ReadOnlyDictionary<string, SqlColumnEncryptionKeyStoreProvider>(customColumnEncryptionKeyStoreProviders);
- }
- }
- /// <summary>
- /// This function walks through both system and custom column encryption key store providers and returns an object if found.
- /// </summary>
- /// <param name="providerName">Provider Name to be searched in System Provider diction and Custom provider dictionary.</param>
- /// <param name="columnKeyStoreProvider">If the provider is found, returns the corresponding SqlColumnEncryptionKeyStoreProvider instance.</param>
- /// <returns>true if the provider is found, else returns false</returns>
- static internal bool TryGetColumnEncryptionKeyStoreProvider(string providerName, out SqlColumnEncryptionKeyStoreProvider columnKeyStoreProvider) {
- Debug.Assert(!string.IsNullOrWhiteSpace(providerName), "Provider name is invalid");
- // Initialize the out parameter
- columnKeyStoreProvider = null;
- // Search in the sytem provider list.
- if (_SystemColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider))
- {
- return true;
- }
- lock (_CustomColumnEncryptionKeyProvidersLock)
- {
- // If custom provider is not set, then return false
- if (_CustomColumnEncryptionKeyStoreProviders == null)
- {
- return false;
- }
- // Search in the custom provider list
- return _CustomColumnEncryptionKeyStoreProviders.TryGetValue(providerName, out columnKeyStoreProvider);
- }
- }
- /// <summary>
- /// This function returns a list of system provider dictionary currently supported by this driver.
- /// </summary>
- /// <returns>Combined list of provider names</returns>
- static internal List<string> GetColumnEncryptionSystemKeyStoreProviders() {
- HashSet<string> providerNames = new HashSet<string>(_SystemColumnEncryptionKeyStoreProviders.Keys);
- return providerNames.ToList();
- }
- /// <summary>
- /// This function returns a list of custom provider dictionary currently registered.
- /// </summary>
- /// <returns>Combined list of provider names</returns>
- static internal List<string> GetColumnEncryptionCustomKeyStoreProviders() {
- if(_CustomColumnEncryptionKeyStoreProviders != null)
- {
- HashSet<string> providerNames = new HashSet<string>(_CustomColumnEncryptionKeyStoreProviders.Keys);
- return providerNames.ToList();
- }
- return new List<string>();
- }
- private SqlDebugContext _sdc; // SQL Debugging support
- private bool _AsyncCommandInProgress;
- // SQLStatistics support
- internal SqlStatistics _statistics;
- private bool _collectstats;
- private bool _fireInfoMessageEventOnUserErrors; // False by default
- // root task associated with current async invocation
- Tuple<TaskCompletionSource<DbConnectionInternal>, Task> _currentCompletion;
- private SqlCredential _credential; // SQL authentication password stored in SecureString
- private string _connectionString;
- private int _connectRetryCount;
- private string _accessToken; // Access Token to be used for token based authententication
- // connection resiliency
- private object _reconnectLock = new object();
- internal Task _currentReconnectionTask;
- private Task _asyncWaitingForReconnection; // current async task waiting for reconnection in non-MARS connections
- private Guid _originalConnectionId = Guid.Empty;
- private CancellationTokenSource _reconnectionCancellationSource;
- internal SessionData _recoverySessionData;
- internal WindowsIdentity _lastIdentity;
- internal WindowsIdentity _impersonateIdentity;
- private int _reconnectCount;
- // Transient Fault handling flag. This is needed to convey to the downstream mechanism of connection establishment, if Transient Fault handling should be used or not
- // The downstream handling of Connection open is the same for idle connection resiliency. Currently we want to apply transient fault handling only to the connections opened
- // using SqlConnection.Open() method.
- internal bool _applyTransientFaultHandling = false;
-
- public SqlConnection(string connectionString) : this(connectionString, null) {
- }
- public SqlConnection(string connectionString, SqlCredential credential) : this() {
- ConnectionString = connectionString; // setting connection string first so that ConnectionOption is available
- if (credential != null)
- {
- // The following checks are necessary as setting Credential property will call CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential
- // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential it will throw InvalidOperationException rather than Arguemtn exception
- // Need to call setter on Credential property rather than setting _credential directly as pool groups need to be checked
- SqlConnectionString connectionOptions = (SqlConnectionString) ConnectionOptions;
- if (UsesClearUserIdOrPassword(connectionOptions))
- {
- throw ADP.InvalidMixedArgumentOfSecureAndClearCredential();
- }
- if (UsesIntegratedSecurity(connectionOptions))
- {
- throw ADP.InvalidMixedArgumentOfSecureCredentialAndIntegratedSecurity();
- }
- if (UsesContextConnection(connectionOptions))
- {
- throw ADP.InvalidMixedArgumentOfSecureCredentialAndContextConnection();
- }
- if (UsesActiveDirectoryIntegrated(connectionOptions))
- {
- throw SQL.SettingCredentialWithIntegratedArgument();
- }
- Credential = credential;
- }
- // else
- // credential == null: we should not set "Credential" as this will do additional validation check and
- // checking pool groups which is not necessary. All necessary operation is already done by calling "ConnectionString = connectionString"
- CacheConnectionStringProperties();
- }
- private SqlConnection(SqlConnection connection) { // Clone
- GC.SuppressFinalize(this);
- CopyFrom(connection);
- _connectionString = connection._connectionString;
- if (connection._credential != null)
- {
- SecureString password = connection._credential.Password.Copy();
- password.MakeReadOnly();
- _credential = new SqlCredential(connection._credential.UserId, password);
- }
- _accessToken = connection._accessToken;
- CacheConnectionStringProperties();
- }
- // This method will be called once connection string is set or changed.
- private void CacheConnectionStringProperties() {
- SqlConnectionString connString = ConnectionOptions as SqlConnectionString;
- if (connString != null) {
- _connectRetryCount = connString.ConnectRetryCount;
- }
- }
- //
- // PUBLIC PROPERTIES
- //
- // used to start/stop collection of statistics data and do verify the current state
- //
- // devnote: start/stop should not performed using a property since it requires execution of code
- //
- // start statistics
- // set the internal flag (_statisticsEnabled) to true.
- // Create a new SqlStatistics object if not already there.
- // connect the parser to the object.
- // if there is no parser at this time we need to connect it after creation.
- //
- [
- DefaultValue(false),
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.SqlConnection_StatisticsEnabled),
- ]
- public bool StatisticsEnabled {
- get {
- return (_collectstats);
- }
- set {
- if (IsContextConnection) {
- if (value) {
- throw SQL.NotAvailableOnContextConnection();
- }
- }
- else {
- if (value) {
- // start
- if (ConnectionState.Open == State) {
- if (null == _statistics) {
- _statistics = new SqlStatistics();
- ADP.TimerCurrent(out _statistics._openTimestamp);
- }
- // set statistics on the parser
- // update timestamp;
- Debug.Assert(Parser != null, "Where's the parser?");
- Parser.Statistics = _statistics;
- }
- }
- else {
- // stop
- if (null != _statistics) {
- if (ConnectionState.Open == State) {
- // remove statistics from parser
- // update timestamp;
- TdsParser parser = Parser;
- Debug.Assert(parser != null, "Where's the parser?");
- parser.Statistics = null;
- ADP.TimerCurrent(out _statistics._closeTimestamp);
- }
- }
- }
- this._collectstats = value;
- }
- }
- }
- internal bool AsyncCommandInProgress {
- get {
- return (_AsyncCommandInProgress);
- }
- [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
- set {
- _AsyncCommandInProgress = value;
- }
- }
- internal bool IsContextConnection {
- get {
- SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
- return UsesContextConnection(opt);
- }
- }
- /// <summary>
- /// Is this connection using column encryption ?
- /// </summary>
- internal bool IsColumnEncryptionSettingEnabled {
- get {
- SqlConnectionString opt = (SqlConnectionString)ConnectionOptions;
- return opt != null ? opt.ColumnEncryptionSetting == SqlConnectionColumnEncryptionSetting.Enabled : false;
- }
- }
- // Is this connection is a Context Connection?
- private bool UsesContextConnection(SqlConnectionString opt)
- {
- return opt != null ? opt.ContextConnection : false;
- }
- private bool UsesActiveDirectoryIntegrated(SqlConnectionString opt)
- {
- return opt != null ? opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated : false;
- }
- private bool UsesAuthentication(SqlConnectionString opt) {
- return opt != null ? opt.Authentication != SqlAuthenticationMethod.NotSpecified : false;
- }
-
- // Does this connection uses Integrated Security?
- private bool UsesIntegratedSecurity(SqlConnectionString opt) {
- return opt != null ? opt.IntegratedSecurity : false;
- }
- // Does this connection uses old style of clear userID or Password in connection string?
- private bool UsesClearUserIdOrPassword(SqlConnectionString opt) {
- bool result = false;
- if (null != opt) {
- result = (!ADP.IsEmpty(opt.UserID) || !ADP.IsEmpty(opt.Password));
- }
- return result;
- }
- internal SqlConnectionString.TransactionBindingEnum TransactionBinding {
- get {
- return ((SqlConnectionString)ConnectionOptions).TransactionBinding;
- }
- }
- internal SqlConnectionString.TypeSystem TypeSystem {
- get {
- return ((SqlConnectionString)ConnectionOptions).TypeSystemVersion;
- }
- }
- internal Version TypeSystemAssemblyVersion {
- get {
- return ((SqlConnectionString)ConnectionOptions).TypeSystemAssemblyVersion;
- }
- }
-
- internal PoolBlockingPeriod PoolBlockingPeriod
- {
- get
- {
- return ((SqlConnectionString)ConnectionOptions).PoolBlockingPeriod;
- }
- }
- internal int ConnectRetryInterval {
- get {
- return ((SqlConnectionString)ConnectionOptions).ConnectRetryInterval;
- }
- }
- override protected DbProviderFactory DbProviderFactory {
- get {
- return SqlClientFactory.Instance;
- }
- }
- // AccessToken: To be used for token based authentication
- [
- Browsable(false),
- DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
- ResDescriptionAttribute(Res.SqlConnection_AccessToken),
- ]
- public string AccessToken {
- get {
- string result = _accessToken;
- // When a connection is connecting or is ever opened, make AccessToken available only if "Persist Security Info" is set to true
- // otherwise, return null
- SqlConnectionString connectionOptions = (SqlConnectionString)UserConnectionOptions;
- if (InnerConnection.ShouldHidePassword && connectionOptions != null && !connectionOptions.PersistSecurityInfo) {
- result = null;
- }
- return result;
- }
- set {
- // If a connection is connecting or is ever opened, AccessToken cannot be set
- if (!InnerConnection.AllowSetConnectionString) {
- throw ADP.OpenConnectionPropertySet("AccessToken", InnerConnection.State);
- }
-
- if (value != null) {
- // Check if the usage of AccessToken has any conflict with the keys used in connection string and credential
- CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken((SqlConnectionString)ConnectionOptions);
- }
-
- _accessToken = value;
- // Need to call ConnectionString_Set to do proper pool group check
- ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, credential: _credential, accessToken: _accessToken));
- }
- }
- [
- DefaultValue(""),
- #pragma warning disable 618 // ignore obsolete warning about RecommendedAsConfigurable to use SettingsBindableAttribute
- RecommendedAsConfigurable(true),
- #pragma warning restore 618
- SettingsBindableAttribute(true),
- RefreshProperties(RefreshProperties.All),
- ResCategoryAttribute(Res.DataCategory_Data),
- Editor("Microsoft.VSDesigner.Data.SQL.Design.SqlConnectionStringEditor, " + AssemblyRef.MicrosoftVSDesigner, "System.Drawing.Design.UITypeEditor, " + AssemblyRef.SystemDrawing),
- ResDescriptionAttribute(Res.SqlConnection_ConnectionString),
- ]
- override public string ConnectionString {
- get {
- return ConnectionString_Get();
- }
- set {
- if(_credential != null || _accessToken != null) {
- SqlConnectionString connectionOptions = new SqlConnectionString(value);
- if(_credential != null) {
- // Check for Credential being used with Authentication=ActiveDirectoryIntegrated. Since a different error string is used
- // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
- // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
- if(UsesActiveDirectoryIntegrated(connectionOptions)) {
- throw SQL.SettingIntegratedWithCredential();
- }
- CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(connectionOptions);
- }
- else if(_accessToken != null) {
- CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(connectionOptions);
- }
- }
- ConnectionString_Set(new SqlConnectionPoolKey(value, _credential, _accessToken));
- _connectionString = value; // Change _connectionString value only after value is validated
- CacheConnectionStringProperties();
- }
- }
- [
- DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
- ResDescriptionAttribute(Res.SqlConnection_ConnectionTimeout),
- ]
- override public int ConnectionTimeout {
- get {
- SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
- return ((null != constr) ? constr.ConnectTimeout : SqlConnectionString.DEFAULT.Connect_Timeout);
- }
- }
- [
- DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
- ResDescriptionAttribute(Res.SqlConnection_Database),
- ]
- override public string Database {
- // if the connection is open, we need to ask the inner connection what it's
- // current catalog is because it may have gotten changed, otherwise we can
- // just return what the connection string had.
- get {
- SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
- string result;
- if (null != innerConnection) {
- result = innerConnection.CurrentDatabase;
- }
- else {
- SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
- result = ((null != constr) ? constr.InitialCatalog : SqlConnectionString.DEFAULT.Initial_Catalog);
- }
- return result;
- }
- }
- [
- Browsable(true),
- DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
- ResDescriptionAttribute(Res.SqlConnection_DataSource),
- ]
- override public string DataSource {
- get {
- SqlInternalConnection innerConnection = (InnerConnection as SqlInternalConnection);
- string result;
- if (null != innerConnection) {
- result = innerConnection.CurrentDataSource;
- }
- else {
- SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
- result = ((null != constr) ? constr.DataSource : SqlConnectionString.DEFAULT.Data_Source);
- }
- return result;
- }
- }
- [
- DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.SqlConnection_PacketSize),
- ]
- public int PacketSize {
- // if the connection is open, we need to ask the inner connection what it's
- // current packet size is because it may have gotten changed, otherwise we
- // can just return what the connection string had.
- get {
- if (IsContextConnection) {
- throw SQL.NotAvailableOnContextConnection();
- }
- SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
- int result;
- if (null != innerConnection) {
- result = innerConnection.PacketSize;
- }
- else {
- SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
- result = ((null != constr) ? constr.PacketSize : SqlConnectionString.DEFAULT.Packet_Size);
- }
- return result;
- }
- }
- [
- DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.SqlConnection_ClientConnectionId),
- ]
- public Guid ClientConnectionId {
- get {
- SqlInternalConnectionTds innerConnection = (InnerConnection as SqlInternalConnectionTds);
- if (null != innerConnection) {
- return innerConnection.ClientConnectionId;
- }
- else {
- Task reconnectTask = _currentReconnectionTask;
- if (reconnectTask != null && !reconnectTask.IsCompleted) {
- return _originalConnectionId;
- }
- return Guid.Empty;
- }
- }
- }
- [
- Browsable(false),
- DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
- ResDescriptionAttribute(Res.SqlConnection_ServerVersion),
- ]
- override public string ServerVersion {
- get {
- return GetOpenConnection().ServerVersion;
- }
- }
- [
- Browsable(false),
- DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
- ResDescriptionAttribute(Res.DbConnection_State),
- ]
- override public ConnectionState State {
- get {
- Task reconnectTask=_currentReconnectionTask;
- if (reconnectTask != null && !reconnectTask.IsCompleted) {
- return ConnectionState.Open;
- }
- return InnerConnection.State;
- }
- }
- internal SqlStatistics Statistics {
- get {
- return _statistics;
- }
- }
- [
- DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
- ResCategoryAttribute(Res.DataCategory_Data),
- ResDescriptionAttribute(Res.SqlConnection_WorkstationId),
- ]
- public string WorkstationId {
- get {
- if (IsContextConnection) {
- throw SQL.NotAvailableOnContextConnection();
- }
- // If not supplied by the user, the default value is the MachineName
- // Note: In Longhorn you'll be able to rename a machine without
- // rebooting. Therefore, don't cache this machine name.
- SqlConnectionString constr = (SqlConnectionString)ConnectionOptions;
- string result = ((null != constr) ? constr.WorkstationId : null);
- if (null == result) {
- // getting machine name requires Environment.Permission
- // user must have that permission in order to retrieve this
- result = Environment.MachineName;
- }
- return result;
- }
- }
- // SqlCredential: Pair User Id and password in SecureString which are to be used for SQL authentication
- [
- Browsable(false),
- DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
- ResDescriptionAttribute(Res.SqlConnection_Credential),
- ]
- public SqlCredential Credential
- {
- get
- {
- SqlCredential result = _credential;
- // When a connection is connecting or is ever opened, make credential available only if "Persist Security Info" is set to true
- // otherwise, return null
- SqlConnectionString connectionOptions = (SqlConnectionString) UserConnectionOptions;
- if (InnerConnection.ShouldHidePassword && connectionOptions != null && !connectionOptions.PersistSecurityInfo)
- {
- result = null;
- }
- return result;
- }
- set
- {
- // If a connection is connecting or is ever opened, user id/password cannot be set
- if (!InnerConnection.AllowSetConnectionString)
- {
- throw ADP.OpenConnectionPropertySet("Credential", InnerConnection.State);
- }
- // check if the usage of credential has any conflict with the keys used in connection string
- if (value != null)
- {
- // Check for Credential being used with Authentication=ActiveDirectoryIntegrated. Since a different error string is used
- // for this case in ConnectionString setter vs in Credential setter, check for this error case before calling
- // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential, which is common to both setters.
- if (UsesActiveDirectoryIntegrated((SqlConnectionString) ConnectionOptions)) {
- throw SQL.SettingCredentialWithIntegratedInvalid();
- }
- CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential((SqlConnectionString) ConnectionOptions);
- if(_accessToken != null) {
- throw ADP.InvalidMixedUsageOfCredentialAndAccessToken();
- }
- }
-
- _credential = value;
- // Need to call ConnectionString_Set to do proper pool group check
- ConnectionString_Set(new SqlConnectionPoolKey(_connectionString, _credential, accessToken: _accessToken));
- }
- }
- // CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential: check if the usage of credential has any conflict
- // with the keys used in connection string
- // If there is any conflict, it throws InvalidOperationException
- // This is to be used setter of ConnectionString and Credential properties
- private void CheckAndThrowOnInvalidCombinationOfConnectionStringAndSqlCredential(SqlConnectionString connectionOptions)
- {
- if (UsesClearUserIdOrPassword(connectionOptions))
- {
- throw ADP.InvalidMixedUsageOfSecureAndClearCredential();
- }
- if (UsesIntegratedSecurity(connectionOptions))
- {
- throw ADP.InvalidMixedUsageOfSecureCredentialAndIntegratedSecurity();
- }
- if (UsesContextConnection(connectionOptions))
- {
- throw ADP.InvalidMixedArgumentOfSecureCredentialAndContextConnection();
- }
- }
- // CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken: check if the usage of AccessToken has any conflict
- // with the keys used in connection string and credential
- // If there is any conflict, it throws InvalidOperationException
- // This is to be used setter of ConnectionString and AccessToken properties
- private void CheckAndThrowOnInvalidCombinationOfConnectionOptionAndAccessToken(SqlConnectionString connectionOptions) {
- if (UsesClearUserIdOrPassword(connectionOptions)) {
- throw ADP.InvalidMixedUsageOfAccessTokenAndUserIDPassword();
- }
- if (UsesIntegratedSecurity(connectionOptions)) {
- throw ADP.InvalidMixedUsageOfAccessTokenAndIntegratedSecurity();
- }
- if (UsesContextConnection(connectionOptions)) {
- throw ADP.InvalidMixedUsageOfAccessTokenAndContextConnection();
- }
- if (UsesAuthentication(connectionOptions)) {
- throw ADP.InvalidMixedUsageOfAccessTokenAndAuthentication();
- }
- // Check if the usage of AccessToken has the conflict with credential
- if (_credential != null) {
- throw ADP.InvalidMixedUsageOfAccessTokenAndCredential();
- }
- }
- //
- // PUBLIC EVENTS
- //
- [
- ResCategoryAttribute(Res.DataCategory_InfoMessage),
- ResDescriptionAttribute(Res.DbConnection_InfoMessage),
- ]
- public event SqlInfoMessageEventHandler InfoMessage {
- add {
- Events.AddHandler(EventInfoMessage, value);
- }
- remove {
- Events.RemoveHandler(EventInfoMessage, value);
- }
- }
- public bool FireInfoMessageEventOnUserErrors {
- get {
- return _fireInfoMessageEventOnUserErrors;
- }
- set {
- _fireInfoMessageEventOnUserErrors = value;
- }
- }
- // Approx. number of times that the internal connection has been reconnected
- internal int ReconnectCount {
- get {
- return _reconnectCount;
- }
- }
- //
- // PUBLIC METHODS
- //
- new public SqlTransaction BeginTransaction() {
- // this is just a delegate. The actual method tracks executiontime
- return BeginTransaction(IsolationLevel.Unspecified, null);
- }
- new public SqlTransaction BeginTransaction(IsolationLevel iso) {
- // this is just a delegate. The actual method tracks executiontime
- return BeginTransaction(iso, null);
- }
- public SqlTransaction BeginTransaction(string transactionName) {
- // Use transaction names only on the outermost pair of nested
- // BEGIN...COMMIT or BEGIN...ROLLBACK statements. Transaction names
- // are ignored for nested BEGIN's. The only way to rollback a nested
- // transaction is to have a save point from a SAVE TRANSACTION call.
- return BeginTransaction(IsolationLevel.Unspecified, transactionName);
- }
- // suppress this message - we cannot use SafeHandle here. Also, see notes in the code (VSTFDEVDIV# 560355)
- [SuppressMessage("Microsoft.Reliability", "CA2004:RemoveCallsToGCKeepAlive")]
- override protected DbTransaction BeginDbTransaction(IsolationLevel isolationLevel) {
- IntPtr hscp;
- Bid.ScopeEnter(out hscp, "<prov.SqlConnection.BeginDbTransaction|API> %d#, isolationLevel=%d{ds.IsolationLevel}", ObjectID, (int)isolationLevel);
- try {
- DbTransaction transaction = BeginTransaction(isolationLevel);
- // VSTFDEVDIV# 560355 - InnerConnection doesn't maintain a ref on the outer connection (this) and
- // subsequently leaves open the possibility that the outer connection could be GC'ed before the SqlTransaction
- // is fully hooked up (leaving a DbTransaction with a null connection property). Ensure that this is reachable
- // until the completion of BeginTransaction with KeepAlive
- GC.KeepAlive(this);
- return transaction;
- }
- finally {
- Bid.ScopeLeave(ref hscp);
- }
- }
- public SqlTransaction BeginTransaction(IsolationLevel iso, string transactionName) {
- WaitForPendingReconnection();
- SqlStatistics statistics = null;
- IntPtr hscp;
- string xactName = ADP.IsEmpty(transactionName)? "None" : transactionName;
- Bid.ScopeEnter(out hscp, "<sc.SqlConnection.BeginTransaction|API> %d#, iso=%d{ds.IsolationLevel}, transactionName='%ls'\n", ObjectID, (int)iso,
- xactName);
- try {
- statistics = SqlStatistics.StartTimer(Statistics);
- // NOTE: we used to throw an exception if the transaction name was empty
- // (see MDAC 50292) but that was incorrect because we have a BeginTransaction
- // method that doesn't have a transactionName argument.
- SqlTransaction transaction;
- bool isFirstAttempt = true;
- do {
- transaction = GetOpenConnection().BeginSqlTransaction(iso, transactionName, isFirstAttempt); // do not reconnect twice
- Debug.Assert(isFirstAttempt || !transaction.InternalTransaction.ConnectionHasBeenRestored, "Restored connection on non-first attempt");
- isFirstAttempt = false;
- } while (transaction.InternalTransaction.ConnectionHasBeenRestored);
- // SQLBU 503873 The GetOpenConnection line above doesn't keep a ref on the outer connection (this),
- // and it could be collected before the inner connection can hook it to the transaction, resulting in
- // a transaction with a null connection property. Use GC.KeepAlive to ensure this doesn't happen.
- GC.KeepAlive(this);
- return transaction;
- }
- finally {
- Bid.ScopeLeave(ref hscp);
- SqlStatistics.StopTimer(statistics);
- }
- }
- override public void ChangeDatabase(string database) {
- SqlStatistics statistics = null;
- RepairInnerConnection();
- Bid.CorrelationTrace("<sc.SqlConnection.ChangeDatabase|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
- TdsParser bestEffortCleanupTarget = null;
- RuntimeHelpers.PrepareConstrainedRegions();
- try {
- #if DEBUG
- TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
- RuntimeHelpers.PrepareConstrainedRegions();
- try {
- tdsReliabilitySection.Start();
- #else
- {
- #endif //DEBUG
- bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
- statistics = SqlStatistics.StartTimer(Statistics);
- InnerConnection.ChangeDatabase(database);
- }
- #if DEBUG
- finally {
- tdsReliabilitySection.Stop();
- }
- #endif //DEBUG
- }
- catch (System.OutOfMemoryException e) {
- Abort(e);
- throw;
- }
- catch (System.StackOverflowException e) {
- Abort(e);
- throw;
- }
- catch (System.Threading.ThreadAbortException e) {
- Abort(e);
- SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
- throw;
- }
- finally {
- SqlStatistics.StopTimer(statistics);
- }
- }
- static public void ClearAllPools() {
- (new SqlClientPermission(PermissionState.Unrestricted)).Demand();
- SqlConnectionFactory.SingletonInstance.ClearAllPools();
- }
- static public void ClearPool(SqlConnection connection) {
- ADP.CheckArgumentNull(connection, "connection");
- DbConnectionOptions connectionOptions = connection.UserConnectionOptions;
- if (null != connectionOptions) {
- connectionOptions.DemandPermission();
- if (connection.IsContextConnection) {
- throw SQL.NotAvailableOnContextConnection();
- }
- SqlConnectionFactory.SingletonInstance.ClearPool(connection);
- }
- }
- object ICloneable.Clone() {
- SqlConnection clone = new SqlConnection(this);
- Bid.Trace("<sc.SqlConnection.Clone|API> %d#, clone=%d#\n", ObjectID, clone.ObjectID);
- return clone;
- }
- void CloseInnerConnection() {
- // CloseConnection() now handles the lock
- // The SqlInternalConnectionTds is set to OpenBusy during close, once this happens the cast below will fail and
- // the command will no longer be cancelable. It might be desirable to be able to cancel the close opperation, but this is
- // outside of the scope of Whidbey RTM. See (SqlCommand::Cancel) for other lock.
- InnerConnection.CloseConnection(this, ConnectionFactory);
- }
- override public void Close() {
- IntPtr hscp;
- Bid.ScopeEnter(out hscp, "<sc.SqlConnection.Close|API> %d#" , ObjectID);
- Bid.CorrelationTrace("<sc.SqlConnection.Close|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
- try {
- SqlStatistics statistics = null;
- TdsParser bestEffortCleanupTarget = null;
- RuntimeHelpers.PrepareConstrainedRegions();
- try {
- #if DEBUG
- TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
- RuntimeHelpers.PrepareConstrainedRegions();
- try {
- tdsReliabilitySection.Start();
- #else
- {
- #endif //DEBUG
- bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(this);
- statistics = SqlStatistics.StartTimer(Statistics);
- Task reconnectTask = _currentReconnectionTask;
- if (reconnectTask != null && !reconnectTask.IsCompleted) {
- CancellationTokenSource cts = _reconnectionCancellationSource;
- if (cts != null) {
- cts.Cancel();
- }
- AsyncHelper.WaitForCompletion(reconnectTask, 0, null, rethrowExceptions: false); // we do not need to deal with possible exceptions in reconnection
- if (State != ConnectionState.Open) {// if we cancelled before the connection was opened
- OnStateChange(DbConnectionInternal.StateChangeClosed);
- }
- }
- CancelOpenAndWait();
- CloseInnerConnection();
- GC.SuppressFinalize(this);
- if (null != Statistics) {
- ADP.TimerCurrent(out _statistics._closeTimestamp);
- }
- }
- #if DEBUG
- finally {
- tdsReliabilitySection.Stop();
- }
- #endif //DEBUG
- }
- catch (System.OutOfMemoryException e) {
- Abort(e);
- throw;
- }
- catch (System.StackOverflowException e) {
- Abort(e);
- throw;
- }
- catch (System.Threading.ThreadAbortException e) {
- Abort(e);
- SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
- throw;
- }
- finally {
- SqlStatistics.StopTimer(statistics);
- //dispose windows identity once connection is closed.
- if (_lastIdentity != null) {
- _lastIdentity.Dispose();
- }
- }
- }
- finally {
- SqlDebugContext sdc = _sdc;
- _sdc = null;
- Bid.ScopeLeave(ref hscp);
- if (sdc != null) {
- sdc.Dispose();
- }
- }
- }
- new public SqlCommand CreateCommand() {
- return new SqlCommand(null, this);
- }
- private void DisposeMe(bool disposing) { // MDAC 65459
- // clear credential and AccessToken here rather than in IDisposable.Dispose as these are specific to SqlConnection only
- // IDisposable.Dispose is generated code from a template and used by other providers as well
- _credential = null;
- _accessToken = null;
- if (!disposing) {
- // DevDiv2 Bug 457934:SQLConnection leaks when not disposed
- // http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/457934
- // For non-pooled connections we need to make sure that if the SqlConnection was not closed, then we release the GCHandle on the stateObject to allow it to be GCed
- // For pooled connections, we will rely on the pool reclaiming the connection
- var innerConnection = (InnerConnection as SqlInternalConnectionTds);
- if ((innerConnection != null) && (!innerConnection.ConnectionOptions.Pooling)) {
- var parser = innerConnection.Parser;
- if ((parser != null) && (parser.…