SqlConnection.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. //
  2. // System.Data.SqlClient.SqlConnection.cs
  3. //
  4. // Author:
  5. // Rodrigo Moya ([email protected])
  6. // Daniel Morgan ([email protected])
  7. // Tim Coleman ([email protected])
  8. //
  9. // (C) Ximian, Inc 2002
  10. // (C) Daniel Morgan 2002
  11. // Copyright (C) Tim Coleman, 2002
  12. //
  13. using Mono.Data.TdsClient.Internal;
  14. using System;
  15. using System.Collections;
  16. using System.Collections.Specialized;
  17. using System.ComponentModel;
  18. using System.Data;
  19. using System.Data.Common;
  20. using System.EnterpriseServices;
  21. using System.Net;
  22. using System.Text;
  23. using System.Xml;
  24. namespace System.Data.SqlClient {
  25. public sealed class SqlConnection : Component, IDbConnection, ICloneable
  26. {
  27. #region Fields
  28. // The set of SQL connection pools
  29. static Hashtable SqlConnectionPools = new Hashtable ();
  30. // The current connection pool
  31. SqlConnectionPool pool;
  32. // The connection string that identifies this connection
  33. string connectionString = null;
  34. // The transaction object for the current transaction
  35. SqlTransaction transaction = null;
  36. // Connection parameters
  37. TdsConnectionParameters parms = new TdsConnectionParameters ();
  38. bool connectionReset;
  39. bool pooling;
  40. string dataSource;
  41. int connectionTimeout;
  42. int minPoolSize;
  43. int maxPoolSize;
  44. int packetSize;
  45. int port = 1433;
  46. // The current state
  47. ConnectionState state = ConnectionState.Closed;
  48. SqlDataReader dataReader = null;
  49. XmlReader xmlReader = null;
  50. // The TDS object
  51. ITds tds;
  52. #endregion // Fields
  53. #region Constructors
  54. public SqlConnection ()
  55. : this (String.Empty)
  56. {
  57. }
  58. public SqlConnection (string connectionString)
  59. {
  60. SetConnectionString (connectionString);
  61. this.connectionString = connectionString;
  62. }
  63. #endregion // Constructors
  64. #region Properties
  65. public string ConnectionString {
  66. get { return connectionString; }
  67. set { SetConnectionString (value); }
  68. }
  69. public int ConnectionTimeout {
  70. get { return connectionTimeout; }
  71. }
  72. public string Database {
  73. get { return tds.Database; }
  74. }
  75. internal SqlDataReader DataReader {
  76. get { return dataReader; }
  77. set { dataReader = value; }
  78. }
  79. public string DataSource {
  80. get { return dataSource; }
  81. }
  82. public int PacketSize {
  83. get { return packetSize; }
  84. }
  85. public string ServerVersion {
  86. get { return tds.ServerVersion; }
  87. }
  88. public ConnectionState State {
  89. get { return state; }
  90. }
  91. internal ITds Tds {
  92. get { return tds; }
  93. }
  94. internal SqlTransaction Transaction {
  95. get { return transaction; }
  96. }
  97. public string WorkstationId {
  98. get { return parms.Hostname; }
  99. }
  100. internal XmlReader XmlReader {
  101. get { return xmlReader; }
  102. set { xmlReader = value; }
  103. }
  104. #endregion // Properties
  105. #region Methods
  106. public SqlTransaction BeginTransaction ()
  107. {
  108. return BeginTransaction (IsolationLevel.ReadCommitted, String.Empty);
  109. }
  110. public SqlTransaction BeginTransaction (IsolationLevel iso)
  111. {
  112. return BeginTransaction (iso, String.Empty);
  113. }
  114. public SqlTransaction BeginTransaction (string transactionName)
  115. {
  116. return BeginTransaction (IsolationLevel.ReadCommitted, String.Empty);
  117. }
  118. public SqlTransaction BeginTransaction (IsolationLevel iso, string transactionName)
  119. {
  120. if (transaction != null)
  121. throw new InvalidOperationException ("SqlConnection does not support parallel transactions.");
  122. tds.ExecuteNonQuery (String.Format ("BEGIN TRANSACTION {0}", transactionName));
  123. CheckForErrors ();
  124. transaction = new SqlTransaction (this, iso);
  125. return transaction;
  126. }
  127. public void ChangeDatabase (string database)
  128. {
  129. if (!IsValidDatabaseName (database))
  130. throw new ArgumentException (String.Format ("The database name {0} is not valid."));
  131. if (state != ConnectionState.Open)
  132. throw new InvalidOperationException ("The connection is not open");
  133. tds.ExecuteNonQuery (String.Format ("use {0}", database));
  134. CheckForErrors ();
  135. }
  136. internal void CheckForErrors ()
  137. {
  138. if (tds.Errors.Count > 0)
  139. throw SqlException.FromTdsError (tds.Errors);
  140. }
  141. public void Close ()
  142. {
  143. if (transaction != null && transaction.IsOpen)
  144. transaction.Rollback ();
  145. if (pooling)
  146. pool.ReleaseConnection (tds);
  147. else
  148. tds.Disconnect ();
  149. this.state = ConnectionState.Closed;
  150. }
  151. public SqlCommand CreateCommand ()
  152. {
  153. SqlCommand command = new SqlCommand ();
  154. command.Connection = this;
  155. return command;
  156. }
  157. protected override void Dispose (bool disposing)
  158. {
  159. Close ();
  160. }
  161. [MonoTODO]
  162. public void EnlistDistributedTransaction (ITransaction transaction)
  163. {
  164. throw new NotImplementedException ();
  165. }
  166. [MonoTODO]
  167. object ICloneable.Clone ()
  168. {
  169. throw new NotImplementedException ();
  170. }
  171. IDbTransaction IDbConnection.BeginTransaction ()
  172. {
  173. return BeginTransaction ();
  174. }
  175. IDbTransaction IDbConnection.BeginTransaction (IsolationLevel iso)
  176. {
  177. return BeginTransaction (iso);
  178. }
  179. IDbCommand IDbConnection.CreateCommand ()
  180. {
  181. return CreateCommand ();
  182. }
  183. void IDisposable.Dispose ()
  184. {
  185. Dispose ();
  186. }
  187. public void Open ()
  188. {
  189. if (connectionString == null)
  190. throw new InvalidOperationException ("Connection string has not been initialized.");
  191. if (!pooling)
  192. tds = new Tds70 (dataSource, port, packetSize);
  193. else {
  194. pool = (SqlConnectionPool) SqlConnectionPools [connectionString];
  195. if (pool == null) {
  196. pool = new SqlConnectionPool (dataSource, port, packetSize, minPoolSize, maxPoolSize);
  197. SqlConnectionPools [connectionString] = pool;
  198. }
  199. tds = pool.AllocateConnection ();
  200. }
  201. state = ConnectionState.Open;
  202. if (!tds.IsConnected) {
  203. tds.Connect (parms);
  204. CheckForErrors ();
  205. ChangeDatabase (parms.Database);
  206. }
  207. else if (connectionReset) {
  208. tds.ExecuteNonQuery ("EXEC sp_connection_reset");
  209. CheckForErrors ();
  210. }
  211. }
  212. void SetConnectionString (string connectionString)
  213. {
  214. connectionString += ";";
  215. NameValueCollection parameters = new NameValueCollection ();
  216. if (connectionString == String.Empty)
  217. return;
  218. bool inQuote = false;
  219. bool inDQuote = false;
  220. string name = String.Empty;
  221. string value = String.Empty;
  222. StringBuilder sb = new StringBuilder ();
  223. foreach (char c in connectionString)
  224. {
  225. switch (c) {
  226. case '\'':
  227. inQuote = !inQuote;
  228. break;
  229. case '"' :
  230. inDQuote = !inDQuote;
  231. break;
  232. case ';' :
  233. if (!inDQuote && !inQuote) {
  234. if (name != String.Empty && name != null) {
  235. value = sb.ToString ();
  236. parameters [name.ToUpper ().Trim ()] = value.Trim ();
  237. }
  238. name = String.Empty;
  239. value = String.Empty;
  240. sb = new StringBuilder ();
  241. }
  242. else
  243. sb.Append (c);
  244. break;
  245. case '=' :
  246. if (!inDQuote && !inQuote) {
  247. name = sb.ToString ();
  248. sb = new StringBuilder ();
  249. }
  250. else
  251. sb.Append (c);
  252. break;
  253. default:
  254. sb.Append (c);
  255. break;
  256. }
  257. }
  258. if (this.ConnectionString == null)
  259. {
  260. SetDefaultConnectionParameters (parameters);
  261. }
  262. SetProperties (parameters);
  263. this.connectionString = connectionString;
  264. }
  265. void SetDefaultConnectionParameters (NameValueCollection parameters)
  266. {
  267. if (null == parameters.Get ("APPLICATION NAME"))
  268. parameters["APPLICATION NAME"] = ".Net SqlClient Data Provider";
  269. if (null == parameters.Get ("CONNECT TIMEOUT") && null == parameters.Get ("CONNECTION TIMEOUT"))
  270. parameters["CONNECT TIMEOUT"] = "15";
  271. if (null == parameters.Get ("CONNECTION LIFETIME"))
  272. parameters["CONNECTION LIFETIME"] = "0";
  273. if (null == parameters.Get ("CONNECTION RESET"))
  274. parameters["CONNECTION RESET"] = "true";
  275. if (null == parameters.Get ("ENLIST"))
  276. parameters["ENLIST"] = "true";
  277. if (null == parameters.Get ("INTEGRATED SECURITY") && null == parameters.Get ("TRUSTED_CONNECTION"))
  278. parameters["INTEGRATED SECURITY"] = "false";
  279. if (null == parameters.Get ("MAX POOL SIZE"))
  280. parameters["MAX POOL SIZE"] = "100";
  281. if (null == parameters.Get ("MIN POOL SIZE"))
  282. parameters["MIN POOL SIZE"] = "0";
  283. if (null == parameters.Get ("NETWORK LIBRARY") && null == parameters.Get ("NET"))
  284. parameters["NETWORK LIBRARY"] = "dbmssocn";
  285. if (null == parameters.Get ("PACKET SIZE"))
  286. parameters["PACKET SIZE"] = "512";
  287. if (null == parameters.Get ("PERSIST SECURITY INFO"))
  288. parameters["PERSIST SECURITY INFO"] = "false";
  289. if (null == parameters.Get ("POOLING"))
  290. parameters["POOLING"] = "true";
  291. if (null == parameters.Get ("WORKSTATION ID"))
  292. parameters["WORKSTATION ID"] = Dns.GetHostByName ("localhost").HostName;
  293. }
  294. private void SetProperties (NameValueCollection parameters)
  295. {
  296. string value;
  297. foreach (string name in parameters) {
  298. value = parameters[name];
  299. switch (name) {
  300. case "APPLICATION NAME" :
  301. parms.ApplicationName = value;
  302. break;
  303. case "ATTACHDBFILENAME" :
  304. case "EXTENDED PROPERTIES" :
  305. case "INITIAL FILE NAME" :
  306. break;
  307. case "CONNECT TIMEOUT" :
  308. case "CONNECTION TIMEOUT" :
  309. connectionTimeout = Int32.Parse (value);
  310. break;
  311. case "CONNECTION LIFETIME" :
  312. break;
  313. case "CONNECTION RESET" :
  314. connectionReset = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
  315. break;
  316. case "CURRENT LANGUAGE" :
  317. parms.Language = value;
  318. break;
  319. case "DATA SOURCE" :
  320. case "SERVER" :
  321. case "ADDRESS" :
  322. case "ADDR" :
  323. case "NETWORK ADDRESS" :
  324. dataSource = value;
  325. break;
  326. case "ENLIST" :
  327. break;
  328. case "INITIAL CATALOG" :
  329. case "DATABASE" :
  330. parms.Database = value;
  331. break;
  332. case "INTEGRATED SECURITY" :
  333. case "TRUSTED_CONNECTION" :
  334. break;
  335. case "MAX POOL SIZE" :
  336. maxPoolSize = Int32.Parse (value);
  337. break;
  338. case "MIN POOL SIZE" :
  339. minPoolSize = Int32.Parse (value);
  340. break;
  341. case "NET" :
  342. case "NETWORK LIBRARY" :
  343. if (!value.ToUpper ().Equals ("DBMSSOCN"))
  344. throw new ArgumentException ("Unsupported network library.");
  345. break;
  346. case "PACKET SIZE" :
  347. packetSize = Int32.Parse (value);
  348. break;
  349. case "PASSWORD" :
  350. case "PWD" :
  351. parms.Password = value;
  352. break;
  353. case "PERSIST SECURITY INFO" :
  354. break;
  355. case "POOLING" :
  356. pooling = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
  357. break;
  358. case "USER ID" :
  359. parms.User = value;
  360. break;
  361. case "WORKSTATION ID" :
  362. parms.Hostname = value;
  363. break;
  364. }
  365. }
  366. }
  367. static bool IsValidDatabaseName (string database)
  368. {
  369. if (database.Length > 32 || database.Length < 1)
  370. return false;
  371. if (database[0] == '"' && database[database.Length] == '"')
  372. database = database.Substring (1, database.Length - 2);
  373. else if (Char.IsDigit (database[0]))
  374. return false;
  375. if (database[0] == '_')
  376. return false;
  377. foreach (char c in database.Substring (1, database.Length - 1))
  378. if (!Char.IsLetterOrDigit (c) && c != '_')
  379. return false;
  380. return true;
  381. }
  382. #endregion // Methods
  383. #region Events
  384. public event SqlInfoMessageEventHandler InfoMessage;
  385. public event StateChangeEventHandler StateChange;
  386. #endregion // Events
  387. }
  388. }