SqlConnection.cs 15 KB

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