SqlConnection.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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. if (tds.Errors.Count > 0)
  118. throw SqlException.FromTdsError (tds.Errors);
  119. transaction = new SqlTransaction (this, iso);
  120. return transaction;
  121. }
  122. public void ChangeDatabase (string database)
  123. {
  124. if (!IsValidDatabaseName (database))
  125. throw new ArgumentException (String.Format ("The database name {0} is not valid."));
  126. if (state != ConnectionState.Open)
  127. throw new InvalidOperationException ("The connection is not open");
  128. tds.ExecuteNonQuery (String.Format ("use {0}", database));
  129. if (tds.Errors.Count > 0)
  130. throw SqlException.FromTdsError (tds.Errors);
  131. }
  132. public void Close ()
  133. {
  134. if (transaction != null && transaction.IsOpen)
  135. transaction.Rollback ();
  136. if (pooling)
  137. pool.ReleaseConnection (tds);
  138. else
  139. tds.Disconnect ();
  140. this.state = ConnectionState.Closed;
  141. }
  142. public SqlCommand CreateCommand ()
  143. {
  144. SqlCommand command = new SqlCommand ();
  145. command.Connection = this;
  146. return command;
  147. }
  148. protected override void Dispose (bool disposing)
  149. {
  150. Close ();
  151. }
  152. [MonoTODO]
  153. public void EnlistDistributedTransaction (ITransaction transaction)
  154. {
  155. throw new NotImplementedException ();
  156. }
  157. [MonoTODO]
  158. object ICloneable.Clone ()
  159. {
  160. throw new NotImplementedException ();
  161. }
  162. IDbTransaction IDbConnection.BeginTransaction ()
  163. {
  164. return BeginTransaction ();
  165. }
  166. IDbTransaction IDbConnection.BeginTransaction (IsolationLevel iso)
  167. {
  168. return BeginTransaction (iso);
  169. }
  170. IDbCommand IDbConnection.CreateCommand ()
  171. {
  172. return CreateCommand ();
  173. }
  174. void IDisposable.Dispose ()
  175. {
  176. Dispose ();
  177. }
  178. public void Open ()
  179. {
  180. if (connectionString == null)
  181. throw new InvalidOperationException ("Connection string has not been initialized.");
  182. if (!pooling)
  183. tds = new Tds70 (dataSource, port, packetSize);
  184. else {
  185. pool = (SqlConnectionPool) SqlConnectionPools [connectionString];
  186. if (pool == null) {
  187. pool = new SqlConnectionPool (dataSource, port, packetSize, minPoolSize, maxPoolSize);
  188. SqlConnectionPools [connectionString] = pool;
  189. }
  190. tds = pool.AllocateConnection ();
  191. }
  192. state = ConnectionState.Open;
  193. if (!tds.IsConnected) {
  194. tds.Connect (parms);
  195. ChangeDatabase (parms.Database);
  196. }
  197. }
  198. void SetConnectionString (string connectionString)
  199. {
  200. connectionString += ";";
  201. NameValueCollection parameters = new NameValueCollection ();
  202. if (connectionString == String.Empty)
  203. return;
  204. bool inQuote = false;
  205. bool inDQuote = false;
  206. string name = String.Empty;
  207. string value = String.Empty;
  208. StringBuilder sb = new StringBuilder ();
  209. foreach (char c in connectionString)
  210. {
  211. switch (c) {
  212. case '\'':
  213. inQuote = !inQuote;
  214. break;
  215. case '"' :
  216. inDQuote = !inDQuote;
  217. break;
  218. case ';' :
  219. if (!inDQuote && !inQuote) {
  220. if (name != String.Empty && name != null) {
  221. value = sb.ToString ();
  222. parameters [name.ToUpper ().Trim ()] = value.Trim ();
  223. }
  224. name = String.Empty;
  225. value = String.Empty;
  226. sb = new StringBuilder ();
  227. }
  228. else
  229. sb.Append (c);
  230. break;
  231. case '=' :
  232. if (!inDQuote && !inQuote) {
  233. name = sb.ToString ();
  234. sb = new StringBuilder ();
  235. }
  236. else
  237. sb.Append (c);
  238. break;
  239. default:
  240. sb.Append (c);
  241. break;
  242. }
  243. }
  244. if (this.ConnectionString == null)
  245. {
  246. SetDefaultConnectionParameters (parameters);
  247. }
  248. SetProperties (parameters);
  249. this.connectionString = connectionString;
  250. }
  251. void SetDefaultConnectionParameters (NameValueCollection parameters)
  252. {
  253. if (null == parameters.Get ("APPLICATION NAME"))
  254. parameters["APPLICATION NAME"] = ".Net SqlClient Data Provider";
  255. if (null == parameters.Get ("CONNECT TIMEOUT") && null == parameters.Get ("CONNECTION TIMEOUT"))
  256. parameters["CONNECT TIMEOUT"] = "15";
  257. if (null == parameters.Get ("CONNECTION LIFETIME"))
  258. parameters["CONNECTION LIFETIME"] = "0";
  259. if (null == parameters.Get ("CONNECTION RESET"))
  260. parameters["CONNECTION RESET"] = "true";
  261. if (null == parameters.Get ("ENLIST"))
  262. parameters["ENLIST"] = "true";
  263. if (null == parameters.Get ("INTEGRATED SECURITY") && null == parameters.Get ("TRUSTED_CONNECTION"))
  264. parameters["INTEGRATED SECURITY"] = "false";
  265. if (null == parameters.Get ("MAX POOL SIZE"))
  266. parameters["MAX POOL SIZE"] = "100";
  267. if (null == parameters.Get ("MIN POOL SIZE"))
  268. parameters["MIN POOL SIZE"] = "0";
  269. if (null == parameters.Get ("NETWORK LIBRARY") && null == parameters.Get ("NET"))
  270. parameters["NETWORK LIBRARY"] = "dbmssocn";
  271. if (null == parameters.Get ("PACKET SIZE"))
  272. parameters["PACKET SIZE"] = "512";
  273. if (null == parameters.Get ("PERSIST SECURITY INFO"))
  274. parameters["PERSIST SECURITY INFO"] = "false";
  275. if (null == parameters.Get ("POOLING"))
  276. parameters["POOLING"] = "true";
  277. if (null == parameters.Get ("WORKSTATION ID"))
  278. parameters["WORKSTATION ID"] = Dns.GetHostByName ("localhost").HostName;
  279. }
  280. private void SetProperties (NameValueCollection parameters)
  281. {
  282. string value;
  283. foreach (string name in parameters) {
  284. value = parameters[name];
  285. switch (name) {
  286. case "APPLICATION NAME" :
  287. parms.ApplicationName = value;
  288. break;
  289. case "ATTACHDBFILENAME" :
  290. case "EXTENDED PROPERTIES" :
  291. case "INITIAL FILE NAME" :
  292. break;
  293. case "CONNECT TIMEOUT" :
  294. case "CONNECTION TIMEOUT" :
  295. connectionTimeout = Int32.Parse (value);
  296. break;
  297. case "CONNECTION LIFETIME" :
  298. break;
  299. case "CONNECTION RESET" :
  300. connectionReset = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
  301. break;
  302. case "CURRENT LANGUAGE" :
  303. parms.Language = value;
  304. break;
  305. case "DATA SOURCE" :
  306. case "SERVER" :
  307. case "ADDRESS" :
  308. case "ADDR" :
  309. case "NETWORK ADDRESS" :
  310. dataSource = value;
  311. break;
  312. case "ENLIST" :
  313. break;
  314. case "INITIAL CATALOG" :
  315. case "DATABASE" :
  316. parms.Database = value;
  317. break;
  318. case "INTEGRATED SECURITY" :
  319. case "TRUSTED_CONNECTION" :
  320. break;
  321. case "MAX POOL SIZE" :
  322. maxPoolSize = Int32.Parse (value);
  323. break;
  324. case "MIN POOL SIZE" :
  325. minPoolSize = Int32.Parse (value);
  326. break;
  327. case "NET" :
  328. case "NETWORK LIBRARY" :
  329. if (!value.ToUpper ().Equals ("DBMSSOCN"))
  330. throw new ArgumentException ("Unsupported network library.");
  331. break;
  332. case "PACKET SIZE" :
  333. packetSize = Int32.Parse (value);
  334. break;
  335. case "PASSWORD" :
  336. case "PWD" :
  337. parms.Password = value;
  338. break;
  339. case "PERSIST SECURITY INFO" :
  340. break;
  341. case "POOLING" :
  342. pooling = !(value.ToUpper ().Equals ("FALSE") || value.ToUpper ().Equals ("NO"));
  343. break;
  344. case "USER ID" :
  345. parms.User = value;
  346. break;
  347. case "WORKSTATION ID" :
  348. parms.Hostname = value;
  349. break;
  350. }
  351. }
  352. }
  353. static bool IsValidDatabaseName (string database)
  354. {
  355. if (database.Length > 32 || database.Length < 1)
  356. return false;
  357. if (database[0] == '"' && database[database.Length] == '"')
  358. database = database.Substring (1, database.Length - 2);
  359. else if (Char.IsDigit (database[0]))
  360. return false;
  361. if (database[0] == '_')
  362. return false;
  363. foreach (char c in database.Substring (1, database.Length - 1))
  364. if (!Char.IsLetterOrDigit (c) && c != '_')
  365. return false;
  366. return true;
  367. }
  368. #endregion // Methods
  369. #region Events
  370. public event SqlInfoMessageEventHandler InfoMessage;
  371. public event StateChangeEventHandler StateChange;
  372. #endregion // Events
  373. }
  374. }