1 //------------------------------------------------------------------------------
2 // <copyright file="SqlConnectionFactory.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">Microsoft</owner>
6 // <owner current="true" primary="false">Microsoft</owner>
7 //------------------------------------------------------------------------------
9 namespace System.Data.SqlClient
12 using System.Data.Common;
13 using System.Data.ProviderBase;
14 using System.Collections.Specialized;
15 using System.Configuration;
16 using System.Diagnostics;
18 using System.Runtime.Versioning;
20 using Microsoft.SqlServer.Server;
23 sealed internal class SqlConnectionFactory : DbConnectionFactory {
24 private SqlConnectionFactory() : base(SqlPerformanceCounters.SingletonInstance) {}
26 public static readonly SqlConnectionFactory SingletonInstance = new SqlConnectionFactory();
27 private const string _metaDataXml = "MetaDataXml";
29 override public DbProviderFactory ProviderFactory {
31 return SqlClientFactory.Instance;
35 override protected DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection) {
36 return CreateConnection(options, poolKey, poolGroupProviderInfo, pool, owningConnection, userOptions: null);
39 override protected DbConnectionInternal CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions) {
40 SqlConnectionString opt = (SqlConnectionString)options;
41 SqlConnectionPoolKey key = (SqlConnectionPoolKey) poolKey;
42 SqlInternalConnection result = null;
43 SessionData recoverySessionData = null;
44 SqlConnection sqlOwningConnection = owningConnection as SqlConnection;
45 bool applyTransientFaultHandling = sqlOwningConnection != null ? sqlOwningConnection._applyTransientFaultHandling : false;
47 SqlConnectionString userOpt = null;
48 if (userOptions != null) {
49 userOpt = (SqlConnectionString)userOptions;
51 else if (sqlOwningConnection != null) {
52 userOpt = (SqlConnectionString)(sqlOwningConnection.UserConnectionOptions);
55 if (sqlOwningConnection != null) {
56 recoverySessionData = sqlOwningConnection._recoverySessionData;
59 if (opt.ContextConnection) {
60 result = GetContextConnection(opt, poolGroupProviderInfo);
63 bool redirectedUserInstance = false;
64 DbConnectionPoolIdentity identity = null;
66 // Pass DbConnectionPoolIdentity to SqlInternalConnectionTds if using integrated security.
67 // Used by notifications.
68 if (opt.IntegratedSecurity || opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated) {
70 identity = pool.Identity;
73 identity = DbConnectionPoolIdentity.GetCurrent();
77 // FOLLOWING IF BLOCK IS ENTIRELY FOR SSE USER INSTANCES
78 // If "user instance=true" is in the connection string, we're using SSE user instances
79 if (opt.UserInstance) {
80 // opt.DataSource is used to create the SSE connection
81 redirectedUserInstance = true;
84 if ( (null == pool) ||
85 (null != pool && pool.Count <= 0) ) { // Non-pooled or pooled and no connections in the pool.
87 SqlInternalConnectionTds sseConnection = null;
89 // What about a failure - throw? YES!
94 SqlConnectionString sseopt = new SqlConnectionString(opt, opt.DataSource, true /* user instance=true */, false /* set Enlist = false */);
95 sseConnection = new SqlInternalConnectionTds(identity, sseopt, key.Credential, null, "", null, false, applyTransientFaultHandling: applyTransientFaultHandling);
96 // NOTE: Retrieve <UserInstanceName> here. This user instance name will be used below to connect to the Sql Express User Instance.
97 instanceName = sseConnection.InstanceName;
99 if (!instanceName.StartsWith("\\\\.\\", StringComparison.Ordinal)) {
100 throw SQL.NonLocalSSEInstance();
103 if (null != pool) { // Pooled connection - cache result
104 SqlConnectionPoolProviderInfo providerInfo = (SqlConnectionPoolProviderInfo) pool.ProviderInfo;
105 // No lock since we are already in creation mutex
106 providerInfo.InstanceName = instanceName;
110 if (null != sseConnection) {
111 sseConnection.Dispose();
115 else { // Cached info from pool.
116 SqlConnectionPoolProviderInfo providerInfo = (SqlConnectionPoolProviderInfo) pool.ProviderInfo;
117 // No lock since we are already in creation mutex
118 instanceName = providerInfo.InstanceName;
121 // NOTE: Here connection option opt is cloned to set 'instanceName=<UserInstanceName>' that was
122 // retrieved from the previous SSE connection. For this UserInstance connection 'Enlist=True'.
123 // options immutable - stored in global hash - don't modify
124 opt = new SqlConnectionString(opt, instanceName, false /* user instance=false */, null /* do not modify the Enlist value */);
125 poolGroupProviderInfo = null; // null so we do not pass to constructor below...
127 result = new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData, pool, key.AccessToken, applyTransientFaultHandling: applyTransientFaultHandling);
132 protected override DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous) {
133 Debug.Assert(!ADP.IsEmpty(connectionString), "empty connectionString");
134 SqlConnectionString result = new SqlConnectionString(connectionString);
138 override internal DbConnectionPoolProviderInfo CreateConnectionPoolProviderInfo(DbConnectionOptions connectionOptions){
139 DbConnectionPoolProviderInfo providerInfo = null;
141 if (((SqlConnectionString) connectionOptions).UserInstance) {
142 providerInfo = new SqlConnectionPoolProviderInfo();
148 override protected DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions( DbConnectionOptions connectionOptions ) {
149 SqlConnectionString opt = (SqlConnectionString)connectionOptions;
151 DbConnectionPoolGroupOptions poolingOptions = null;
153 if (!opt.ContextConnection && opt.Pooling) { // never pool context connections.
154 int connectionTimeout = opt.ConnectTimeout;
156 if ((0 < connectionTimeout) && (connectionTimeout < Int32.MaxValue/1000))
157 connectionTimeout *= 1000;
158 else if (connectionTimeout >= Int32.MaxValue/1000)
159 connectionTimeout = Int32.MaxValue;
161 poolingOptions = new DbConnectionPoolGroupOptions(
162 opt.IntegratedSecurity || opt.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated,
166 opt.LoadBalanceTimeout,
169 return poolingOptions;
172 // SxS (VSDD 545786): metadata files are opened from <.NetRuntimeFolder>\CONFIG\<metadatafilename.xml>
173 // this operation is safe in SxS because the file is opened in read-only mode and each NDP runtime accesses its own copy of the metadata
174 // under the runtime folder.
175 [ResourceExposure(ResourceScope.None)]
176 [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
177 override protected DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection, out bool cacheMetaDataFactory){
178 Debug.Assert (internalConnection != null, "internalConnection may not be null.");
179 cacheMetaDataFactory = false;
181 if (internalConnection is SqlInternalConnectionSmi) {
182 throw SQL.NotAvailableOnContextConnection();
185 Stream XMLStream =null;
186 #if !NO_CONFIGURATION
187 NameValueCollection settings = (NameValueCollection)PrivilegedConfigurationManager.GetSection("system.data.sqlclient");
188 if (settings != null){
189 string [] values = settings.GetValues(_metaDataXml);
190 if (values != null) {
191 XMLStream = ADP.GetXmlStreamFromValues(values, _metaDataXml);
196 // if the xml was not obtained from machine.config use the embedded XML resource
197 if (XMLStream == null){
198 XMLStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("System.Data.SqlClient.SqlMetaData.xml");
199 cacheMetaDataFactory = true;
201 Debug.Assert (XMLStream != null,"XMLstream may not be null.");
203 return new SqlMetaDataFactory (XMLStream,
204 internalConnection.ServerVersion,
205 internalConnection.ServerVersion); //internalConnection.ServerVersionNormalized);
209 override internal DbConnectionPoolGroupProviderInfo CreateConnectionPoolGroupProviderInfo (DbConnectionOptions connectionOptions) {
210 return new SqlConnectionPoolGroupProviderInfo((SqlConnectionString)connectionOptions);
214 internal static SqlConnectionString FindSqlConnectionOptions(SqlConnectionPoolKey key) {
215 SqlConnectionString connectionOptions = (SqlConnectionString )SingletonInstance.FindConnectionOptions(key);
216 if (null == connectionOptions) {
217 connectionOptions = new SqlConnectionString(key.ConnectionString);
219 if (connectionOptions.IsEmpty) {
220 throw ADP.NoConnectionString();
222 return connectionOptions;
225 private SqlInternalConnectionSmi GetContextConnection(SqlConnectionString options, object providerInfo) {
226 SmiContext smiContext = SmiContextFactory.Instance.GetCurrentContext();
228 SqlInternalConnectionSmi result = (SqlInternalConnectionSmi)smiContext.GetContextValue((int)SmiContextFactory.ContextKey.Connection);
230 // context connections are automatically re-useable if they exist unless they've been doomed.
231 if (null == result || result.IsConnectionDoomed) {
232 if (null != result) {
233 result.Dispose(); // A doomed connection is a messy thing. Dispose of it promptly in nearest receptacle.
236 result = new SqlInternalConnectionSmi(options, smiContext);
237 smiContext.SetContextValue((int)SmiContextFactory.ContextKey.Connection, result);
245 override internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection) {
246 SqlConnection c = (connection as SqlConnection);
253 override internal DbConnectionInternal GetInnerConnection(DbConnection connection) {
254 SqlConnection c = (connection as SqlConnection);
256 return c.InnerConnection;
261 override protected int GetObjectId(DbConnection connection) {
262 SqlConnection c = (connection as SqlConnection);
269 override internal void PermissionDemand(DbConnection outerConnection) {
270 SqlConnection c = (outerConnection as SqlConnection);
272 c.PermissionDemand();
276 override internal void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup) {
277 SqlConnection c = (outerConnection as SqlConnection);
279 c.PoolGroup = poolGroup;
283 override internal void SetInnerConnectionEvent(DbConnection owningObject, DbConnectionInternal to) {
284 SqlConnection c = (owningObject as SqlConnection);
286 c.SetInnerConnectionEvent(to);
290 override internal bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from) {
291 SqlConnection c = (owningObject as SqlConnection);
293 return c.SetInnerConnectionFrom(to, from);
298 override internal void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to) {
299 SqlConnection c = (owningObject as SqlConnection);
301 c.SetInnerConnectionTo(to);
307 sealed internal class SqlPerformanceCounters : DbConnectionPoolCounters {
308 private const string CategoryName = ".NET Data Provider for SqlServer";
309 private const string CategoryHelp = "Counters for System.Data.SqlClient";
311 public static readonly SqlPerformanceCounters SingletonInstance = new SqlPerformanceCounters();
314 [System.Diagnostics.PerformanceCounterPermissionAttribute(System.Security.Permissions.SecurityAction.Assert, PermissionAccess=PerformanceCounterPermissionAccess.Write, MachineName=".", CategoryName=CategoryName)]
315 private SqlPerformanceCounters() : base (CategoryName, CategoryHelp) {