2 // System.Data.SqlClient.SqlConnection.cs
5 // Rodrigo Moya (rodrigo@ximian.com)
6 // Daniel Morgan (danmorg@sc.rr.com)
7 // Tim Coleman (tim@timcoleman.com)
9 // (C) Ximian, Inc 2002
10 // (C) Daniel Morgan 2002
11 // Copyright (C) Tim Coleman, 2002
14 using Mono.Data.Tds.Protocol;
16 using System.Collections;
17 using System.Collections.Specialized;
18 using System.ComponentModel;
20 using System.Data.Common;
21 using System.EnterpriseServices;
26 namespace System.Data.SqlClient {
27 [DefaultEvent ("InfoMessage")]
28 public sealed class SqlConnection : Component, IDbConnection, ICloneable
31 bool disposed = false;
33 // The set of SQL connection pools
34 static Hashtable SqlConnectionPools = new Hashtable ();
36 // The current connection pool
37 SqlConnectionPool pool;
39 // The connection string that identifies this connection
40 string connectionString = null;
42 // The transaction object for the current transaction
43 SqlTransaction transaction = null;
45 // Connection parameters
46 TdsConnectionParameters parms = new TdsConnectionParameters ();
50 int connectionTimeout;
57 ConnectionState state = ConnectionState.Closed;
59 SqlDataReader dataReader = null;
60 XmlReader xmlReader = null;
69 public SqlConnection ()
74 public SqlConnection (string connectionString)
76 ConnectionString = connectionString;
79 #endregion // Constructors
83 [DataCategory ("Data")]
84 [DataSysDescription ("Information used to connect to a DataSource, such as 'Data Source=x;Initial Catalog=x;Integrated Security=SSPI'.")]
86 [RecommendedAsConfigurable (true)]
87 [RefreshProperties (RefreshProperties.All)]
88 public string ConnectionString {
89 get { return connectionString; }
90 set { SetConnectionString (value); }
93 [DataSysDescription ("Current connection timeout value, 'Connect Timeout=X' in the ConnectionString.")]
94 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
95 public int ConnectionTimeout {
96 get { return connectionTimeout; }
99 [DataSysDescription ("Current SQL Server database, 'Initial Catalog=X' in the ConnectionString.")]
100 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
101 public string Database {
102 get { return tds.Database; }
105 internal SqlDataReader DataReader {
106 get { return dataReader; }
107 set { dataReader = value; }
110 [DataSysDescription ("Current SqlServer that the connection is opened to, 'Data Source=X' in the ConnectionString.")]
111 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
112 public string DataSource {
113 get { return dataSource; }
116 [DataSysDescription ("Network packet size, 'Packet Size=x' in the ConnectionString.")]
117 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
118 public int PacketSize {
119 get { return packetSize; }
123 [DataSysDescription ("Version of the SQL Server accessed by the SqlConnection.")]
124 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
125 public string ServerVersion {
126 get { return tds.ServerVersion; }
130 [DataSysDescription ("The ConnectionState indicating whether the connection is open or closed.")]
131 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
132 public ConnectionState State {
133 get { return state; }
140 internal SqlTransaction Transaction {
141 get { return transaction; }
142 set { transaction = value; }
145 [DataSysDescription ("Workstation Id, 'Workstation Id=x' in the ConnectionString.")]
146 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
147 public string WorkstationId {
148 get { return parms.Hostname; }
151 internal XmlReader XmlReader {
152 get { return xmlReader; }
153 set { xmlReader = value; }
156 #endregion // Properties
160 [DataCategory ("InfoMessage")]
161 [DataSysDescription ("Event triggered when messages arrive from the DataSource.")]
162 public event SqlInfoMessageEventHandler InfoMessage;
164 [DataCategory ("StateChange")]
165 [DataSysDescription ("Event triggered when the connection changes state.")]
166 public event StateChangeEventHandler StateChange;
172 private void ErrorHandler (object sender, TdsInternalErrorMessageEventArgs e)
174 throw new SqlException (e.Class, e.LineNumber, e.Message, e.Number, e.Procedure, e.Server, "Mono SqlClient Data Provider", e.State);
177 private void MessageHandler (object sender, TdsInternalInfoMessageEventArgs e)
179 OnSqlInfoMessage (CreateSqlInfoMessageEvent (e.Errors));
182 #endregion // Delegates
186 public SqlTransaction BeginTransaction ()
188 return BeginTransaction (IsolationLevel.ReadCommitted, String.Empty);
191 public SqlTransaction BeginTransaction (IsolationLevel iso)
193 return BeginTransaction (iso, String.Empty);
196 public SqlTransaction BeginTransaction (string transactionName)
198 return BeginTransaction (IsolationLevel.ReadCommitted, transactionName);
201 public SqlTransaction BeginTransaction (IsolationLevel iso, string transactionName)
203 if (state == ConnectionState.Closed)
204 throw new InvalidOperationException ("The connection is not open.");
205 if (transaction != null)
206 throw new InvalidOperationException ("SqlConnection does not support parallel transactions.");
208 string isolevel = String.Empty;
210 case IsolationLevel.Chaos:
213 case IsolationLevel.ReadCommitted:
214 isolevel = "READ COMMITTED";
216 case IsolationLevel.ReadUncommitted:
217 isolevel = "READ UNCOMMITTED";
219 case IsolationLevel.RepeatableRead:
220 isolevel = "REPEATABLE READ";
222 case IsolationLevel.Serializable:
223 isolevel = "SERIALIZABLE";
227 tds.ExecuteNonQuery (String.Format ("SET TRANSACTION ISOLATION LEVEL {0};BEGIN TRANSACTION {1}", isolevel, transactionName));
229 transaction = new SqlTransaction (this, iso);
233 public void ChangeDatabase (string database)
235 if (!IsValidDatabaseName (database))
236 throw new ArgumentException (String.Format ("The database name {0} is not valid."));
237 if (state != ConnectionState.Open)
238 throw new InvalidOperationException ("The connection is not open.");
239 tds.ExecuteNonQuery (String.Format ("use {0}", database));
242 private void ChangeState (ConnectionState currentState)
244 ConnectionState originalState = state;
245 state = currentState;
246 OnStateChange (CreateStateChangeEvent (originalState, currentState));
251 if (transaction != null && transaction.IsOpen)
252 transaction.Rollback ();
254 pool.ReleaseConnection (tds);
258 tds.TdsErrorMessage -= new TdsInternalErrorMessageEventHandler (ErrorHandler);
259 tds.TdsInfoMessage -= new TdsInternalInfoMessageEventHandler (MessageHandler);
261 ChangeState (ConnectionState.Closed);
264 public SqlCommand CreateCommand ()
266 SqlCommand command = new SqlCommand ();
267 command.Connection = this;
271 private SqlInfoMessageEventArgs CreateSqlInfoMessageEvent (TdsInternalErrorCollection errors)
273 return new SqlInfoMessageEventArgs (errors);
276 private StateChangeEventArgs CreateStateChangeEvent (ConnectionState originalState, ConnectionState currentState)
278 return new StateChangeEventArgs (originalState, currentState);
281 protected override void Dispose (bool disposing)
285 if (State == ConnectionState.Open)
290 base.Dispose (disposing);
295 [MonoTODO ("Not sure what this means at present.")]
296 public void EnlistDistributedTransaction (ITransaction transaction)
298 throw new NotImplementedException ();
301 object ICloneable.Clone ()
303 return new SqlConnection (ConnectionString);
306 IDbTransaction IDbConnection.BeginTransaction ()
308 return BeginTransaction ();
311 IDbTransaction IDbConnection.BeginTransaction (IsolationLevel iso)
313 return BeginTransaction (iso);
316 IDbCommand IDbConnection.CreateCommand ()
318 return CreateCommand ();
321 void IDisposable.Dispose ()
324 GC.SuppressFinalize (this);
329 if (connectionString == null)
330 throw new InvalidOperationException ("Connection string has not been initialized.");
334 tds = new Tds70 (DataSource, port, PacketSize, ConnectionTimeout);
336 pool = (SqlConnectionPool) SqlConnectionPools [connectionString];
338 pool = new SqlConnectionPool (dataSource, port, packetSize, ConnectionTimeout, minPoolSize, maxPoolSize);
339 SqlConnectionPools [connectionString] = pool;
341 tds = pool.AllocateConnection ();
344 catch (TdsTimeoutException e) {
345 throw SqlException.FromTdsInternalException ((TdsInternalException) e);
348 tds.TdsErrorMessage += new TdsInternalErrorMessageEventHandler (ErrorHandler);
349 tds.TdsInfoMessage += new TdsInternalInfoMessageEventHandler (MessageHandler);
351 if (!tds.IsConnected)
353 else if (connectionReset)
354 tds.ExecuteNonQuery ("EXEC sp_reset_connection");
356 ChangeState (ConnectionState.Open);
359 void SetConnectionString (string connectionString)
361 connectionString += ";";
362 NameValueCollection parameters = new NameValueCollection ();
364 if (connectionString == String.Empty)
367 bool inQuote = false;
368 bool inDQuote = false;
370 string name = String.Empty;
371 string value = String.Empty;
372 StringBuilder sb = new StringBuilder ();
374 foreach (char c in connectionString)
381 inDQuote = !inDQuote;
384 if (!inDQuote && !inQuote) {
385 if (name != String.Empty && name != null) {
386 value = sb.ToString ();
387 parameters [name.ToUpper ().Trim ()] = value.Trim ();
390 value = String.Empty;
391 sb = new StringBuilder ();
397 if (!inDQuote && !inQuote) {
398 name = sb.ToString ();
399 sb = new StringBuilder ();
410 if (this.ConnectionString == null)
412 SetDefaultConnectionParameters (parameters);
415 SetProperties (parameters);
417 this.connectionString = connectionString;
420 void SetDefaultConnectionParameters (NameValueCollection parameters)
422 if (null == parameters.Get ("APPLICATION NAME"))
423 parameters["APPLICATION NAME"] = "Mono SqlClient Data Provider";
424 if (null == parameters.Get ("CONNECT TIMEOUT") && null == parameters.Get ("CONNECTION TIMEOUT"))
425 parameters["CONNECT TIMEOUT"] = "15";
426 if (null == parameters.Get ("CONNECTION LIFETIME"))
427 parameters["CONNECTION LIFETIME"] = "0";
428 if (null == parameters.Get ("CONNECTION RESET"))
429 parameters["CONNECTION RESET"] = "true";
430 if (null == parameters.Get ("ENLIST"))
431 parameters["ENLIST"] = "true";
432 if (null == parameters.Get ("INTEGRATED SECURITY") && null == parameters.Get ("TRUSTED_CONNECTION"))
433 parameters["INTEGRATED SECURITY"] = "false";
434 if (null == parameters.Get ("MAX POOL SIZE"))
435 parameters["MAX POOL SIZE"] = "100";
436 if (null == parameters.Get ("MIN POOL SIZE"))
437 parameters["MIN POOL SIZE"] = "0";
438 if (null == parameters.Get ("NETWORK LIBRARY") && null == parameters.Get ("NET"))
439 parameters["NETWORK LIBRARY"] = "dbmssocn";
440 if (null == parameters.Get ("PACKET SIZE"))
441 parameters["PACKET SIZE"] = "512";
442 if (null == parameters.Get ("PERSIST SECURITY INFO"))
443 parameters["PERSIST SECURITY INFO"] = "false";
444 if (null == parameters.Get ("POOLING"))
445 parameters["POOLING"] = "true";
446 if (null == parameters.Get ("WORKSTATION ID"))
447 parameters["WORKSTATION ID"] = Dns.GetHostByName ("localhost").HostName;
450 private void SetProperties (NameValueCollection parameters)
453 foreach (string name in parameters) {
454 value = parameters[name];
457 case "APPLICATION NAME" :
458 parms.ApplicationName = value;
460 case "ATTACHDBFILENAME" :
461 case "EXTENDED PROPERTIES" :
462 case "INITIAL FILE NAME" :
464 case "CONNECT TIMEOUT" :
465 case "CONNECTION TIMEOUT" :
466 connectionTimeout = Int32.Parse (value);
468 case "CONNECTION LIFETIME" :
470 case "CONNECTION RESET" :
471 connectionReset = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
473 case "CURRENT LANGUAGE" :
474 parms.Language = value;
480 case "NETWORK ADDRESS" :
485 case "INITIAL CATALOG" :
487 parms.Database = value;
489 case "INTEGRATED SECURITY" :
490 case "TRUSTED_CONNECTION" :
492 case "MAX POOL SIZE" :
493 maxPoolSize = Int32.Parse (value);
495 case "MIN POOL SIZE" :
496 minPoolSize = Int32.Parse (value);
499 case "NETWORK LIBRARY" :
500 if (!value.ToUpper ().Equals ("DBMSSOCN"))
501 throw new ArgumentException ("Unsupported network library.");
504 packetSize = Int32.Parse (value);
508 parms.Password = value;
510 case "PERSIST SECURITY INFO" :
513 pooling = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
518 case "WORKSTATION ID" :
519 parms.Hostname = value;
525 static bool IsValidDatabaseName (string database)
527 if (database.Length > 32 || database.Length < 1)
530 if (database[0] == '"' && database[database.Length] == '"')
531 database = database.Substring (1, database.Length - 2);
532 else if (Char.IsDigit (database[0]))
535 if (database[0] == '_')
538 foreach (char c in database.Substring (1, database.Length - 1))
539 if (!Char.IsLetterOrDigit (c) && c != '_')
544 private void OnSqlInfoMessage (SqlInfoMessageEventArgs value)
546 if (InfoMessage != null)
547 InfoMessage (this, value);
550 private void OnStateChange (StateChangeEventArgs value)
552 if (StateChange != null)
553 StateChange (this, value);
556 #endregion // Methods