PageRenderTime 71ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/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
  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.GetFunction(mc.Method);
  1042. }
  1043. return null;
  1044. }
  1045. private void LogCommand(TextWriter writer, DbCommand cmd) {
  1046. if (writer != null) {
  1047. writer.WriteLine(cmd.CommandText);
  1048. foreach (DbParameter p in cmd.Parameters) {
  1049. int prec = 0;
  1050. int scale = 0;
  1051. PropertyInfo piPrecision = p.GetType().GetProperty("Precision");
  1052. if (piPrecision != null) {
  1053. prec = (int)Convert.ChangeType(piPrecision.GetValue(p, null), typeof(int), CultureInfo.InvariantCulture);
  1054. }
  1055. PropertyInfo piScale = p.GetType().GetProperty("Scale");
  1056. if (piScale != null) {
  1057. scale = (int)Convert.ChangeType(piScale.GetValue(p, null), typeof(int), CultureInfo.InvariantCulture);
  1058. }
  1059. var sp = p as System.Data.SqlClient.SqlParameter;
  1060. writer.WriteLine("-- {0}: {1} {2} (Size = {3}; Prec = {4}; Scale = {5}) [{6}]",
  1061. p.ParameterName,
  1062. p.Direction,
  1063. sp == null ? p.DbType.ToString() : sp.SqlDbType.ToString(),
  1064. p.Size.ToString(System.Globalization.CultureInfo.CurrentCulture),
  1065. prec,
  1066. scale,
  1067. sp == null ? p.Value : sp.SqlValue);
  1068. }
  1069. writer.WriteLine("-- Context: {0}({1}) Model: {2} Build: {3}", this.GetType().Name, this.Mode, this.services.Model.GetType().Name, ThisAssembly.InformationalVersion);
  1070. writer.WriteLine();
  1071. }
  1072. }
  1073. private void AssignParameters(DbCommand cmd, ReadOnlyCollection<SqlParameterInfo> parms, object[] userArguments, object lastResult) {
  1074. if (parms != null) {
  1075. foreach (SqlParameterInfo pi in parms) {
  1076. DbParameter p = cmd.CreateParameter();
  1077. p.ParameterName = pi.Parameter.Name;
  1078. p.Direction = pi.Parameter.Direction;
  1079. if (pi.Parameter.Direction == ParameterDirection.Input ||
  1080. pi.Parameter.Direction == ParameterDirection.InputOutput) {
  1081. object value = pi.Value;
  1082. switch (pi.Type) {
  1083. case SqlParameterType.UserArgument:
  1084. try {
  1085. value = pi.Accessor.DynamicInvoke(new object[] { userArguments });
  1086. } catch (System.Reflection.TargetInvocationException e) {
  1087. throw e.InnerException;
  1088. }
  1089. break;
  1090. case SqlParameterType.PreviousResult:
  1091. value = lastResult;
  1092. break;
  1093. }
  1094. this.typeProvider.InitializeParameter(pi.Parameter.SqlType, p, value);
  1095. }
  1096. else {
  1097. this.typeProvider.InitializeParameter(pi.Parameter.SqlType, p, null);
  1098. }
  1099. cmd.Parameters.Add(p);
  1100. }
  1101. }
  1102. }
  1103. [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
  1104. IEnumerable IProvider.Translate(Type elementType, DbDataReader reader) {
  1105. this.CheckDispose();
  1106. this.CheckInitialized();
  1107. this.InitializeProviderMode();
  1108. if (elementType == null) {
  1109. throw Error.ArgumentNull("elementType");
  1110. }
  1111. if (reader == null) {
  1112. throw Error.ArgumentNull("reader");
  1113. }
  1114. MetaType rowType = services.Model.GetMetaType(elementType);
  1115. IObjectReaderFactory factory = this.GetDefaultFactory(rowType);
  1116. IEnumerator e = factory.Create(reader, true, this, null, null, null);
  1117. Type enumerableType = typeof(OneTimeEnumerable<>).MakeGenericType(elementType);
  1118. return (IEnumerable)Activator.CreateInstance(enumerableType, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { e }, null);
  1119. }
  1120. IMultipleResults IProvider.Translate(DbDataReader reader) {
  1121. this.CheckDispose();
  1122. this.CheckInitialized();
  1123. this.InitializeProviderMode();
  1124. if (reader == null) {
  1125. throw Error.ArgumentNull("reader");
  1126. }
  1127. IObjectReaderSession session = this.readerCompiler.CreateSession(reader, this, null, null, null);
  1128. return new MultipleResults(this, null, session, null);
  1129. }
  1130. string IProvider.GetQueryText(Expression query) {
  1131. this.CheckDispose();
  1132. this.CheckInitialized();
  1133. if (query == null) {
  1134. throw Error.ArgumentNull("query");
  1135. }
  1136. this.InitializeProviderMode();
  1137. SqlNodeAnnotations annotations = new SqlNodeAnnotations();
  1138. QueryInfo[] qis = this.BuildQuery(query, annotations);
  1139. StringBuilder sb = new StringBuilder();
  1140. for (int i = 0, n = qis.Length; i < n; i++) {
  1141. QueryInfo qi = qis[i];
  1142. #if DEBUG
  1143. StringWriter writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
  1144. DbCommand cmd = this.conManager.Connection.CreateCommand();
  1145. cmd.CommandText = qi.CommandText;
  1146. AssignParameters(cmd, qi.Parameters, null, null);
  1147. LogCommand(writer, cmd);
  1148. sb.Append(writer.ToString());
  1149. #else
  1150. sb.Append(qi.CommandText);
  1151. sb.AppendLine();
  1152. #endif
  1153. }
  1154. return sb.ToString();
  1155. }
  1156. DbCommand IProvider.GetCommand(Expression query) {
  1157. this.CheckDispose();
  1158. this.CheckInitialized();
  1159. if (query == null) {
  1160. throw Error.ArgumentNull("query");
  1161. }
  1162. this.InitializeProviderMode();
  1163. SqlNodeAnnotations annotations = new SqlNodeAnnotations();
  1164. QueryInfo[] qis = this.BuildQuery(query, annotations);
  1165. QueryInfo qi = qis[qis.Length - 1];
  1166. DbCommand cmd = this.conManager.Connection.CreateCommand();
  1167. cmd.CommandText = qi.CommandText;
  1168. cmd.Transaction = this.conManager.Transaction;
  1169. cmd.CommandTimeout = this.commandTimeout;
  1170. AssignParameters(cmd, qi.Parameters, null, null);
  1171. return cmd;
  1172. }
  1173. internal class QueryInfo {
  1174. SqlNode query;
  1175. string commandText;
  1176. ReadOnlyCollection<SqlParameterInfo> parameters;
  1177. ResultShape resultShape;
  1178. Type resultType;
  1179. internal QueryInfo(SqlNode query, string commandText, ReadOnlyCollection<SqlParameterInfo> parameters, ResultShape resultShape, Type resultType) {
  1180. this.query = query;
  1181. this.commandText = commandText;
  1182. this.parameters = parameters;
  1183. this.resultShape = resultShape;
  1184. this.resultType = resultType;
  1185. }
  1186. internal SqlNode Query {
  1187. get { return this.query; }
  1188. }
  1189. internal string CommandText {
  1190. get { return this.commandText; }
  1191. }
  1192. internal ReadOnlyCollection<SqlParameterInfo> Parameters {
  1193. get { return this.parameters; }
  1194. }
  1195. internal ResultShape ResultShape {
  1196. get { return this.resultShape; }
  1197. }
  1198. internal Type ResultType {
  1199. get { return this.resultType; }
  1200. }
  1201. }
  1202. internal enum ResultShape {
  1203. Return,
  1204. Singleton,
  1205. Sequence,
  1206. MultipleResults
  1207. }
  1208. private ResultShape GetResultShape(Expression query) {
  1209. LambdaExpression lambda = query as LambdaExpression;
  1210. if (lambda != null) {
  1211. query = lambda.Body;
  1212. }
  1213. if (query.Type == typeof(void)) {
  1214. return ResultShape.Return;
  1215. }
  1216. else if (query.Type == typeof(IMultipleResults)) {
  1217. return ResultShape.MultipleResults;
  1218. }
  1219. bool isSequence = typeof(IEnumerable).IsAssignableFrom(query.Type);
  1220. ProviderType pt = this.typeProvider.From(query.Type);
  1221. bool isScalar = !pt.IsRuntimeOnlyType && !pt.IsApplicationType;
  1222. bool isSingleton = isScalar || !isSequence;
  1223. MethodCallExpression mce = query as MethodCallExpression;
  1224. if (mce != null) {
  1225. // query operators
  1226. if (mce.Method.DeclaringType == typeof(Queryable) ||
  1227. mce.Method.DeclaringType == typeof(Enumerable)) {
  1228. switch (mce.Method.Name) {
  1229. // methods known to produce singletons
  1230. case "First":
  1231. case "FirstOrDefault":
  1232. case "Single":
  1233. case "SingleOrDefault":
  1234. isSingleton = true;
  1235. break;
  1236. }
  1237. }
  1238. else if (mce.Method.DeclaringType == typeof(DataContext)) {
  1239. if (mce.Method.Name == "ExecuteCommand") {
  1240. return ResultShape.Return;
  1241. }
  1242. }
  1243. else if (mce.Method.DeclaringType.IsSubclassOf(typeof(DataContext))) {
  1244. MetaFunction f = this.GetFunction(query);
  1245. if (f != null) {
  1246. if (!f.IsComposable) {
  1247. isSingleton = false;
  1248. }
  1249. else if (isScalar) {
  1250. isSingleton = true;
  1251. }
  1252. }
  1253. }
  1254. else if (mce.Method.DeclaringType == typeof(DataManipulation) && mce.Method.ReturnType == typeof(int)) {
  1255. return ResultShape.Return;
  1256. }
  1257. }
  1258. if (isSingleton) {
  1259. return ResultShape.Singleton;
  1260. }
  1261. else if (isScalar) {
  1262. return ResultShape.Return;
  1263. }
  1264. else {
  1265. return ResultShape.Sequence;
  1266. }
  1267. }
  1268. [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
  1269. private Type GetResultType(Expression query) {
  1270. LambdaExpression lambda = query as LambdaExpression;
  1271. if (lambda != null) {
  1272. query = lambda.Body;
  1273. }
  1274. return query.Type;
  1275. }
  1276. internal QueryInfo[] BuildQuery(Expression query, SqlNodeAnnotations annotations) {
  1277. this.CheckDispose();
  1278. // apply maximal funcletization
  1279. query = Funcletizer.Funcletize(query);
  1280. // convert query nodes into sql nodes
  1281. QueryConverter converter = new QueryConverter(this.services, this.typeProvider, this.translator, this.sqlFactory);
  1282. switch (this.Mode) {
  1283. case ProviderMode.Sql2000:
  1284. converter.ConverterStrategy =
  1285. ConverterStrategy.CanUseScopeIdentity |
  1286. ConverterStrategy.CanUseJoinOn |
  1287. ConverterStrategy.CanUseRowStatus;
  1288. break;
  1289. case ProviderMode.Sql2005:
  1290. case ProviderMode.Sql2008:
  1291. converter.ConverterStrategy =
  1292. ConverterStrategy.CanUseScopeIdentity |
  1293. ConverterStrategy.SkipWithRowNumber |
  1294. ConverterStrategy.CanUseRowStatus |
  1295. ConverterStrategy.CanUseJoinOn |
  1296. ConverterStrategy.CanUseOuterApply |
  1297. ConverterStrategy.CanOutputFromInsert;
  1298. break;
  1299. case ProviderMode.SqlCE:
  1300. converter.ConverterStrategy = ConverterStrategy.CanUseOuterApply;
  1301. // Can't set ConverterStrategy.CanUseJoinOn because scalar subqueries in the ON clause
  1302. // can't be converted into anything.
  1303. break;
  1304. }
  1305. SqlNode node = converter.ConvertOuter(query);
  1306. return this.BuildQuery(this.GetResultShape(query), this.GetResultType(query), node, null, annotations);
  1307. }
  1308. [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", 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.")]
  1309. private QueryInfo[] BuildQuery(ResultShape resultShape, Type resultType, SqlNode node, ReadOnlyCollection<Me.SqlParameter> parentParameters, SqlNodeAnnotations annotations) {
  1310. System.Diagnostics.Debug.Assert(resultType != null);
  1311. System.Diagnostics.Debug.Assert(node != null);
  1312. SqlSupersetValidator validator = new SqlSupersetValidator();
  1313. // These are the rules that apply to every SQL tree.
  1314. if (this.checkQueries) {
  1315. validator.AddValidator(new ColumnTypeValidator()); /* Column CLR Type must agree with its Expressions CLR Type */
  1316. validator.AddValidator(new LiteralValidator()); /* Constrain literal Types */
  1317. }
  1318. validator.Validate(node);
  1319. SqlColumnizer columnizer = new SqlColumnizer();
  1320. // resolve member references
  1321. bool canUseOuterApply = (this.Mode == ProviderMode.Sql2005 || this.Mode == ProviderMode.Sql2008 || this.Mode == ProviderMode.SqlCE);
  1322. SqlBinder binder = new SqlBinder(this.translator, this.sqlFactory, this.services.Model, this.services.Context.LoadOptions, columnizer, canUseOuterApply);
  1323. binder.OptimizeLinkExpansions = (optimizationFlags & OptimizationFlags.OptimizeLinkExpansions) != 0;
  1324. binder.SimplifyCaseStatements = (optimizationFlags & OptimizationFlags.SimplifyCaseStatements) != 0;
  1325. binder.PreBinder = delegate(SqlNode n) {
  1326. // convert methods into known reversable operators
  1327. return PreBindDotNetConverter.Convert(n, this.sqlFactory, this.services.Model);
  1328. };
  1329. node = binder.Bind(node);
  1330. if (this.checkQueries) {
  1331. validator.AddValidator(new ExpectNoAliasRefs());
  1332. validator.AddValidator(new ExpectNoSharedExpressions());
  1333. }
  1334. validator.Validate(node);
  1335. node = PostBindDotNetConverter.Convert(node, this.sqlFactory, this.Mode);
  1336. // identify true flow of sql data types
  1337. SqlRetyper retyper = new SqlRetyper(this.typeProvider, this.services.Model);
  1338. node = retyper.Retype(node);
  1339. validator.Validate(node);
  1340. // change CONVERT to special conversions like UNICODE,CHAR,...
  1341. SqlTypeConverter converter = new SqlTypeConverter(this.sqlFactory);
  1342. node = converter.Visit(node);
  1343. validator.Validate(node);
  1344. // transform type-sensitive methods such as LEN (to DATALENGTH), ...
  1345. SqlMethodTransformer methodTransformer = new SqlMethodTransformer(this.sqlFactory);
  1346. node = methodTransformer.Visit(node);
  1347. validator.Validate(node);
  1348. // convert multisets into separate queries
  1349. SqlMultiplexer.Options options = (this.Mode == ProviderMode.Sql2008 ||
  1350. this.Mode == ProviderMode.Sql2005 ||
  1351. this.Mode == ProviderMode.SqlCE)
  1352. ? SqlMultiplexer.Options.EnableBigJoin : SqlMultiplexer.Options.None;
  1353. SqlMultiplexer mux = new SqlMultiplexer(options, parentParameters, this.sqlFactory);
  1354. node = mux.Multiplex(node);
  1355. validator.Validate(node);
  1356. // convert object construction expressions into flat row projections
  1357. SqlFlattener flattener = new SqlFlattener(this.sqlFactory, columnizer);
  1358. node = flattener.Flatten(node);
  1359. validator.Validate(node);
  1360. if (this.mode == ProviderMode.SqlCE) {
  1361. SqlRewriteScalarSubqueries rss = new SqlRewriteScalarSubqueries(this.sqlFactory);
  1362. node = rss.Rewrite(node);
  1363. }
  1364. // Simplify case statements where all alternatives map to the same thing.
  1365. // Doing this before deflator because the simplified results may lead to
  1366. // more deflation opportunities.
  1367. // Doing this before booleanizer because it may convert CASE statements (non-predicates) into
  1368. // predicate expressions.
  1369. // Doing this before reorderer because it may reduce some orders to constant nodes which should not
  1370. // be passed onto ROW_NUMBER.
  1371. node = SqlCaseSimplifier.Simplify(node, this.sqlFactory);
  1372. // Rewrite order-by clauses so that they only occur at the top-most select
  1373. // or in selects with TOP
  1374. SqlReorderer reorderer = new SqlReorderer(this.typeProvider, this.sqlFactory);
  1375. node = reorderer.Reorder(node);
  1376. validator.Validate(node);
  1377. // Inject code to turn predicates into bits, and bits into predicates where necessary
  1378. node = SqlBooleanizer.Rationalize(node, this.typeProvider, this.services.Model);
  1379. if (this.checkQueries) {
  1380. validator.AddValidator(new ExpectRationalizedBooleans()); /* From now on all boolean expressions should remain rationalized. */
  1381. }
  1382. validator.Validate(node);
  1383. if (this.checkQueries) {
  1384. validator.AddValidator(new ExpectNoFloatingColumns());
  1385. }
  1386. // turning predicates into bits/ints can change Sql types, propagate changes
  1387. node = retyper.Retype(node);
  1388. validator.Validate(node);
  1389. // assign aliases to columns
  1390. // we need to do this now so that the sql2k lifters will work
  1391. SqlAliaser aliaser = new SqlAliaser();
  1392. node = aliaser.AssociateColumnsWithAliases(node);
  1393. validator.Validate(node);
  1394. // SQL2K enablers.
  1395. node = SqlLiftWhereClauses.Lift(node, this.typeProvider, this.services.Model);
  1396. node = SqlLiftIndependentRowExpressions.Lift(node);
  1397. node = SqlOuterApplyReducer.Reduce(node, this.sqlFactory, annotations);
  1398. node = SqlTopReducer.Reduce(node, annotations, this.sqlFactory);
  1399. // resolve references to columns in other scopes by adding them
  1400. // to the intermediate selects
  1401. SqlResolver resolver = new SqlResolver();
  1402. node = resolver.Resolve(node);
  1403. validator.Validate(node);
  1404. // re-assign aliases after resolving (new columns may have been added)
  1405. node = aliaser.AssociateColumnsWithAliases(node);
  1406. validator.Validate(node);
  1407. // fixup union projections
  1408. node = SqlUnionizer.Unionize(node);
  1409. // remove order-by of literals
  1410. node = SqlRemoveConstantOrderBy.Remove(node);
  1411. // throw out unused columns and redundant sub-queries...
  1412. SqlDeflator deflator = new SqlDeflator();
  1413. node = deflator.Deflate(node);
  1414. validator.Validate(node);
  1415. // Positioning after deflator because it may remove unnecessary columns
  1416. // from SELECT projection lists and allow more CROSS APPLYs to be reduced
  1417. // to CROSS JOINs.
  1418. node = SqlCrossApplyToCrossJoin.Reduce(node, annotations);
  1419. // fixup names for aliases, columns, locals, etc..
  1420. SqlNamer namer = new SqlNamer();
  1421. node = namer.AssignNames(node);
  1422. validator.Validate(node);
  1423. // Convert [N]Text,Image to [N]VarChar(MAX),VarBinary(MAX) where necessary.
  1424. // These new types do not exist on SQL2k, so add annotations.
  1425. LongTypeConverter longTypeConverter = new LongTypeConverter(this.sqlFactory);
  1426. node = longTypeConverter.AddConversions(node, annotations);
  1427. // final validation
  1428. validator.AddValidator(new ExpectNoMethodCalls());
  1429. validator.AddValidator(new ValidateNoInvalidComparison());
  1430. validator.Validate(node);
  1431. SqlParameterizer parameterizer = new SqlParameterizer(this.typeProvider, annotations);
  1432. SqlFormatter formatter = new SqlFormatter();
  1433. if (this.mode == ProviderMode.SqlCE ||
  1434. this.mode == ProviderMode.Sql2005 ||
  1435. this.mode == ProviderMode.Sql2008) {
  1436. formatter.ParenthesizeTop = true;
  1437. }
  1438. SqlBlock block = node as SqlBlock;
  1439. if (block != null && this.mode == ProviderMode.SqlCE) {
  1440. // SQLCE cannot batch multiple statements.
  1441. ReadOnlyCollection<ReadOnlyCollection<SqlParameterInfo>> parameters = parameterizer.ParameterizeBlock(block);
  1442. string[] commands = formatter.FormatBlock(block, false);
  1443. QueryInfo[] queries = new QueryInfo[commands.Length];
  1444. for (int i = 0, n = commands.Length; i < n; i++) {
  1445. queries[i] = new QueryInfo(
  1446. block.Statements[i],
  1447. commands[i],
  1448. parameters[i],
  1449. (i < n - 1) ? ResultShape.Return : resultShape,
  1450. (i < n - 1) ? typeof(int) : resultType
  1451. );
  1452. }
  1453. return queries;
  1454. }
  1455. else {
  1456. // build only one result
  1457. ReadOnlyCollection<SqlParameterInfo> parameters = parameterizer.Parameterize(node);
  1458. string commandText = formatter.Format(node);
  1459. return new QueryInfo[] {
  1460. new QueryInfo(node, commandText, parameters, resultShape, resultType)
  1461. };
  1462. }
  1463. }
  1464. private SqlSelect GetFinalSelect(SqlNode node) {
  1465. switch (node.NodeType) {
  1466. case SqlNodeType.Select:
  1467. return (SqlSelect)node;
  1468. case SqlNodeType.Block: {
  1469. SqlBlock b = (SqlBlock)node;
  1470. return GetFinalSelect(b.Statements[b.Statements.Count - 1]);
  1471. }
  1472. }
  1473. return null;
  1474. }
  1475. private IObjectReaderFactory GetReaderFactory(SqlNode node, Type elemType) {
  1476. SqlSelect sel = node as SqlSelect;
  1477. SqlExpression projection = null;
  1478. if (sel == null && node.NodeType == SqlNodeType.Block) {
  1479. sel = this.GetFinalSelect(node);
  1480. }
  1481. if (sel != null) {
  1482. projection = sel.Selection;
  1483. }
  1484. else {
  1485. SqlUserQuery suq = node as SqlUserQuery;
  1486. if (suq != null && suq.Projection != null) {
  1487. projection = suq.Projection;
  1488. }
  1489. }
  1490. IObjectReaderFactory factory;
  1491. if (projection != null) {
  1492. factory = this.readerCompiler.Compile(projection, elemType);
  1493. }
  1494. else {
  1495. return this.GetDefaultFactory(services.Model.GetMetaType(elemType));
  1496. }
  1497. return factory;
  1498. }
  1499. private IObjectReaderFactory GetDefaultFactory(MetaType rowType) {
  1500. if (rowType == null) {
  1501. throw Error.ArgumentNull("rowType");
  1502. }
  1503. SqlNodeAnnotations annotations = new SqlNodeAnnotations();
  1504. Expression tmp = Expression.Constant(null);
  1505. SqlUserQuery suq = new SqlUserQuery(string.Empty, null, null, tmp);
  1506. if (TypeSystem.IsSimpleType(rowType.Type)) {
  1507. // if the element type is a simple type (int, bool, etc.) we create
  1508. // a single column binding
  1509. SqlUserColumn col = new SqlUserColumn(rowType.Type, typeProvider.From(rowType.Type), suq, "", false, suq.SourceExpression);
  1510. suq.Columns.Add(col);
  1511. suq.Projection = col;
  1512. }
  1513. else {
  1514. // ... otherwise we generate a default projection
  1515. SqlUserRow rowExp = new SqlUserRow(rowType.InheritanceRoot, this.typeProvider.GetApplicationType((int)ConverterSpecialTypes.Row), suq, tmp);
  1516. suq.Projection = this.translator.BuildProjection(rowExp, rowType, true, null, tmp);
  1517. }
  1518. Type resultType = TypeSystem.GetSequenceType(rowType.Type);
  1519. QueryInfo[] qis = this.BuildQuery(ResultShape.Sequence, resultType, suq, null, annotations);
  1520. return this.GetReaderFactory(qis[qis.Length - 1].Query, rowType.Type);
  1521. }
  1522. class CompiledQuery : ICompiledQuery {
  1523. DataLoadOptions originalShape;
  1524. Expression query;
  1525. QueryInfo[] queryInfos;
  1526. IObjectReaderFactory factory;
  1527. ICompiledSubQuery[] subQueries;
  1528. internal CompiledQuery(SqlProvider provider, Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, ICompiledSubQuery[] subQueries) {
  1529. this.originalShape = provider.services.Context.LoadOptions;
  1530. this.query = query;
  1531. this.queryInfos = queryInfos;
  1532. this.factory = factory;
  1533. this.subQueries = subQueries;
  1534. }
  1535. public IExecuteResult Execute(IProvider provider, object[] arguments) {
  1536. if (provider == null) {
  1537. throw Error.ArgumentNull("provider");
  1538. }
  1539. SqlProvider sqlProvider = provider as SqlProvider;
  1540. if (sqlProvider == null) {
  1541. throw Error.ArgumentTypeMismatch("provider");
  1542. }
  1543. // verify shape is compatibile with original.
  1544. if (!AreEquivalentShapes(this.originalShape, sqlProvider.services.Context.LoadOptions)) {
  1545. throw Error.CompiledQueryAgainstMultipleShapesNotSupported();
  1546. }
  1547. // execute query (only last query produces results)
  1548. return sqlProvider.ExecuteAll(this.query, this.queryInfos, this.factory, arguments, subQueries);
  1549. }
  1550. private static bool AreEquivalentShapes(DataLoadOptions shape1, DataLoadOptions shape2) {
  1551. if (shape1 == shape2) {
  1552. return true;
  1553. }
  1554. else if (shape1 == null) {
  1555. return shape2.IsEmpty;
  1556. }
  1557. else if (shape2 == null) {
  1558. return shape1.IsEmpty;
  1559. }
  1560. else if (shape1.IsEmpty && shape2.IsEmpty) {
  1561. return true;
  1562. }
  1563. return false;
  1564. }
  1565. }
  1566. class CompiledSubQuery : ICompiledSubQuery {
  1567. QueryInfo queryInfo;
  1568. IObjectReaderFactory factory;
  1569. ReadOnlyCollection<Me.SqlParameter> parameters;
  1570. ICompiledSubQuery[] subQueries;
  1571. internal CompiledSubQuery(QueryInfo queryInfo, IObjectReaderFactory factory, ReadOnlyCollection<Me.SqlParameter> parameters, ICompiledSubQuery[] subQueries) {
  1572. this.queryInfo = queryInfo;
  1573. this.factory = factory;
  1574. this.parameters = parameters;
  1575. this.subQueries = subQueries;
  1576. }
  1577. public IExecuteResult Execute(IProvider provider, object[] parentArgs, object[] userArgs) {
  1578. if (parentArgs == null && !(this.parameters == null || this.parameters.Count == 0)) {
  1579. throw Error.ArgumentNull("arguments");
  1580. }
  1581. SqlProvider sqlProvider = provider as SqlProvider;
  1582. if (sqlProvider == null) {
  1583. throw Error.ArgumentTypeMismatch("provider");
  1584. }
  1585. // construct new copy of query info
  1586. List<SqlParameterInfo> spis = new List<SqlParameterInfo>(this.queryInfo.Parameters);
  1587. // add call arguments
  1588. for (int i = 0, n = this.parameters.Count; i < n; i++) {
  1589. spis.Add(new SqlParameterInfo(this.parameters[i], parentArgs[i]));
  1590. }
  1591. QueryInfo qi = new QueryInfo(
  1592. this.queryInfo.Query,
  1593. this.queryInfo.CommandText,
  1594. spis.AsReadOnly(),
  1595. this.queryInfo.ResultShape,
  1596. this.queryInfo.ResultType
  1597. );
  1598. // execute query
  1599. return sqlProvider.Execute(null, qi, this.factory, parentArgs, userArgs, subQueries, null);
  1600. }
  1601. }
  1602. class ExecuteResult : IExecuteResult, IDisposable {
  1603. DbCommand command;
  1604. ReadOnlyCollection<SqlParameterInfo> parameters;
  1605. IObjectReaderSession session;
  1606. int iReturnParameter = -1;
  1607. object value;
  1608. [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Microsoft: used in an assert in ReturnValue.set")]
  1609. bool useReturnValue;
  1610. bool isDisposed;
  1611. internal ExecuteResult(DbCommand command, ReadOnlyCollection<SqlParameterInfo> parameters, IObjectReaderSession session, object value, bool useReturnValue)
  1612. : this(command, parameters, session) {
  1613. this.value = value;
  1614. this.useReturnValue = useReturnValue;
  1615. if (this.command != null && this.parameters != null && useReturnValue) {
  1616. iReturnParameter = GetParameterIndex("@RETURN_VALUE");
  1617. }
  1618. }
  1619. internal ExecuteResult(DbCommand command, ReadOnlyCollection<SqlParameterInfo> parameters, IObjectReaderSession session) {
  1620. this.command = command;
  1621. this.parameters = parameters;
  1622. this.session = session;
  1623. }
  1624. internal ExecuteResult(DbCommand command, ReadOnlyCollection<SqlParameterInfo> parameters, IObjectReaderSession session, object value)
  1625. : this(command, parameters, session, value, false) {
  1626. }
  1627. [SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "value", Justification="FxCop Error -- False positive during code analysis")]
  1628. public object ReturnValue {
  1629. get {
  1630. if (this.iReturnParameter >= 0) {
  1631. return this.GetParameterValue(this.iReturnParameter);
  1632. }
  1633. return this.value;
  1634. }
  1635. internal set {
  1636. Debug.Assert(!useReturnValue);
  1637. this.value = value;
  1638. }
  1639. }
  1640. private int GetParameterIndex(string paramName) {
  1641. int idx = -1;
  1642. for (int i = 0, n = this.parameters.Count; i < n; i++) {
  1643. if (string.Compare(parameters[i].Parameter.Name, paramName, StringComparison.OrdinalIgnoreCase) == 0) {
  1644. idx = i;
  1645. break;
  1646. }
  1647. }
  1648. return idx;
  1649. }
  1650. internal object GetParameterValue(string paramName) {
  1651. int idx = GetParameterIndex(paramName);
  1652. if (idx >= 0) {
  1653. return GetParameterValue(idx);
  1654. }
  1655. return null;
  1656. }
  1657. public object GetParameterValue(int parameterIndex) {
  1658. if (this.parameters == null || parameterIndex < 0 || parameterIndex > this.parameters.Count) {
  1659. throw Error.ArgumentOutOfRange("parameterIndex");
  1660. }
  1661. // SQL server requires all results to be read before output parameters are visible
  1662. if (this.session != null && !this.session.IsBuffered) {
  1663. this.session.Buffer();
  1664. }
  1665. SqlParameterInfo pi = this.parameters[parameterIndex];
  1666. object parameterValue = this.command.Parameters[parameterIndex].Value;
  1667. if (parameterValue == DBNull.Value) parameterValue = null;
  1668. if (parameterValue != null && parameterValue.GetType() != pi.Parameter.ClrType) {
  1669. return DBConvert.ChangeType(parameterValue, pi.Parameter.ClrType);
  1670. }
  1671. return parameterValue;
  1672. }
  1673. public void Dispose() {
  1674. if (!this.isDisposed) {
  1675. // Technically, calling GC.SuppressFinalize is not required because the class does not
  1676. // have a finalizer, but it does no harm, protects against the case where a finalizer is added
  1677. // in the future, and prevents an FxCop warning.
  1678. GC.SuppressFinalize(this);
  1679. this.isDisposed = true;
  1680. if (this.session!=null) {
  1681. this.session.Dispose();
  1682. }
  1683. }
  1684. }
  1685. }
  1686. class SequenceOfOne<T> : IEnumerable<T>, IEnumerable {
  1687. T[] sequence;
  1688. internal SequenceOfOne(T value) {
  1689. this.sequence = new T[] { value };
  1690. }
  1691. public IEnumerator<T> GetEnumerator() {
  1692. return ((IEnumerable<T>)this.sequence).GetEnumerator();
  1693. }
  1694. IEnumerator IEnumerable.GetEnumerator() {
  1695. return this.GetEnumerator();
  1696. }
  1697. }
  1698. class OneTimeEnumerable<T> : IEnumerable<T>, IEnumerable {
  1699. IEnumerator<T> enumerator;
  1700. internal OneTimeEnumerable(IEnumerator<T> enumerator) {
  1701. System.Diagnostics.Debug.Assert(enumerator != null);
  1702. this.enumerator = enumerator;
  1703. }
  1704. public IEnumerator<T> GetEnumerator() {
  1705. if (this.enumerator == null) {
  1706. throw Error.CannotEnumerateResultsMoreThanOnce();
  1707. }
  1708. IEnumerator<T> e = this.enumerator;
  1709. this.enumerator = null;
  1710. return e;
  1711. }
  1712. IEnumerator IEnumerable.GetEnumerator() {
  1713. return this.GetEnumerator();
  1714. }
  1715. }
  1716. /// <summary>
  1717. /// Result type for single rowset returning stored procedures.
  1718. /// </summary>
  1719. class SingleResult<T> : ISingleResult<T>, IDisposable, IListSource {
  1720. private IEnumerable<T> enumerable;
  1721. private ExecuteResult executeResult;
  1722. private DataContext context;
  1723. private IBindingList cachedList;
  1724. internal SingleResult(IEnumerable<T> enumerable, ExecuteResult executeResult, DataContext context) {
  1725. System.Diagnostics.Debug.Assert(enumerable != null);
  1726. System.Diagnostics.Debug.Assert(executeResult != null);
  1727. this.enumerable = enumerable;
  1728. this.executeResult = executeResult;
  1729. this.context = context;
  1730. }
  1731. public IEnumerator<T> GetEnumerator() {
  1732. return enumerable.GetEnumerator();
  1733. }
  1734. IEnumerator IEnumerable.GetEnumerator() {
  1735. return this.GetEnumerator();
  1736. }
  1737. public object ReturnValue {
  1738. get {
  1739. return executeResult.GetParameterValue("@RETURN_VALUE");
  1740. }
  1741. }
  1742. public void Dispose() {
  1743. // Technically, calling GC.SuppressFinalize is not required because the class does not
  1744. // have a finalizer, but it does no harm, protects against the case where a finalizer is added
  1745. // in the future, and prevents an FxCop warning.
  1746. GC.SuppressFinalize(this);
  1747. this.executeResult.Dispose();
  1748. }
  1749. IList IListSource.GetList() {
  1750. if (this.cachedList == null) {
  1751. this.cachedList = BindingList.Create<T>(this.context, this);
  1752. }
  1753. return this.cachedList;
  1754. }
  1755. bool IListSource.ContainsListCollection {
  1756. get { return false; }
  1757. }
  1758. }
  1759. class MultipleResults : IMultipleResults, IDisposable {
  1760. SqlProvider provider;
  1761. MetaFunction function;
  1762. IObjectReaderSession session;
  1763. bool isDisposed;
  1764. private ExecuteResult executeResult;
  1765. internal MultipleResults(SqlProvider provider, MetaFunction function, IObjectReaderSession session, ExecuteResult executeResult) {
  1766. this.provider = provider;
  1767. this.function = function;
  1768. this.session = session;
  1769. this.executeResult = executeResult;
  1770. }
  1771. public IEnumerable<T> GetResult<T>() {
  1772. MetaType metaType = null;
  1773. // Check the inheritance hierarchy of each mapped result row type
  1774. // for the function.
  1775. if (this.function != null) {
  1776. foreach (MetaType mt in function.ResultRowTypes) {
  1777. metaType = mt.InheritanceTypes.SingleOrDefault(it => it.Type == typeof(T));
  1778. if (metaType != null) {
  1779. break;
  1780. }
  1781. }
  1782. }
  1783. if (metaType == null) {
  1784. metaType = this.provider.services.Model.GetMetaType(typeof(T));
  1785. }
  1786. IObjectReaderFactory factory = this.provider.GetDefaultFactory(metaType);
  1787. IObjectReader objReader = factory.GetNextResult(this.session, false);
  1788. if (objReader == null) {
  1789. this.Dispose();
  1790. return null;
  1791. }
  1792. return new SingleResult<T>(new OneTimeEnumerable<T>((IEnumerator<T>)objReader), this.executeResult, this.provider.services.Context);
  1793. }
  1794. public void Dispose() {
  1795. if (!this.isDisposed) {
  1796. // Technically, calling GC.SuppressFinalize is not required because the class does not
  1797. // have a finalizer, but it does no harm, protects against the case where a finalizer is added
  1798. // in the future, and prevents an FxCop warning.
  1799. GC.SuppressFinalize(this);
  1800. this.isDisposed = true;
  1801. if (this.executeResult != null) {
  1802. this.executeResult.Dispose();
  1803. }
  1804. else {
  1805. this.session.Dispose();
  1806. }
  1807. }
  1808. }
  1809. public object ReturnValue {
  1810. get {
  1811. if (this.executeResult != null) {
  1812. return executeResult.GetParameterValue("@RETURN_VALUE");
  1813. } else {
  1814. return null;
  1815. }
  1816. }
  1817. }
  1818. }
  1819. }
  1820. }