Add #if-s for mobile builds.
[mono.git] / mcs / class / referencesource / System.Data / System / Data / SqlClient / SqlConnectionFactory.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SqlConnectionFactory.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">[....]</owner>
6 // <owner current="true" primary="false">[....]</owner>
7 //------------------------------------------------------------------------------
8
9 namespace System.Data.SqlClient
10 {
11     using System;
12     using System.Data.Common;
13     using System.Data.ProviderBase;
14     using System.Collections.Specialized;
15     using System.Configuration;
16     using System.Diagnostics;
17     using System.IO;
18     using System.Runtime.Versioning;
19
20     using Microsoft.SqlServer.Server;
21
22     
23     sealed internal class SqlConnectionFactory : DbConnectionFactory {
24         private SqlConnectionFactory() : base(SqlPerformanceCounters.SingletonInstance) {}
25
26         public static readonly SqlConnectionFactory SingletonInstance = new SqlConnectionFactory();
27         private const string _metaDataXml          = "MetaDataXml";
28
29         override public DbProviderFactory ProviderFactory {
30             get {
31                 return SqlClientFactory.Instance;
32             }
33         }
34
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);
37         }
38
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
45             SqlConnectionString userOpt = null;
46             if (userOptions != null) {
47                 userOpt = (SqlConnectionString)userOptions;
48             }
49             else if (owningConnection != null) {
50                 userOpt = (SqlConnectionString)(((SqlConnection)owningConnection).UserConnectionOptions);                
51             }
52
53             if (owningConnection != null) {
54                 recoverySessionData = ((SqlConnection)owningConnection)._recoverySessionData;
55             }
56
57             if (opt.ContextConnection) {
58                 result = GetContextConnection(opt, poolGroupProviderInfo);
59             }
60             else {
61                 bool redirectedUserInstance       = false;
62                 DbConnectionPoolIdentity identity = null;
63
64                 // Pass DbConnectionPoolIdentity to SqlInternalConnectionTds if using integrated security.
65                 // Used by notifications.
66                 if (opt.IntegratedSecurity) {
67                     if (pool != null) {
68                         identity = pool.Identity;
69                     }
70                     else {
71                         identity = DbConnectionPoolIdentity.GetCurrent();
72                     }
73                 }
74
75                 // FOLLOWING IF BLOCK IS ENTIRELY FOR SSE USER INSTANCES
76                 // If "user instance=true" is in the connection string, we're using SSE user instances
77                 if (opt.UserInstance) {
78                     // opt.DataSource is used to create the SSE connection
79                     redirectedUserInstance = true;
80                     string instanceName;
81
82                     if ( (null == pool) || 
83                          (null != pool && pool.Count <= 0) ) { // Non-pooled or pooled and no connections in the pool.
84
85                         SqlInternalConnectionTds sseConnection = null;
86                         try {
87                             // What about a failure - throw?  YES!
88                             // 
89
90
91
92                             SqlConnectionString sseopt = new SqlConnectionString(opt, opt.DataSource, true /* user instance=true */, false /* set Enlist = false */);
93                             sseConnection = new SqlInternalConnectionTds(identity, sseopt, key.Credential, null, "", null, false);
94                             // NOTE: Retrieve <UserInstanceName> here. This user instance name will be used below to connect to the Sql Express User Instance.
95                             instanceName = sseConnection.InstanceName;
96
97                             if (!instanceName.StartsWith("\\\\.\\", StringComparison.Ordinal)) {
98                                 throw SQL.NonLocalSSEInstance();
99                             }
100
101                             if (null != pool) { // Pooled connection - cache result
102                                 SqlConnectionPoolProviderInfo providerInfo = (SqlConnectionPoolProviderInfo) pool.ProviderInfo;
103                                 // No lock since we are already in creation mutex
104                                 providerInfo.InstanceName = instanceName;
105                             }
106                         }
107                         finally {
108                             if (null != sseConnection) {
109                                 sseConnection.Dispose();
110                             }
111                         }
112                     }
113                     else { // Cached info from pool.
114                         SqlConnectionPoolProviderInfo providerInfo = (SqlConnectionPoolProviderInfo) pool.ProviderInfo;
115                         // No lock since we are already in creation mutex
116                         instanceName = providerInfo.InstanceName;
117                     }
118
119                     // NOTE: Here connection option opt is cloned to set 'instanceName=<UserInstanceName>' that was
120                     //       retrieved from the previous SSE connection. For this UserInstance connection 'Enlist=True'.
121                     // options immutable - stored in global hash - don't modify
122                     opt = new SqlConnectionString(opt, instanceName, false /* user instance=false */, null /* do not modify the Enlist value */);
123                     poolGroupProviderInfo = null; // null so we do not pass to constructor below...
124                 }
125                 result = new SqlInternalConnectionTds(identity, opt, key.Credential, poolGroupProviderInfo, "", null, redirectedUserInstance, userOpt, recoverySessionData);            
126             }
127             return result;
128         }
129
130         protected override DbConnectionOptions CreateConnectionOptions(string connectionString, DbConnectionOptions previous) {
131             Debug.Assert(!ADP.IsEmpty(connectionString), "empty connectionString");
132             SqlConnectionString result = new SqlConnectionString(connectionString);
133             return result;
134         }
135
136         override internal DbConnectionPoolProviderInfo CreateConnectionPoolProviderInfo(DbConnectionOptions connectionOptions){
137             DbConnectionPoolProviderInfo providerInfo = null;
138
139             if (((SqlConnectionString) connectionOptions).UserInstance) {
140                 providerInfo = new SqlConnectionPoolProviderInfo();
141             }
142
143             return providerInfo;
144         }
145
146         override protected DbConnectionPoolGroupOptions CreateConnectionPoolGroupOptions( DbConnectionOptions connectionOptions ) {
147             SqlConnectionString opt = (SqlConnectionString)connectionOptions;
148
149             DbConnectionPoolGroupOptions poolingOptions = null;
150
151             if (!opt.ContextConnection && opt.Pooling) {    // never pool context connections.
152                 int connectionTimeout = opt.ConnectTimeout;
153
154                 if ((0 < connectionTimeout) && (connectionTimeout < Int32.MaxValue/1000))
155                     connectionTimeout *= 1000;
156                 else if (connectionTimeout >= Int32.MaxValue/1000)
157                     connectionTimeout = Int32.MaxValue;
158
159                 poolingOptions = new DbConnectionPoolGroupOptions(
160                                                     opt.IntegratedSecurity,
161                                                     opt.MinPoolSize,
162                                                     opt.MaxPoolSize,
163                                                     connectionTimeout,
164                                                     opt.LoadBalanceTimeout,
165                                                     opt.Enlist);
166             }
167             return poolingOptions;
168         }
169
170         // SxS (VSDD 545786): metadata files are opened from <.NetRuntimeFolder>\CONFIG\<metadatafilename.xml>
171         // 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
172         // under the runtime folder.
173         [ResourceExposure(ResourceScope.None)]
174         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
175         override protected DbMetaDataFactory CreateMetaDataFactory(DbConnectionInternal internalConnection, out bool cacheMetaDataFactory){
176             Debug.Assert (internalConnection != null, "internalConnection may not be null.");
177             cacheMetaDataFactory = false;
178
179             if (internalConnection is SqlInternalConnectionSmi) {
180                 throw SQL.NotAvailableOnContextConnection();
181             }
182
183             Stream XMLStream =null;
184 #if !NO_CONFIGURATION
185             NameValueCollection settings = (NameValueCollection)PrivilegedConfigurationManager.GetSection("system.data.sqlclient");
186             if (settings != null){
187                 string [] values = settings.GetValues(_metaDataXml);
188                 if (values != null) {
189                     XMLStream = ADP.GetXmlStreamFromValues(values, _metaDataXml);
190                 }
191             }
192 #endif
193
194             // if the xml was not obtained from machine.config use the embedded XML resource
195             if (XMLStream == null){
196                 XMLStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("System.Data.SqlClient.SqlMetaData.xml");
197                 cacheMetaDataFactory = true;
198             }
199             Debug.Assert (XMLStream != null,"XMLstream may not be null.");
200
201             return new SqlMetaDataFactory (XMLStream,
202                                           internalConnection.ServerVersion,
203                                           internalConnection.ServerVersion); //internalConnection.ServerVersionNormalized);
204
205         }
206
207         override internal DbConnectionPoolGroupProviderInfo CreateConnectionPoolGroupProviderInfo (DbConnectionOptions connectionOptions) {
208             return new SqlConnectionPoolGroupProviderInfo((SqlConnectionString)connectionOptions);
209         }
210
211
212         internal static SqlConnectionString FindSqlConnectionOptions(SqlConnectionPoolKey key) {
213             SqlConnectionString connectionOptions = (SqlConnectionString )SingletonInstance.FindConnectionOptions(key);
214             if (null == connectionOptions) {
215                 connectionOptions = new SqlConnectionString(key.ConnectionString);
216             }
217             if (connectionOptions.IsEmpty) {
218                 throw ADP.NoConnectionString();
219             }
220             return connectionOptions;
221         }
222
223         private SqlInternalConnectionSmi GetContextConnection(SqlConnectionString options, object providerInfo) {
224             SmiContext smiContext = SmiContextFactory.Instance.GetCurrentContext();
225
226             SqlInternalConnectionSmi result = (SqlInternalConnectionSmi)smiContext.GetContextValue((int)SmiContextFactory.ContextKey.Connection);
227
228             // context connections are automatically re-useable if they exist unless they've been doomed.
229             if (null == result || result.IsConnectionDoomed) {
230                 if (null != result) {
231                     result.Dispose();   // A doomed connection is a messy thing.  Dispose of it promptly in nearest receptacle.
232                 }
233
234                 result = new SqlInternalConnectionSmi(options, smiContext);
235                 smiContext.SetContextValue((int)SmiContextFactory.ContextKey.Connection, result);
236             }
237
238             result.Activate();
239
240             return result;
241         }
242
243         override internal DbConnectionPoolGroup GetConnectionPoolGroup(DbConnection connection) {
244             SqlConnection c = (connection as SqlConnection);
245             if (null != c) {
246                 return c.PoolGroup;
247             }
248             return null;
249         }
250
251         override internal DbConnectionInternal GetInnerConnection(DbConnection connection) {
252             SqlConnection c = (connection as SqlConnection);
253             if (null != c) {
254                 return c.InnerConnection;
255             }
256             return null;
257         }
258
259         override protected int GetObjectId(DbConnection connection) {
260             SqlConnection c = (connection as SqlConnection);
261             if (null != c) {
262                 return c.ObjectID;
263             }
264             return 0;
265         }
266
267         override internal void PermissionDemand(DbConnection outerConnection) {
268             SqlConnection c = (outerConnection as SqlConnection);
269             if (null != c) {
270                 c.PermissionDemand();
271             }
272         }
273
274         override internal void SetConnectionPoolGroup(DbConnection outerConnection, DbConnectionPoolGroup poolGroup) {
275             SqlConnection c = (outerConnection as SqlConnection);
276             if (null != c) {
277                 c.PoolGroup = poolGroup;
278             }
279         }
280
281         override internal void SetInnerConnectionEvent(DbConnection owningObject, DbConnectionInternal to) {
282             SqlConnection c = (owningObject as SqlConnection);
283             if (null != c) {
284                 c.SetInnerConnectionEvent(to);
285             }
286         }
287
288         override internal bool SetInnerConnectionFrom(DbConnection owningObject, DbConnectionInternal to, DbConnectionInternal from) {
289             SqlConnection c = (owningObject as SqlConnection);
290             if (null != c) {
291                 return c.SetInnerConnectionFrom(to, from);
292             }
293             return false;
294         }
295
296         override internal void SetInnerConnectionTo(DbConnection owningObject, DbConnectionInternal to) {
297             SqlConnection c = (owningObject as SqlConnection);
298             if (null != c) {
299                 c.SetInnerConnectionTo(to);
300             }
301         }
302
303     }
304
305     sealed internal class SqlPerformanceCounters : DbConnectionPoolCounters {
306         private const string CategoryName = ".NET Data Provider for SqlServer";
307         private const string CategoryHelp = "Counters for System.Data.SqlClient";
308
309         public static readonly SqlPerformanceCounters SingletonInstance = new SqlPerformanceCounters();
310
311 #if !MOBILE
312         [System.Diagnostics.PerformanceCounterPermissionAttribute(System.Security.Permissions.SecurityAction.Assert, PermissionAccess=PerformanceCounterPermissionAccess.Write, MachineName=".", CategoryName=CategoryName)]
313         private SqlPerformanceCounters() : base (CategoryName, CategoryHelp) {
314         }
315 #endif
316     }
317 }
318