PageRenderTime 55ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/referencesource/System.Data.Linq/SqlClient/SqlProvider.cs

http://github.com/mono/mono
C# | 2024 lines | 1692 code | 204 blank | 128 comment | 393 complexity | 925ac6166544d3fb2119787e9cbcdd2f MD5 | raw file
Possible License(s): GPL-2.0, CC-BY-SA-3.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, Unlicense, Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.ComponentModel;
  6. using System.Data;
  7. using System.Data.Common;
  8. using System.Data.Linq;
  9. using System.Data.Linq.Mapping;
  10. using System.Data.Linq.Provider;
  11. using System.Data.SqlClient;
  12. using System.Diagnostics;
  13. using System.IO;
  14. using System.Linq;
  15. using System.Linq.Expressions;
  16. using System.Reflection;
  17. using System.Text;
  18. using System.Globalization;
  19. using System.Diagnostics.CodeAnalysis;
  20. using Me = System.Data.Linq.SqlClient;
  21. using System.Runtime.Versioning;
  22. using System.Runtime.CompilerServices;
  23. namespace System.Data.Linq.SqlClient {
  24. public sealed class Sql2000Provider : SqlProvider {
  25. public Sql2000Provider()
  26. : base(ProviderMode.Sql2000) {
  27. }
  28. }
  29. public sealed class Sql2005Provider : SqlProvider {
  30. public Sql2005Provider()
  31. : base(ProviderMode.Sql2005) {
  32. }
  33. }
  34. public sealed class Sql2008Provider : SqlProvider {
  35. public Sql2008Provider()
  36. : base(ProviderMode.Sql2008) {
  37. }
  38. }
  39. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification="Unknown reason.")]
  40. public class SqlProvider : IReaderProvider, IConnectionUser {
  41. private IDataServices services;
  42. private SqlConnectionManager conManager;
  43. private TypeSystemProvider typeProvider;
  44. private SqlFactory sqlFactory;
  45. private Translator translator;
  46. private IObjectReaderCompiler readerCompiler;
  47. private bool disposed;
  48. private int commandTimeout;
  49. private TextWriter log;
  50. string dbName = string.Empty;
  51. // stats and flags
  52. private int queryCount;
  53. private bool checkQueries;
  54. private OptimizationFlags optimizationFlags = OptimizationFlags.All;
  55. private bool enableCacheLookup = true;
  56. private ProviderMode mode = ProviderMode.NotYetDecided;
  57. private bool deleted = false;
  58. #if PERFORMANCE_BUILD
  59. private bool collectPerfInfo;
  60. private bool collectPerfInfoInitialized = false;
  61. private bool collectQueryPerf;
  62. internal bool CollectPerfInfo {
  63. get {
  64. if (!collectPerfInfoInitialized) {
  65. string s = System.Environment.GetEnvironmentVariable("CollectDLinqPerfInfo");
  66. collectPerfInfo = (s != null) && (s == "On");
  67. collectPerfInfoInitialized = true;
  68. }
  69. return this.collectPerfInfo;
  70. }
  71. }
  72. internal bool CollectQueryPerf {
  73. get { return this.collectQueryPerf; }
  74. }
  75. #endif
  76. internal enum ProviderMode {
  77. NotYetDecided,
  78. Sql2000,
  79. Sql2005,
  80. Sql2008,
  81. SqlCE
  82. }
  83. const string SqlCeProviderInvariantName = "System.Data.SqlServerCe.3.5";
  84. const string SqlCeDataReaderTypeName = "System.Data.SqlServerCe.SqlCeDataReader";
  85. const string SqlCeConnectionTypeName = "System.Data.SqlServerCe.SqlCeConnection";
  86. const string SqlCeTransactionTypeName = "System.Data.SqlServerCe.SqlCeTransaction";
  87. internal ProviderMode Mode {
  88. get {
  89. this.CheckDispose();
  90. this.CheckInitialized();
  91. this.InitializeProviderMode();
  92. return this.mode;
  93. }
  94. }
  95. private void InitializeProviderMode() {
  96. if (this.mode == ProviderMode.NotYetDecided) {
  97. if (this.IsSqlCe) {
  98. this.mode = ProviderMode.SqlCE;
  99. } else if (this.IsServer2KOrEarlier) {
  100. this.mode = ProviderMode.Sql2000;
  101. }
  102. else if (this.IsServer2005) {
  103. this.mode = ProviderMode.Sql2005;
  104. } else {
  105. this.mode = ProviderMode.Sql2008;
  106. }
  107. }
  108. if (this.typeProvider == null) {
  109. switch (this.mode) {
  110. case ProviderMode.Sql2000:
  111. this.typeProvider = SqlTypeSystem.Create2000Provider();
  112. break;
  113. case ProviderMode.Sql2005:
  114. this.typeProvider = SqlTypeSystem.Create2005Provider();
  115. break;
  116. case ProviderMode.Sql2008:
  117. this.typeProvider = SqlTypeSystem.Create2008Provider();
  118. break;
  119. case ProviderMode.SqlCE:
  120. this.typeProvider = SqlTypeSystem.CreateCEProvider();
  121. break;
  122. default:
  123. System.Diagnostics.Debug.Assert(false);
  124. break;
  125. }
  126. }
  127. if (this.sqlFactory == null) {
  128. this.sqlFactory = new SqlFactory(this.typeProvider, this.services.Model);
  129. this.translator = new Translator(this.services, this.sqlFactory, this.typeProvider);
  130. }
  131. }
  132. /// <summary>
  133. /// Return true if the current connection is SQLCE.
  134. /// </summary>
  135. private bool IsSqlCe {
  136. get {
  137. DbConnection con = conManager.UseConnection(this);
  138. try {
  139. if (String.CompareOrdinal(con.GetType().FullName, SqlCeConnectionTypeName) == 0) {
  140. return true;
  141. }
  142. } finally {
  143. conManager.ReleaseConnection(this);
  144. }
  145. return false;
  146. }
  147. }
  148. /// <summary>
  149. /// Return true if this is a 2K (or earlier) server. This may be a round trip to the server.
  150. /// </summary>
  151. private bool IsServer2KOrEarlier {
  152. get {
  153. DbConnection con = conManager.UseConnection(this);
  154. try {
  155. string serverVersion = con.ServerVersion;
  156. if (serverVersion.StartsWith("06.00.", StringComparison.Ordinal)) {
  157. return true;
  158. }
  159. else if (serverVersion.StartsWith("06.50.", StringComparison.Ordinal)) {
  160. return true;
  161. }
  162. else if (serverVersion.StartsWith("07.00.", StringComparison.Ordinal)) {
  163. return true;
  164. }
  165. else if (serverVersion.StartsWith("08.00.", StringComparison.Ordinal)) {
  166. return true;
  167. }
  168. return false;
  169. }
  170. finally {
  171. conManager.ReleaseConnection(this);
  172. }
  173. }
  174. }
  175. /// <summary>
  176. /// Return true if this is a SQL 2005 server. This may be a round trip to the server.
  177. /// </summary>
  178. private bool IsServer2005 {
  179. get {
  180. DbConnection con = conManager.UseConnection(this);
  181. try {
  182. string serverVersion = con.ServerVersion;
  183. if (serverVersion.StartsWith("09.00.", StringComparison.Ordinal)) {
  184. return true;
  185. }
  186. return false;
  187. }
  188. finally {
  189. conManager.ReleaseConnection(this);
  190. }
  191. }
  192. }
  193. DbConnection IProvider.Connection {
  194. get {
  195. this.CheckDispose();
  196. this.CheckInitialized();
  197. return this.conManager.Connection;
  198. }
  199. }
  200. TextWriter IProvider.Log {
  201. get {
  202. this.CheckDispose();
  203. this.CheckInitialized();
  204. return this.log;
  205. }
  206. set {
  207. this.CheckDispose();
  208. this.CheckInitialized();
  209. this.log = value;
  210. }
  211. }
  212. DbTransaction IProvider.Transaction {
  213. get {
  214. this.CheckDispose();
  215. this.CheckInitialized();
  216. return this.conManager.Transaction;
  217. }
  218. set {
  219. this.CheckDispose();
  220. this.CheckInitialized();
  221. this.conManager.Transaction = value;
  222. }
  223. }
  224. int IProvider.CommandTimeout {
  225. get {
  226. this.CheckDispose();
  227. return this.commandTimeout;
  228. }
  229. set {
  230. this.CheckDispose();
  231. this.commandTimeout = value;
  232. }
  233. }
  234. /// <summary>
  235. /// Expose a test hook which controls which SQL optimizations are executed.
  236. /// </summary>
  237. internal OptimizationFlags OptimizationFlags {
  238. get {
  239. CheckDispose();
  240. return this.optimizationFlags;
  241. }
  242. set {
  243. CheckDispose();
  244. this.optimizationFlags = value;
  245. }
  246. }
  247. /// <summary>
  248. /// Validate queries as they are generated.
  249. /// </summary>
  250. internal bool CheckQueries {
  251. get {
  252. CheckDispose();
  253. return checkQueries;
  254. }
  255. set {
  256. CheckDispose();
  257. checkQueries = value;
  258. }
  259. }
  260. internal bool EnableCacheLookup {
  261. get {
  262. CheckDispose();
  263. return this.enableCacheLookup;
  264. }
  265. set {
  266. CheckDispose();
  267. this.enableCacheLookup = value;
  268. }
  269. }
  270. internal int QueryCount {
  271. get {
  272. CheckDispose();
  273. return this.queryCount;
  274. }
  275. }
  276. internal int MaxUsers {
  277. get {
  278. CheckDispose();
  279. return this.conManager.MaxUsers;
  280. }
  281. }
  282. IDataServices IReaderProvider.Services {
  283. get { return this.services; }
  284. }
  285. IConnectionManager IReaderProvider.ConnectionManager {
  286. get { return this.conManager; }
  287. }
  288. public SqlProvider() {
  289. this.mode = ProviderMode.NotYetDecided;
  290. }
  291. internal SqlProvider(ProviderMode mode) {
  292. this.mode = mode;
  293. }
  294. private void CheckInitialized() {
  295. if (this.services == null) {
  296. throw Error.ContextNotInitialized();
  297. }
  298. }
  299. private void CheckNotDeleted() {
  300. if (this.deleted) {
  301. throw Error.DatabaseDeleteThroughContext();
  302. }
  303. }
  304. [ResourceExposure(ResourceScope.Machine)] // connection parameter may refer to filenames.
  305. void IProvider.Initialize(IDataServices dataServices, object connection) {
  306. if (dataServices == null) {
  307. throw Error.ArgumentNull("dataServices");
  308. }
  309. this.services = dataServices;
  310. DbConnection con;
  311. DbTransaction tx = null;
  312. string fileOrServerOrConnectionString = connection as string;
  313. if (fileOrServerOrConnectionString != null) {
  314. string connectionString = this.GetConnectionString(fileOrServerOrConnectionString);
  315. this.dbName = this.GetDatabaseName(connectionString);
  316. if (this.dbName.EndsWith(".sdf", StringComparison.OrdinalIgnoreCase)) {
  317. this.mode = ProviderMode.SqlCE;
  318. }
  319. if (this.mode == ProviderMode.SqlCE) {
  320. DbProviderFactory factory = SqlProvider.GetProvider(SqlCeProviderInvariantName);
  321. if (factory == null) {
  322. throw Error.ProviderNotInstalled(this.dbName, SqlCeProviderInvariantName);
  323. }
  324. con = factory.CreateConnection();
  325. }
  326. else {
  327. con = new SqlConnection();
  328. }
  329. con.ConnectionString = connectionString;
  330. }
  331. else {
  332. // We only support SqlTransaction and SqlCeTransaction
  333. tx = connection as SqlTransaction;
  334. if (tx == null) {
  335. // See if it's a SqlCeTransaction
  336. if (connection.GetType().FullName == SqlCeTransactionTypeName) {
  337. tx = connection as DbTransaction;
  338. }
  339. }
  340. if (tx != null) {
  341. connection = tx.Connection;
  342. }
  343. con = connection as DbConnection;
  344. if (con == null) {
  345. throw Error.InvalidConnectionArgument("connection");
  346. }
  347. if (con.GetType().FullName == SqlCeConnectionTypeName) {
  348. this.mode = ProviderMode.SqlCE;
  349. }
  350. this.dbName = this.GetDatabaseName(con.ConnectionString);
  351. }
  352. // initialize to the default command timeout
  353. using (DbCommand c = con.CreateCommand()) {
  354. this.commandTimeout = c.CommandTimeout;
  355. }
  356. int maxUsersPerConnection = 1;
  357. if (con.ConnectionString.IndexOf("MultipleActiveResultSets", StringComparison.OrdinalIgnoreCase) >= 0) {
  358. DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
  359. builder.ConnectionString = con.ConnectionString;
  360. if (string.Compare((string)builder["MultipleActiveResultSets"], "true", StringComparison.OrdinalIgnoreCase) == 0) {
  361. maxUsersPerConnection = 10;
  362. }
  363. }
  364. // If fileOrServerOrConnectionString != null, that means we just created the connection instance and we have to tell
  365. // the SqlConnectionManager that it should dispose the connection when the context is disposed. Otherwise the user owns
  366. // the connection and should dispose of it themselves.
  367. this.conManager = new SqlConnectionManager(this, con, maxUsersPerConnection, fileOrServerOrConnectionString != null /*disposeConnection*/);
  368. if (tx != null) {
  369. this.conManager.Transaction = tx;
  370. }
  371. #if DEBUG
  372. SqlNode.Formatter = new SqlFormatter();
  373. #endif
  374. #if ILGEN
  375. Type readerType;
  376. if (this.mode == ProviderMode.SqlCE) {
  377. readerType = con.GetType().Module.GetType(SqlCeDataReaderTypeName);
  378. }
  379. else if (con is SqlConnection) {
  380. readerType = typeof(SqlDataReader);
  381. }
  382. else {
  383. readerType = typeof(DbDataReader);
  384. }
  385. this.readerCompiler = new ObjectReaderCompiler(readerType, this.services);
  386. #else
  387. this.readerCompiler = new ObjectReaderBuilder(this, this.services);
  388. #endif
  389. }
  390. private static DbProviderFactory GetProvider(string providerName) {
  391. bool hasProvider =
  392. DbProviderFactories.GetFactoryClasses().Rows.OfType<DataRow>()
  393. .Select(r => (string)r["InvariantName"])
  394. .Contains(providerName, StringComparer.OrdinalIgnoreCase);
  395. if (hasProvider) {
  396. return DbProviderFactories.GetFactory(providerName);
  397. }
  398. return null;
  399. }
  400. #region Dispose\Finalize
  401. public void Dispose() {
  402. this.disposed = true;
  403. Dispose(true);
  404. // Technically, calling GC.SuppressFinalize is not required because the class does not
  405. // have a finalizer, but it does no harm, protects against the case where a finalizer is added
  406. // in the future, and prevents an FxCop warning.
  407. GC.SuppressFinalize(this);
  408. }
  409. // Not implementing finalizer here because there are no unmanaged resources
  410. // to release. See http://msdnwiki.microsoft.com/en-us/mtpswiki/12afb1ea-3a17-4a3f-a1f0-fcdb853e2359.aspx
  411. // The bulk of the clean-up code is implemented in Dispose(bool)
  412. protected virtual void Dispose(bool disposing) {
  413. // Implemented but empty so that derived contexts can implement
  414. // a finalizer that potentially cleans up unmanaged resources.
  415. if (disposing) {
  416. this.services = null;
  417. if (this.conManager != null) {
  418. this.conManager.DisposeConnection();
  419. }
  420. this.conManager = null;
  421. this.typeProvider = null;
  422. this.sqlFactory = null;
  423. this.translator = null;
  424. this.readerCompiler = null;
  425. this.log = null;
  426. }
  427. }
  428. internal void CheckDispose() {
  429. if (this.disposed) {
  430. throw Error.ProviderCannotBeUsedAfterDispose();
  431. }
  432. }
  433. #endregion
  434. private string GetConnectionString(string fileOrServerOrConnectionString) {
  435. if (fileOrServerOrConnectionString.IndexOf('=') >= 0) {
  436. return fileOrServerOrConnectionString;
  437. }
  438. else {
  439. DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
  440. if (fileOrServerOrConnectionString.EndsWith(".mdf", StringComparison.OrdinalIgnoreCase)) {
  441. // if just a database file is specified, default to local SqlExpress instance
  442. builder.Add("AttachDBFileName", fileOrServerOrConnectionString);
  443. builder.Add("Server", "localhost\\sqlexpress");
  444. builder.Add("Integrated Security", "SSPI");
  445. builder.Add("User Instance", "true");
  446. builder.Add("MultipleActiveResultSets", "true");
  447. }
  448. else if (fileOrServerOrConnectionString.EndsWith(".sdf", StringComparison.OrdinalIgnoreCase)) {
  449. // A SqlCE database file has been specified
  450. builder.Add("Data Source", fileOrServerOrConnectionString);
  451. }
  452. else {
  453. builder.Add("Server", fileOrServerOrConnectionString);
  454. builder.Add("Database", this.services.Model.DatabaseName);
  455. builder.Add("Integrated Security", "SSPI");
  456. }
  457. return builder.ToString();
  458. }
  459. }
  460. private string GetDatabaseName(string constr) {
  461. DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
  462. builder.ConnectionString = constr;
  463. if (builder.ContainsKey("Initial Catalog")) {
  464. return (string)builder["Initial Catalog"];
  465. }
  466. else if (builder.ContainsKey("Database")) {
  467. return (string)builder["Database"];
  468. }
  469. else if (builder.ContainsKey("AttachDBFileName")) {
  470. return (string)builder["AttachDBFileName"];
  471. }
  472. else if (builder.ContainsKey("Data Source")
  473. && ((string)builder["Data Source"]).EndsWith(".sdf", StringComparison.OrdinalIgnoreCase)) {
  474. return (string)builder["Data Source"];
  475. }
  476. else {
  477. return this.services.Model.DatabaseName;
  478. }
  479. }
  480. [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "These issues are related to our use of if-then and case statements for node types, which adds to the complexity count however when reviewed they are easy to navigate and understand.")]
  481. [ResourceExposure(ResourceScope.None)] // Exposure is via other methods that set dbName.
  482. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // File.Exists method call.
  483. void IProvider.CreateDatabase() {
  484. this.CheckDispose();
  485. this.CheckInitialized();
  486. // Don't need to call CheckNotDeleted() here since we allow CreateDatabase after DeleteDatabase
  487. // Don't need to call InitializeProviderMode() here since we don't need to know the provider to do this.
  488. string catalog = null;
  489. string filename = null;
  490. DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
  491. builder.ConnectionString = this.conManager.Connection.ConnectionString;
  492. if (this.conManager.Connection.State == ConnectionState.Closed) {
  493. if (this.mode == ProviderMode.SqlCE) {
  494. if (!File.Exists(this.dbName)) {
  495. Type engineType = this.conManager.Connection.GetType().Module.GetType("System.Data.SqlServerCe.SqlCeEngine");
  496. object engine = Activator.CreateInstance(engineType, new object[] { builder.ToString() });
  497. try {
  498. engineType.InvokeMember("CreateDatabase", BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, null, engine, new object[] { }, CultureInfo.InvariantCulture);
  499. }
  500. catch (TargetInvocationException tie) {
  501. throw tie.InnerException;
  502. }
  503. finally {
  504. IDisposable disp = engine as IDisposable;
  505. if (disp != null) {
  506. disp.Dispose();
  507. }
  508. }
  509. }
  510. else {
  511. throw Error.CreateDatabaseFailedBecauseSqlCEDatabaseAlreadyExists(this.dbName);
  512. }
  513. }
  514. else {
  515. // get connection string w/o reference to new catalog
  516. object val;
  517. if (builder.TryGetValue("Initial Catalog", out val)) {
  518. catalog = val.ToString();
  519. builder.Remove("Initial Catalog");
  520. }
  521. if (builder.TryGetValue("Database", out val)) {
  522. catalog = val.ToString();
  523. builder.Remove("Database");
  524. }
  525. if (builder.TryGetValue("AttachDBFileName", out val)) {
  526. filename = val.ToString();
  527. builder.Remove("AttachDBFileName");
  528. }
  529. }
  530. this.conManager.Connection.ConnectionString = builder.ToString();
  531. }
  532. else {
  533. if (this.mode == ProviderMode.SqlCE) {
  534. if (File.Exists(this.dbName)) {
  535. throw Error.CreateDatabaseFailedBecauseSqlCEDatabaseAlreadyExists(this.dbName);
  536. }
  537. }
  538. object val;
  539. if (builder.TryGetValue("Initial Catalog", out val)) {
  540. catalog = val.ToString();
  541. }
  542. if (builder.TryGetValue("Database", out val)) {
  543. catalog = val.ToString();
  544. }
  545. if (builder.TryGetValue("AttachDBFileName", out val)) {
  546. filename = val.ToString();
  547. }
  548. }
  549. if (String.IsNullOrEmpty(catalog)) {
  550. if (!String.IsNullOrEmpty(filename)) {
  551. catalog = Path.GetFullPath(filename);
  552. }
  553. else if (!String.IsNullOrEmpty(this.dbName)) {
  554. catalog = this.dbName;
  555. }
  556. else {
  557. throw Error.CouldNotDetermineCatalogName();
  558. }
  559. }
  560. this.conManager.UseConnection(this);
  561. this.conManager.AutoClose = false;
  562. try {
  563. if (this.services.Model.GetTables().FirstOrDefault() == null) {
  564. // we have no tables to create
  565. throw Error.CreateDatabaseFailedBecauseOfContextWithNoTables(this.services.Model.DatabaseName);
  566. }
  567. this.deleted = false;
  568. // create database
  569. if (this.mode == ProviderMode.SqlCE) {
  570. // create tables
  571. foreach (MetaTable table in this.services.Model.GetTables()) {
  572. string command = SqlBuilder.GetCreateTableCommand(table);
  573. if (!String.IsNullOrEmpty(command)) {
  574. this.ExecuteCommand(command);
  575. }
  576. }
  577. // create all foreign keys after all tables are defined
  578. foreach (MetaTable table in this.services.Model.GetTables()) {
  579. foreach (string command in SqlBuilder.GetCreateForeignKeyCommands(table)) {
  580. if (!String.IsNullOrEmpty(command)) {
  581. this.ExecuteCommand(command);
  582. }
  583. }
  584. }
  585. }
  586. else {
  587. string createdb = SqlBuilder.GetCreateDatabaseCommand(catalog, filename, Path.ChangeExtension(filename, ".ldf"));
  588. this.ExecuteCommand(createdb);
  589. this.conManager.Connection.ChangeDatabase(catalog);
  590. // create the schemas that our tables will need
  591. // cannot be batched together with the rest of the CREATE TABLES
  592. if (this.mode == ProviderMode.Sql2005 || this.mode == ProviderMode.Sql2008) {
  593. HashSet<string> schemaCommands = new HashSet<string>();
  594. foreach (MetaTable table in this.services.Model.GetTables()) {
  595. string schemaCommand = SqlBuilder.GetCreateSchemaForTableCommand(table);
  596. if (!string.IsNullOrEmpty(schemaCommand)) {
  597. schemaCommands.Add(schemaCommand);
  598. }
  599. }
  600. foreach (string schemaCommand in schemaCommands) {
  601. this.ExecuteCommand(schemaCommand);
  602. }
  603. }
  604. StringBuilder sb = new StringBuilder();
  605. // create tables
  606. foreach (MetaTable table in this.services.Model.GetTables()) {
  607. string createTable = SqlBuilder.GetCreateTableCommand(table);
  608. if (!string.IsNullOrEmpty(createTable)) {
  609. sb.AppendLine(createTable);
  610. }
  611. }
  612. // create all foreign keys after all tables are defined
  613. foreach (MetaTable table in this.services.Model.GetTables()) {
  614. foreach (string createFK in SqlBuilder.GetCreateForeignKeyCommands(table)) {
  615. if (!string.IsNullOrEmpty(createFK)) {
  616. sb.AppendLine(createFK);
  617. }
  618. }
  619. }
  620. if (sb.Length > 0) {
  621. // must be on when creating indexes on computed columns
  622. sb.Insert(0, "SET ARITHABORT ON" + Environment.NewLine);
  623. this.ExecuteCommand(sb.ToString());
  624. }
  625. }
  626. }
  627. finally {
  628. this.conManager.ReleaseConnection(this);
  629. if (this.conManager.Connection is SqlConnection) {
  630. SqlConnection.ClearAllPools();
  631. }
  632. }
  633. }
  634. [ResourceExposure(ResourceScope.None)] // Exposure is via other methods that set dbName.
  635. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // File.Delete method call.
  636. void IProvider.DeleteDatabase() {
  637. this.CheckDispose();
  638. this.CheckInitialized();
  639. // Don't need to call InitializeProviderMode() here since we don't need to know the provider to do this.
  640. if (this.deleted) {
  641. // 2nd delete is no-op.
  642. return;
  643. }
  644. if (this.mode == ProviderMode.SqlCE) {
  645. ((IProvider)this).ClearConnection();
  646. System.Diagnostics.Debug.Assert(this.conManager.Connection.State == ConnectionState.Closed);
  647. File.Delete(this.dbName);
  648. this.deleted = true;
  649. }
  650. else {
  651. string holdConnStr = conManager.Connection.ConnectionString;
  652. DbConnection con = this.conManager.UseConnection(this);
  653. try {
  654. con.ChangeDatabase("master");
  655. if (con is SqlConnection) {
  656. SqlConnection.ClearAllPools();
  657. }
  658. if (this.log != null) {
  659. this.log.WriteLine(Strings.LogAttemptingToDeleteDatabase(this.dbName));
  660. }
  661. this.ExecuteCommand(SqlBuilder.GetDropDatabaseCommand(this.dbName));
  662. this.deleted = true;
  663. }
  664. finally {
  665. this.conManager.ReleaseConnection(this);
  666. if (conManager.Connection.State == ConnectionState.Closed &&
  667. string.Compare(conManager.Connection.ConnectionString, holdConnStr, StringComparison.Ordinal) != 0) {
  668. // Credential information may have been stripped from the connection
  669. // string as a result of opening the connection. Restore the full
  670. // connection string.
  671. conManager.Connection.ConnectionString = holdConnStr;
  672. }
  673. }
  674. }
  675. }
  676. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification="Microsoft: Code needs to return false regarless of exception.")]
  677. [ResourceExposure(ResourceScope.None)] // Exposure is via other methods that set dbName.
  678. [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // File.Exists method call.
  679. bool IProvider.DatabaseExists() {
  680. this.CheckDispose();
  681. this.CheckInitialized();
  682. if (this.deleted) {
  683. return false;
  684. }
  685. // Don't need to call InitializeProviderMode() here since we don't need to know the provider to do this.
  686. bool exists = false;
  687. if (this.mode == ProviderMode.SqlCE) {
  688. exists = File.Exists(this.dbName);
  689. }
  690. else {
  691. string holdConnStr = conManager.Connection.ConnectionString;
  692. try {
  693. // If no database name is explicitly specified on the connection,
  694. // UseConnection will connect to 'Master', which is why after connecting
  695. // we call ChangeDatabase to verify that the database actually exists.
  696. this.conManager.UseConnection(this);
  697. this.conManager.Connection.ChangeDatabase(this.dbName);
  698. this.conManager.ReleaseConnection(this);
  699. exists = true;
  700. } catch (Exception) {
  701. } finally {
  702. if (conManager.Connection.State == ConnectionState.Closed &&
  703. string.Compare(conManager.Connection.ConnectionString, holdConnStr, StringComparison.Ordinal) != 0) {
  704. // Credential information may have been stripped from the connection
  705. // string as a result of opening the connection. Restore the full
  706. // connection string.
  707. conManager.Connection.ConnectionString = holdConnStr;
  708. }
  709. }
  710. }
  711. return exists;
  712. }
  713. void IConnectionUser.CompleteUse() {
  714. }
  715. void IProvider.ClearConnection() {
  716. this.CheckDispose();
  717. this.CheckInitialized();
  718. this.conManager.ClearConnection();
  719. }
  720. private void ExecuteCommand(string command) {
  721. if (this.log != null) {
  722. this.log.WriteLine(command);
  723. this.log.WriteLine();
  724. }
  725. IDbCommand cmd = this.conManager.Connection.CreateCommand();
  726. cmd.CommandTimeout = this.commandTimeout;
  727. cmd.Transaction = this.conManager.Transaction;
  728. cmd.CommandText = command;
  729. cmd.ExecuteNonQuery();
  730. }
  731. ICompiledQuery IProvider.Compile(Expression query) {
  732. this.CheckDispose();
  733. this.CheckInitialized();
  734. if (query == null) {
  735. throw Error.ArgumentNull("query");
  736. }
  737. this.InitializeProviderMode();
  738. SqlNodeAnnotations annotations = new SqlNodeAnnotations();
  739. QueryInfo[] qis = this.BuildQuery(query, annotations);
  740. CheckSqlCompatibility(qis, annotations);
  741. LambdaExpression lambda = query as LambdaExpression;
  742. if (lambda != null) {
  743. query = lambda.Body;
  744. }
  745. IObjectReaderFactory factory = null;
  746. ICompiledSubQuery[] subQueries = null;
  747. QueryInfo qi = qis[qis.Length - 1];
  748. if (qi.ResultShape == ResultShape.Singleton) {
  749. subQueries = this.CompileSubQueries(qi.Query);
  750. factory = this.GetReaderFactory(qi.Query, qi.ResultType);
  751. }
  752. else if (qi.ResultShape == ResultShape.Sequence) {
  753. subQueries = this.CompileSubQueries(qi.Query);
  754. factory = this.GetReaderFactory(qi.Query, TypeSystem.GetElementType(qi.ResultType));
  755. }
  756. return new CompiledQuery(this, query, qis, factory, subQueries);
  757. }
  758. private ICompiledSubQuery CompileSubQuery(SqlNode query, Type elementType, ReadOnlyCollection<Me.SqlParameter> parameters) {
  759. query = SqlDuplicator.Copy(query);
  760. SqlNodeAnnotations annotations = new SqlNodeAnnotations();
  761. QueryInfo[] qis = this.BuildQuery(ResultShape.Sequence, TypeSystem.GetSequenceType(elementType), query, parameters, annotations);
  762. System.Diagnostics.Debug.Assert(qis.Length == 1);
  763. QueryInfo qi = qis[0];
  764. ICompiledSubQuery[] subQueries = this.CompileSubQueries(qi.Query);
  765. IObjectReaderFactory factory = this.GetReaderFactory(qi.Query, elementType);
  766. CheckSqlCompatibility(qis, annotations);
  767. return new CompiledSubQuery(qi, factory, parameters, subQueries);
  768. }
  769. IExecuteResult IProvider.Execute(Expression query) {
  770. this.CheckDispose();
  771. this.CheckInitialized();
  772. this.CheckNotDeleted();
  773. if (query == null) {
  774. throw Error.ArgumentNull("query");
  775. }
  776. this.InitializeProviderMode();
  777. #if PERFORMANCE_BUILD
  778. PerformanceCounter pcBuildQuery = null, bpcBuildQuery = null, pcExecQuery = null, bpcExecQuery = null,
  779. pcSession = null, bpcSession = null;
  780. PerfTimer timerAll = null, timer = null;
  781. if (this.CollectPerfInfo) {
  782. string s = System.Environment.GetEnvironmentVariable("EnableDLinqQueryPerf");
  783. collectQueryPerf = (s != null && s == "On");
  784. }
  785. if (collectQueryPerf) {
  786. pcBuildQuery = new PerformanceCounter("DLinq", "BuildQueryElapsedTime", false);
  787. bpcBuildQuery = new PerformanceCounter("DLinq", "BuildQueryElapsedTimeBase", false);
  788. pcExecQuery = new PerformanceCounter("DLinq", "ExecuteQueryElapsedTime", false);
  789. bpcExecQuery = new PerformanceCounter("DLinq", "ExecuteQueryElapsedTimeBase", false);
  790. pcSession = new PerformanceCounter("DLinq", "SessionExecuteQueryElapsedTime", false);
  791. bpcSession = new PerformanceCounter("DLinq", "SessionExecuteQueryElapsedTimeBase", false);
  792. timerAll = new PerfTimer();
  793. timer = new PerfTimer();
  794. timerAll.Start();
  795. }
  796. #endif
  797. query = Funcletizer.Funcletize(query);
  798. if (this.EnableCacheLookup) {
  799. IExecuteResult cached = this.GetCachedResult(query);
  800. if (cached != null) {
  801. return cached;
  802. }
  803. }
  804. #if PERFORMANCE_BUILD
  805. if (collectQueryPerf) {
  806. timer.Start();
  807. }
  808. #endif
  809. SqlNodeAnnotations annotations = new SqlNodeAnnotations();
  810. QueryInfo[] qis = this.BuildQuery(query, annotations);
  811. CheckSqlCompatibility(qis, annotations);
  812. LambdaExpression lambda = query as LambdaExpression;
  813. if (lambda != null) {
  814. query = lambda.Body;
  815. }
  816. IObjectReaderFactory factory = null;
  817. ICompiledSubQuery[] subQueries = null;
  818. QueryInfo qi = qis[qis.Length - 1];
  819. if (qi.ResultShape == ResultShape.Singleton) {
  820. subQueries = this.CompileSubQueries(qi.Query);
  821. factory = this.GetReaderFactory(qi.Query, qi.ResultType);
  822. }
  823. else if (qi.ResultShape == ResultShape.Sequence) {
  824. subQueries = this.CompileSubQueries(qi.Query);
  825. factory = this.GetReaderFactory(qi.Query, TypeSystem.GetElementType(qi.ResultType));
  826. }
  827. #if PERFORMANCE_BUILD
  828. if (collectQueryPerf) {
  829. timer.Stop();
  830. pcBuildQuery.IncrementBy(timer.Duration);
  831. bpcBuildQuery.Increment();
  832. }
  833. #endif
  834. #if PERFORMANCE_BUILD
  835. if (collectQueryPerf) {
  836. timer.Start();
  837. }
  838. #endif
  839. IExecuteResult result = this.ExecuteAll(query, qis, factory, null, subQueries);
  840. #if PERFORMANCE_BUILD
  841. if (collectQueryPerf) {
  842. timer.Stop();
  843. pcSession.IncrementBy(timer.Duration);
  844. bpcSession.Increment();
  845. timerAll.Stop();
  846. pcExecQuery.IncrementBy(timerAll.Duration);
  847. bpcExecQuery.Increment();
  848. }
  849. #endif
  850. return result;
  851. }
  852. private ICompiledSubQuery[] CompileSubQueries(SqlNode query) {
  853. return new SubQueryCompiler(this).Compile(query);
  854. }
  855. class SubQueryCompiler : SqlVisitor {
  856. SqlProvider provider;
  857. List<ICompiledSubQuery> subQueries;
  858. internal SubQueryCompiler(SqlProvider provider) {
  859. this.provider = provider;
  860. }
  861. internal ICompiledSubQuery[] Compile(SqlNode node) {
  862. this.subQueries = new List<ICompiledSubQuery>();
  863. this.Visit(node);
  864. return this.subQueries.ToArray();
  865. }
  866. internal override SqlSelect VisitSelect(SqlSelect select) {
  867. this.Visit(select.Selection);
  868. return select;
  869. }
  870. internal override SqlExpression VisitSubSelect(SqlSubSelect ss) {
  871. return ss;
  872. }
  873. internal override SqlExpression VisitClientQuery(SqlClientQuery cq) {
  874. Type clientElementType = cq.Query.NodeType == SqlNodeType.Multiset ? TypeSystem.GetElementType(cq.ClrType) : cq.ClrType;
  875. ICompiledSubQuery c = this.provider.CompileSubQuery(cq.Query.Select, clientElementType, cq.Parameters.AsReadOnly());
  876. cq.Ordinal = this.subQueries.Count;
  877. this.subQueries.Add(c);
  878. return cq;
  879. }
  880. }
  881. /// <summary>
  882. /// Look for compatibility annotations for the set of providers we
  883. /// add annotations for.
  884. /// </summary>
  885. private void CheckSqlCompatibility(QueryInfo[] queries, SqlNodeAnnotations annotations) {
  886. if (this.Mode == ProviderMode.Sql2000 ||
  887. this.Mode == ProviderMode.SqlCE) {
  888. for (int i = 0, n = queries.Length; i < n; i++) {
  889. SqlServerCompatibilityCheck.ThrowIfUnsupported(queries[i].Query, annotations, this.Mode);
  890. }
  891. }
  892. }
  893. private IExecuteResult ExecuteAll(Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, object[] userArguments, ICompiledSubQuery[] subQueries) {
  894. IExecuteResult result = null;
  895. object lastResult = null;
  896. for (int i = 0, n = queryInfos.Length; i < n; i++) {
  897. if (i < n - 1) {
  898. result = this.Execute(query, queryInfos[i], null, null, userArguments, subQueries, lastResult);
  899. }
  900. else {
  901. result = this.Execute(query, queryInfos[i], factory, null, userArguments, subQueries, lastResult);
  902. }
  903. if (queryInfos[i].ResultShape == ResultShape.Return) {
  904. lastResult = result.ReturnValue;
  905. }
  906. }
  907. return result;
  908. }
  909. [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
  910. private IExecuteResult GetCachedResult(Expression query) {
  911. object obj = this.services.GetCachedObject(query);
  912. if (obj != null) {
  913. switch (this.GetResultShape(query)) {
  914. case ResultShape.Singleton:
  915. return new ExecuteResult(null, null, null, obj);
  916. case ResultShape.Sequence:
  917. return new ExecuteResult(null, null, null,
  918. Activator.CreateInstance(
  919. typeof(SequenceOfOne<>).MakeGenericType(TypeSystem.GetElementType(this.GetResultType(query))),
  920. BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { obj }, null
  921. ));
  922. }
  923. }
  924. return null;
  925. }
  926. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification="Unknown reason.")]
  927. [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
  928. private IExecuteResult Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, object[] parentArgs, object[] userArgs, ICompiledSubQuery[] subQueries, object lastResult) {
  929. this.InitializeProviderMode();
  930. DbConnection con = this.conManager.UseConnection(this);
  931. try {
  932. DbCommand cmd = con.CreateCommand();
  933. cmd.CommandText = queryInfo.CommandText;
  934. cmd.Transaction = this.conManager.Transaction;
  935. cmd.CommandTimeout = this.commandTimeout;
  936. AssignParameters(cmd, queryInfo.Parameters, userArgs, lastResult);
  937. LogCommand(this.log, cmd);
  938. this.queryCount += 1;
  939. switch (queryInfo.ResultShape) {
  940. default:
  941. case ResultShape.Return: {
  942. return new ExecuteResult(cmd, queryInfo.Parameters, null, cmd.ExecuteNonQuery(), true);
  943. }
  944. case ResultShape.Singleton: {
  945. DbDataReader reader = cmd.ExecuteReader();
  946. IObjectReader objReader = factory.Create(reader, true, this, parentArgs, userArgs, subQueries);
  947. this.conManager.UseConnection(objReader.Session);
  948. try {
  949. IEnumerable sequence = (IEnumerable)Activator.CreateInstance(
  950. typeof(OneTimeEnumerable<>).MakeGenericType(queryInfo.ResultType),
  951. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
  952. new object[] { objReader }, null
  953. );
  954. object value = null;
  955. MethodCallExpression mce = query as MethodCallExpression;
  956. MethodInfo sequenceMethod = null;
  957. if (mce != null && (
  958. mce.Method.DeclaringType == typeof(Queryable) ||
  959. mce.Method.DeclaringType == typeof(Enumerable))
  960. ) {
  961. switch (mce.Method.Name) {
  962. case "First":
  963. case "FirstOrDefault":
  964. case "SingleOrDefault":
  965. sequenceMethod = TypeSystem.FindSequenceMethod(mce.Method.Name, sequence);
  966. break;
  967. case "Single":
  968. default:
  969. sequenceMethod = TypeSystem.FindSequenceMethod("Single", sequence);
  970. break;
  971. }
  972. }
  973. else {
  974. sequenceMethod = TypeSystem.FindSequenceMethod("SingleOrDefault", sequence);
  975. }
  976. // When dynamically invoking the sequence method, we want to
  977. // return the inner exception if the invocation fails
  978. if (sequenceMethod != null) {
  979. try {
  980. value = sequenceMethod.Invoke(null, new object[] { sequence });
  981. }
  982. catch (TargetInvocationException tie) {
  983. if (tie.InnerException != null) {
  984. throw tie.InnerException;
  985. }
  986. throw;
  987. }
  988. }
  989. return new ExecuteResult(cmd, queryInfo.Parameters, objReader.Session, value);
  990. }
  991. finally {
  992. objReader.Dispose();
  993. }
  994. }
  995. case ResultShape.Sequence: {
  996. DbDataReader reader = cmd.ExecuteReader();
  997. IObjectReader objReader = factory.Create(reader, true, this, parentArgs, userArgs, subQueries);
  998. this.conManager.UseConnection(objReader.Session);
  999. IEnumerable sequence = (IEnumerable)Activator.CreateInstance(
  1000. typeof(OneTimeEnumerable<>).MakeGenericType(TypeSystem.GetElementType(queryInfo.ResultType)),
  1001. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
  1002. new object[] { objReader }, null
  1003. );
  1004. if (typeof(IQueryable).IsAssignableFrom(queryInfo.ResultType)) {
  1005. sequence = sequence.AsQueryable();
  1006. }
  1007. ExecuteResult result = new ExecuteResult(cmd, queryInfo.Parameters, objReader.Session);
  1008. MetaFunction function = this.GetFunction(query);
  1009. if (function != null && !function.IsComposable) {
  1010. sequence = (IEnumerable)Activator.CreateInstance(
  1011. typeof(SingleResult<>).MakeGenericType(TypeSystem.GetElementType(queryInfo.ResultType)),
  1012. BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
  1013. new object[] { sequence, result, this.services.Context }, null
  1014. );
  1015. }
  1016. result.ReturnValue = sequence;
  1017. return result;
  1018. }
  1019. case ResultShape.MultipleResults: {
  1020. DbDataReader reader = cmd.ExecuteReader();
  1021. IObjectReaderSession session = this.readerCompiler.CreateSession(reader, this, parentArgs, userArgs, subQueries);
  1022. this.conManager.UseConnection(session);
  1023. MetaFunction function = this.GetFunction(query);
  1024. ExecuteResult result = new ExecuteResult(cmd, queryInfo.Parameters, session);
  1025. result.ReturnValue = new MultipleResults(this, function, session, result);
  1026. return result;
  1027. }
  1028. }
  1029. }
  1030. finally {
  1031. this.conManager.ReleaseConnection(this);
  1032. }
  1033. }
  1034. private MetaFunction GetFunction(Expression query) {
  1035. LambdaExpression lambda = query as LambdaExpression;
  1036. if (lambda != null) {
  1037. query = lambda.Body;
  1038. }
  1039. MethodCallExpression mc = query as MethodCallExpression;
  1040. if (mc != null && typeof(DataContext).IsAssignableFrom(mc.Method.DeclaringType)) {
  1041. return this.services.Model.GetF

Large files files are truncated, but you can click here to view the full file