437759cb900e786adf8e02bbfeb7669b903e3705
[mono.git] / mcs / class / referencesource / System.Data.Linq / SqlClient / SqlProvider.cs
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
24 namespace System.Data.Linq.SqlClient {
25     public sealed class Sql2000Provider : SqlProvider {
26         public Sql2000Provider()
27             : base(ProviderMode.Sql2000) {
28         }
29     }
30
31     public sealed class Sql2005Provider : SqlProvider {
32         public Sql2005Provider()
33             : base(ProviderMode.Sql2005) {
34         }
35     }
36
37     public sealed class Sql2008Provider : SqlProvider {
38         public Sql2008Provider()
39             : base(ProviderMode.Sql2008) {
40         }
41     }
42
43     [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification="Unknown reason.")]
44     public class SqlProvider : IReaderProvider, IConnectionUser {
45         private IDataServices services;
46         private SqlConnectionManager conManager;
47         private TypeSystemProvider typeProvider;
48         private SqlFactory sqlFactory;
49         private Translator translator;
50         private IObjectReaderCompiler readerCompiler;
51         private bool disposed;
52         private int commandTimeout;
53
54         private TextWriter log;
55         string dbName = string.Empty;
56
57         // stats and flags
58         private int queryCount;
59         private bool checkQueries;
60         private OptimizationFlags optimizationFlags = OptimizationFlags.All;
61         private bool enableCacheLookup = true;
62         private ProviderMode mode = ProviderMode.NotYetDecided;
63         private bool deleted = false;
64
65 #if PERFORMANCE_BUILD
66         private bool collectPerfInfo;
67         private bool collectPerfInfoInitialized = false;
68         private bool collectQueryPerf;
69
70         internal bool CollectPerfInfo {
71             get { 
72                 if (!collectPerfInfoInitialized) {
73                     string s = System.Environment.GetEnvironmentVariable("CollectDLinqPerfInfo");
74                     collectPerfInfo = (s != null) && (s == "On");
75                     collectPerfInfoInitialized = true;
76                 }
77                 return this.collectPerfInfo; 
78             }
79         }
80
81         internal bool CollectQueryPerf {
82             get { return this.collectQueryPerf; }
83         }
84 #endif
85
86         internal enum ProviderMode {
87             NotYetDecided,
88             Sql2000,
89             Sql2005,
90             Sql2008,
91             SqlCE
92         }
93
94         const string SqlCeProviderInvariantName = "System.Data.SqlServerCe.3.5";
95         const string SqlCeDataReaderTypeName = "System.Data.SqlServerCe.SqlCeDataReader";
96         const string SqlCeConnectionTypeName = "System.Data.SqlServerCe.SqlCeConnection";
97         const string SqlCeTransactionTypeName = "System.Data.SqlServerCe.SqlCeTransaction";
98
99         internal ProviderMode Mode {
100             get {
101                 this.CheckDispose();
102                 this.CheckInitialized();
103                 this.InitializeProviderMode();
104                 return this.mode; 
105             }
106         }
107         
108         private void InitializeProviderMode() {
109             if (this.mode == ProviderMode.NotYetDecided) {
110                 if (this.IsSqlCe) {
111                     this.mode = ProviderMode.SqlCE;
112                 } else if (this.IsServer2KOrEarlier) {
113                     this.mode = ProviderMode.Sql2000;
114                 }
115                 else if (this.IsServer2005) {
116                     this.mode = ProviderMode.Sql2005;
117                 } else {
118                     this.mode = ProviderMode.Sql2008;
119                 }
120             }
121             if (this.typeProvider == null) {
122                 switch (this.mode) {
123                     case ProviderMode.Sql2000:
124                         this.typeProvider = SqlTypeSystem.Create2000Provider();
125                         break;
126                     case ProviderMode.Sql2005:
127                         this.typeProvider = SqlTypeSystem.Create2005Provider();
128                         break;
129                     case ProviderMode.Sql2008:
130                         this.typeProvider = SqlTypeSystem.Create2008Provider();
131                         break;
132                     case ProviderMode.SqlCE:
133                         this.typeProvider = SqlTypeSystem.CreateCEProvider();
134                         break;
135                     default:
136                         System.Diagnostics.Debug.Assert(false);
137                         break;
138                 }
139             }
140             if (this.sqlFactory == null) {
141                 this.sqlFactory = new SqlFactory(this.typeProvider, this.services.Model);
142                 this.translator = new Translator(this.services, this.sqlFactory, this.typeProvider);
143             }
144         }
145
146         /// <summary>
147         /// Return true if the current connection is SQLCE.
148         /// </summary>
149         private bool IsSqlCe {
150             get {
151                 DbConnection con = conManager.UseConnection(this);
152                 try {
153                     if (String.CompareOrdinal(con.GetType().FullName, SqlCeConnectionTypeName) == 0) {
154                         return true;
155                     }
156                 } finally {
157                     conManager.ReleaseConnection(this);
158                 }
159                 return false;
160             }
161         }
162         
163         /// <summary>
164         /// Return true if this is a 2K (or earlier) server. This may be a round trip to the server.
165         /// </summary>
166         private bool IsServer2KOrEarlier {
167             get {
168                 DbConnection con = conManager.UseConnection(this);
169                 try {
170                     string serverVersion = con.ServerVersion;
171                     if (serverVersion.StartsWith("06.00.", StringComparison.Ordinal)) {
172                         return true;
173                     }
174                     else if (serverVersion.StartsWith("06.50.", StringComparison.Ordinal)) {
175                         return true;
176                     }
177                     else if (serverVersion.StartsWith("07.00.", StringComparison.Ordinal)) {
178                         return true;
179                     }
180                     else if (serverVersion.StartsWith("08.00.", StringComparison.Ordinal)) {
181                         return true;
182                     }
183                     return false;
184                 }
185                 finally {
186                     conManager.ReleaseConnection(this);
187                 }
188             }
189         }
190
191         /// <summary>
192         /// Return true if this is a SQL 2005 server. This may be a round trip to the server.
193         /// </summary>
194         private bool IsServer2005 {
195             get {
196                 DbConnection con = conManager.UseConnection(this);
197                 try {
198                     string serverVersion = con.ServerVersion;
199                     if (serverVersion.StartsWith("09.00.", StringComparison.Ordinal)) {
200                         return true;
201                     }
202                     return false;
203                 }
204                 finally {
205                     conManager.ReleaseConnection(this);
206                 }
207             }
208         }
209
210         DbConnection IProvider.Connection {
211             get {
212                 this.CheckDispose();
213                 this.CheckInitialized();
214                 return this.conManager.Connection;
215             }
216         }
217
218         TextWriter IProvider.Log {
219             get {
220                 this.CheckDispose();
221                 this.CheckInitialized();
222                 return this.log;
223             }
224             set {
225                 this.CheckDispose();
226                 this.CheckInitialized();
227                 this.log = value;
228             }
229         }
230
231         DbTransaction IProvider.Transaction {
232             get {
233                 this.CheckDispose();
234                 this.CheckInitialized();
235                 return this.conManager.Transaction;
236             }
237             set {
238                 this.CheckDispose();
239                 this.CheckInitialized();
240                 this.conManager.Transaction = value;
241             }
242         }
243
244         int IProvider.CommandTimeout {
245             get {
246                 this.CheckDispose();
247                 return this.commandTimeout; 
248             }
249             set {
250                 this.CheckDispose();
251                 this.commandTimeout = value; 
252             }
253         }
254
255         /// <summary>
256         /// Expose a test hook which controls which SQL optimizations are executed.
257         /// </summary>
258         internal OptimizationFlags OptimizationFlags {
259             get { 
260                 CheckDispose();
261                 return this.optimizationFlags; 
262             }
263             set { 
264                 CheckDispose();
265                 this.optimizationFlags = value; 
266             }
267         }
268
269         /// <summary>
270         /// Validate queries as they are generated.
271         /// </summary>
272         internal bool CheckQueries {
273             get { 
274                 CheckDispose();
275                 return checkQueries; 
276             }
277             set { 
278                 CheckDispose();
279                 checkQueries = value; 
280             }
281         }
282
283         internal bool EnableCacheLookup {
284             get { 
285                 CheckDispose();
286                 return this.enableCacheLookup; 
287             }
288             set { 
289                 CheckDispose();
290                 this.enableCacheLookup = value; 
291             }
292         }
293
294         internal int QueryCount {
295             get { 
296                 CheckDispose();
297                 return this.queryCount; 
298             }
299         }
300
301         internal int MaxUsers {
302             get {
303                 CheckDispose();
304                 return this.conManager.MaxUsers;
305             }
306         }
307
308         IDataServices IReaderProvider.Services {
309             get { return this.services; }
310         }
311
312         IConnectionManager IReaderProvider.ConnectionManager {
313             get { return this.conManager; }
314         }
315
316         public SqlProvider() {
317             this.mode = ProviderMode.NotYetDecided;
318         }
319
320         internal SqlProvider(ProviderMode mode) {
321             this.mode = mode;
322         }
323
324         private void CheckInitialized() {
325             if (this.services == null) {
326                 throw Error.ContextNotInitialized();
327             }
328         }
329         private void CheckNotDeleted() {
330             if (this.deleted) {
331                 throw Error.DatabaseDeleteThroughContext();
332             }
333         }
334
335         [ResourceExposure(ResourceScope.Machine)] // connection parameter may refer to filenames.
336         void IProvider.Initialize(IDataServices dataServices, object connection) {
337             if (dataServices == null) {
338                 throw Error.ArgumentNull("dataServices");
339             }
340             this.services = dataServices;
341
342             DbConnection con;
343             DbTransaction tx = null;
344
345             string fileOrServerOrConnectionString = connection as string;
346             if (fileOrServerOrConnectionString != null) {
347                 string connectionString = this.GetConnectionString(fileOrServerOrConnectionString);
348                 this.dbName = this.GetDatabaseName(connectionString);
349                 if (this.dbName.EndsWith(".sdf", StringComparison.OrdinalIgnoreCase)) {
350                     this.mode = ProviderMode.SqlCE;
351                 }
352                 if (this.mode == ProviderMode.SqlCE) {
353                     DbProviderFactory factory = SqlProvider.GetProvider(SqlCeProviderInvariantName);
354                     if (factory == null) {
355                         throw Error.ProviderNotInstalled(this.dbName, SqlCeProviderInvariantName);
356                     }
357                     con = factory.CreateConnection();
358                 }
359                 else {
360                     con = new SqlConnection();
361                 }
362                 con.ConnectionString = connectionString;
363             }
364             else {
365                 // We only support SqlTransaction and SqlCeTransaction
366                 tx = connection as SqlTransaction;
367                 if (tx == null) {
368                     // See if it's a SqlCeTransaction
369                     if (connection.GetType().FullName == SqlCeTransactionTypeName) {
370                         tx = connection as DbTransaction;
371                     }
372                 }
373                 if (tx != null) {
374                     connection = tx.Connection;
375                 }
376                 con = connection as DbConnection;
377                 if (con == null) {
378                     throw Error.InvalidConnectionArgument("connection");
379                 }
380                 if (con.GetType().FullName == SqlCeConnectionTypeName) {
381                     this.mode = ProviderMode.SqlCE;
382                 }
383                 this.dbName = this.GetDatabaseName(con.ConnectionString);
384             }
385             
386             // initialize to the default command timeout
387             using (DbCommand c = con.CreateCommand()) {
388                 this.commandTimeout = c.CommandTimeout;
389             }
390
391             int maxUsersPerConnection = 1;
392             if (con.ConnectionString.IndexOf("MultipleActiveResultSets", StringComparison.OrdinalIgnoreCase) >= 0) {
393                 DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
394                 builder.ConnectionString = con.ConnectionString;
395                 if (string.Compare((string)builder["MultipleActiveResultSets"], "true", StringComparison.OrdinalIgnoreCase) == 0) {
396                     maxUsersPerConnection = 10;
397                 }
398             }
399
400             // If fileOrServerOrConnectionString != null, that means we just created the connection instance and we have to tell
401             // the SqlConnectionManager that it should dispose the connection when the context is disposed. Otherwise the user owns
402             // the connection and should dispose of it themselves.
403             this.conManager = new SqlConnectionManager(this, con, maxUsersPerConnection, fileOrServerOrConnectionString != null /*disposeConnection*/);
404             if (tx != null) {
405                 this.conManager.Transaction = tx;
406             }
407
408 #if DEBUG
409             SqlNode.Formatter = new SqlFormatter();
410 #endif
411
412 #if ILGEN
413             Type readerType;
414             if (this.mode == ProviderMode.SqlCE) {
415                 readerType = con.GetType().Module.GetType(SqlCeDataReaderTypeName);
416             }
417             else if (con is SqlConnection) {
418                 readerType = typeof(SqlDataReader);
419             }
420             else {
421                 readerType = typeof(DbDataReader);
422             }
423             this.readerCompiler = new ObjectReaderCompiler(readerType, this.services);
424 #else
425             this.readerCompiler = new ObjectReaderBuilder(this, this.services);
426 #endif
427         }
428
429         private static DbProviderFactory GetProvider(string providerName) {
430             bool hasProvider = 
431                 DbProviderFactories.GetFactoryClasses().Rows.OfType<DataRow>()
432                 .Select(r => (string)r["InvariantName"])
433                 .Contains(providerName, StringComparer.OrdinalIgnoreCase);
434             if (hasProvider) {
435                 return DbProviderFactories.GetFactory(providerName);
436             }
437             return null;
438         }
439
440         #region Dispose\Finalize
441         public void Dispose() {            
442             this.disposed = true;
443             Dispose(true);
444             // Technically, calling GC.SuppressFinalize is not required because the class does not
445             // have a finalizer, but it does no harm, protects against the case where a finalizer is added
446             // in the future, and prevents an FxCop warning.
447             GC.SuppressFinalize(this);
448         }
449
450         // Not implementing finalizer here because there are no unmanaged resources
451         // to release. See http://msdnwiki.microsoft.com/en-us/mtpswiki/12afb1ea-3a17-4a3f-a1f0-fcdb853e2359.aspx
452
453         // The bulk of the clean-up code is implemented in Dispose(bool)
454         protected virtual void Dispose(bool disposing) {
455             // Implemented but empty so that derived contexts can implement
456             // a finalizer that potentially cleans up unmanaged resources.
457             if (disposing) {
458                 this.services = null;
459                 if (this.conManager != null) {
460                     this.conManager.DisposeConnection();
461                 }
462                 this.conManager = null;
463                 this.typeProvider = null;
464                 this.sqlFactory = null;
465                 this.translator = null;
466                 this.readerCompiler = null;
467                 this.log = null;
468             }
469         }
470
471         internal void CheckDispose() {
472             if (this.disposed) {
473                 throw Error.ProviderCannotBeUsedAfterDispose();
474             }
475         }
476         #endregion
477
478         private string GetConnectionString(string fileOrServerOrConnectionString) {
479             if (fileOrServerOrConnectionString.IndexOf('=') >= 0) {
480                 return fileOrServerOrConnectionString;
481             }
482             else {
483                 DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
484                 if (fileOrServerOrConnectionString.EndsWith(".mdf", StringComparison.OrdinalIgnoreCase)) {
485                     // if just a database file is specified, default to local SqlExpress instance
486                     builder.Add("AttachDBFileName", fileOrServerOrConnectionString);
487                     builder.Add("Server", "localhost\\sqlexpress");
488                     builder.Add("Integrated Security", "SSPI");
489                     builder.Add("User Instance", "true");
490                     builder.Add("MultipleActiveResultSets", "true");
491                 }
492                 else if (fileOrServerOrConnectionString.EndsWith(".sdf", StringComparison.OrdinalIgnoreCase)) {
493                     // A SqlCE database file has been specified
494                     builder.Add("Data Source", fileOrServerOrConnectionString);
495                 }
496                 else {
497                     builder.Add("Server", fileOrServerOrConnectionString);
498                     builder.Add("Database", this.services.Model.DatabaseName);
499                     builder.Add("Integrated Security", "SSPI");
500                 }
501                 return builder.ToString();
502             }
503         }
504
505         private string GetDatabaseName(string constr) {
506             DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
507             builder.ConnectionString = constr;
508
509             if (builder.ContainsKey("Initial Catalog")) {
510                 return (string)builder["Initial Catalog"];
511             }
512             else if (builder.ContainsKey("Database")) {
513                 return (string)builder["Database"];
514             }
515             else if (builder.ContainsKey("AttachDBFileName")) {
516                 return (string)builder["AttachDBFileName"];
517             }
518             else if (builder.ContainsKey("Data Source") 
519                 && ((string)builder["Data Source"]).EndsWith(".sdf", StringComparison.OrdinalIgnoreCase)) {
520                 return (string)builder["Data Source"];
521             }
522             else {
523                 return this.services.Model.DatabaseName;
524             }
525         }
526
527         [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.")]
528         [ResourceExposure(ResourceScope.None)] // Exposure is via other methods that set dbName.
529         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // File.Exists method call.
530         void IProvider.CreateDatabase() {
531             this.CheckDispose();
532             this.CheckInitialized();
533             // Don't need to call CheckNotDeleted() here since we allow CreateDatabase after DeleteDatabase
534             // Don't need to call InitializeProviderMode() here since we don't need to know the provider to do this.
535             string catalog = null;
536             string filename = null;
537
538             DbConnectionStringBuilder builder = new DbConnectionStringBuilder();
539             builder.ConnectionString = this.conManager.Connection.ConnectionString;
540
541             if (this.conManager.Connection.State == ConnectionState.Closed) {
542                 if (this.mode == ProviderMode.SqlCE) {
543                     if (!File.Exists(this.dbName)) {
544                         Type engineType = this.conManager.Connection.GetType().Module.GetType("System.Data.SqlServerCe.SqlCeEngine");
545                         object engine = Activator.CreateInstance(engineType, new object[] { builder.ToString() });
546                         try {
547                             engineType.InvokeMember("CreateDatabase", BindingFlags.Instance | BindingFlags.Public | BindingFlags.InvokeMethod, null, engine, new object[] { }, CultureInfo.InvariantCulture);
548                         } 
549                         catch (TargetInvocationException tie) {
550                             throw tie.InnerException;
551                         } 
552                         finally {
553                             IDisposable disp = engine as IDisposable;
554                             if (disp != null) {
555                                 disp.Dispose();
556                             }
557                         }
558                     } 
559                     else {
560                         throw Error.CreateDatabaseFailedBecauseSqlCEDatabaseAlreadyExists(this.dbName);
561                     }
562                 } 
563                 else {
564                     // get connection string w/o reference to new catalog
565                     object val;
566                     if (builder.TryGetValue("Initial Catalog", out val)) {
567                         catalog = val.ToString();
568                         builder.Remove("Initial Catalog");
569                     }
570                     if (builder.TryGetValue("Database", out val)) {
571                         catalog = val.ToString();
572                         builder.Remove("Database");
573                     }
574                     if (builder.TryGetValue("AttachDBFileName", out val)) {
575                         filename = val.ToString();
576                         builder.Remove("AttachDBFileName");
577                     }
578                 }
579                 this.conManager.Connection.ConnectionString = builder.ToString();
580             }
581             else {
582                 if (this.mode == ProviderMode.SqlCE) {
583                     if (File.Exists(this.dbName)) {
584                         throw Error.CreateDatabaseFailedBecauseSqlCEDatabaseAlreadyExists(this.dbName);
585                     }
586                 }
587                 object val;
588                 if (builder.TryGetValue("Initial Catalog", out val)) {
589                     catalog = val.ToString();
590                 }
591                 if (builder.TryGetValue("Database", out val)) {
592                     catalog = val.ToString();
593                 }
594                 if (builder.TryGetValue("AttachDBFileName", out val)) {
595                     filename = val.ToString();
596                 }
597             }
598
599             if (String.IsNullOrEmpty(catalog)) {
600                 if (!String.IsNullOrEmpty(filename)) {
601                     catalog = Path.GetFullPath(filename);
602                 } 
603                 else if (!String.IsNullOrEmpty(this.dbName)) {
604                     catalog = this.dbName;
605                 } 
606                 else {
607                     throw Error.CouldNotDetermineCatalogName();
608                 }
609             }
610
611             this.conManager.UseConnection(this);
612             this.conManager.AutoClose = false;
613
614             try {
615                 if (this.services.Model.GetTables().FirstOrDefault() == null) {
616                     // we have no tables to create
617                     throw Error.CreateDatabaseFailedBecauseOfContextWithNoTables(this.services.Model.DatabaseName);
618                 }
619
620                 this.deleted = false;
621
622                 // create database
623                 if (this.mode == ProviderMode.SqlCE) {
624
625                     // create tables
626                     foreach (MetaTable table in this.services.Model.GetTables()) {
627                         string command = SqlBuilder.GetCreateTableCommand(table);
628                         if (!String.IsNullOrEmpty(command)) {
629                             this.ExecuteCommand(command);
630                         }
631                     }
632                     // create all foreign keys after all tables are defined
633                     foreach (MetaTable table in this.services.Model.GetTables()) {
634                         foreach (string command in SqlBuilder.GetCreateForeignKeyCommands(table)) {
635                             if (!String.IsNullOrEmpty(command)) {
636                                 this.ExecuteCommand(command);
637                             }
638                         }
639                     }
640                 }
641                 else {
642                     string createdb = SqlBuilder.GetCreateDatabaseCommand(catalog, filename, Path.ChangeExtension(filename, ".ldf"));
643                     this.ExecuteCommand(createdb);
644                     this.conManager.Connection.ChangeDatabase(catalog);
645
646                     // create the schemas that our tables will need
647                     // cannot be batched together with the rest of the CREATE TABLES
648                     if (this.mode == ProviderMode.Sql2005 || this.mode == ProviderMode.Sql2008) {
649                         HashSet<string> schemaCommands = new HashSet<string>();
650
651                         foreach (MetaTable table in this.services.Model.GetTables()) {
652                             string schemaCommand = SqlBuilder.GetCreateSchemaForTableCommand(table);
653                             if (!string.IsNullOrEmpty(schemaCommand)) {
654                                 schemaCommands.Add(schemaCommand);
655                             }
656                         }
657
658                         foreach (string schemaCommand in schemaCommands) {
659                             this.ExecuteCommand(schemaCommand);
660                         }
661                     }
662
663                     StringBuilder sb = new StringBuilder();
664
665                     // create tables
666                     foreach (MetaTable table in this.services.Model.GetTables()) {
667                         string createTable = SqlBuilder.GetCreateTableCommand(table);
668                         if (!string.IsNullOrEmpty(createTable)) {
669                             sb.AppendLine(createTable);
670                         }
671                     }
672
673                     // create all foreign keys after all tables are defined
674                     foreach (MetaTable table in this.services.Model.GetTables()) {
675                         foreach (string createFK in SqlBuilder.GetCreateForeignKeyCommands(table)) {
676                             if (!string.IsNullOrEmpty(createFK)) {
677                                 sb.AppendLine(createFK);
678                             }
679                         }
680                     }
681
682                     if (sb.Length > 0) {
683                         // must be on when creating indexes on computed columns
684                         sb.Insert(0, "SET ARITHABORT ON" + Environment.NewLine);
685                         this.ExecuteCommand(sb.ToString());
686                     }
687                 }
688             }
689             finally {
690                 this.conManager.ReleaseConnection(this);
691                 if (this.conManager.Connection is SqlConnection) {
692                     SqlConnection.ClearAllPools();
693                 }
694             }
695         }
696
697         [ResourceExposure(ResourceScope.None)] // Exposure is via other methods that set dbName.
698         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // File.Delete method call.
699         void IProvider.DeleteDatabase() {
700             this.CheckDispose();
701             this.CheckInitialized();
702             // Don't need to call InitializeProviderMode() here since we don't need to know the provider to do this.
703             if (this.deleted) {
704                 // 2nd delete is no-op.
705                 return;
706             }
707
708             if (this.mode == ProviderMode.SqlCE) {
709                 ((IProvider)this).ClearConnection();
710                 System.Diagnostics.Debug.Assert(this.conManager.Connection.State == ConnectionState.Closed);
711                 File.Delete(this.dbName);
712                 this.deleted = true;
713             }
714             else {
715                 string holdConnStr = conManager.Connection.ConnectionString;
716                 DbConnection con = this.conManager.UseConnection(this);
717                 try {
718                     con.ChangeDatabase("master");
719                     if (con is SqlConnection) {
720                         SqlConnection.ClearAllPools();
721                     }
722                     if (this.log != null) {
723                         this.log.WriteLine(Strings.LogAttemptingToDeleteDatabase(this.dbName));
724                     }
725                     this.ExecuteCommand(SqlBuilder.GetDropDatabaseCommand(this.dbName));
726                     this.deleted = true;
727                 }
728                 finally {
729                     this.conManager.ReleaseConnection(this);
730                     if (conManager.Connection.State == ConnectionState.Closed &&
731                         string.Compare(conManager.Connection.ConnectionString, holdConnStr, StringComparison.Ordinal) != 0) {
732                         // Credential information may have been stripped from the connection
733                         // string as a result of opening the connection. Restore the full
734                         // connection string.
735                         conManager.Connection.ConnectionString = holdConnStr;
736                     }
737                 }
738             }
739         }
740
741         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification="Microsoft: Code needs to return false regarless of exception.")]
742         [ResourceExposure(ResourceScope.None)] // Exposure is via other methods that set dbName.
743         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)] // File.Exists method call.
744         bool IProvider.DatabaseExists() {
745             this.CheckDispose();
746             this.CheckInitialized();
747             if (this.deleted) {
748                 return false;
749             }
750             // Don't need to call InitializeProviderMode() here since we don't need to know the provider to do this.
751
752             bool exists = false;
753             if (this.mode == ProviderMode.SqlCE) {
754                 exists = File.Exists(this.dbName);
755             }
756             else {
757                 string holdConnStr = conManager.Connection.ConnectionString;
758                 try {
759                     // If no database name is explicitly specified on the connection,
760                     // UseConnection will connect to 'Master', which is why after connecting
761                     // we call ChangeDatabase to verify that the database actually exists.
762                     this.conManager.UseConnection(this);
763                     this.conManager.Connection.ChangeDatabase(this.dbName);
764                     this.conManager.ReleaseConnection(this);
765                     exists = true;
766                 } catch (Exception) {
767                 } finally {
768                     if (conManager.Connection.State == ConnectionState.Closed &&
769                         string.Compare(conManager.Connection.ConnectionString, holdConnStr, StringComparison.Ordinal) != 0) {
770                         // Credential information may have been stripped from the connection
771                         // string as a result of opening the connection. Restore the full
772                         // connection string.
773                         conManager.Connection.ConnectionString = holdConnStr;
774                     }
775                 }
776             }
777             return exists;
778         }
779
780         void IConnectionUser.CompleteUse() {
781         }
782
783         void IProvider.ClearConnection() {
784             this.CheckDispose();
785             this.CheckInitialized();
786             this.conManager.ClearConnection();
787         }
788
789         private void ExecuteCommand(string command) {
790             if (this.log != null) {
791                 this.log.WriteLine(command);
792                 this.log.WriteLine();
793             }
794             IDbCommand cmd = this.conManager.Connection.CreateCommand();
795             cmd.CommandTimeout = this.commandTimeout;
796             cmd.Transaction = this.conManager.Transaction;
797             cmd.CommandText = command;
798             cmd.ExecuteNonQuery();
799         }
800
801         ICompiledQuery IProvider.Compile(Expression query) {
802             this.CheckDispose();
803             this.CheckInitialized();
804             if (query == null) {
805                 throw Error.ArgumentNull("query");
806             }
807             this.InitializeProviderMode();
808
809             SqlNodeAnnotations annotations = new SqlNodeAnnotations();
810             QueryInfo[] qis = this.BuildQuery(query, annotations);
811             CheckSqlCompatibility(qis, annotations);
812             
813             LambdaExpression lambda = query as LambdaExpression;
814             if (lambda != null) {
815                 query = lambda.Body;
816             }
817
818             IObjectReaderFactory factory = null;
819             ICompiledSubQuery[] subQueries = null;
820             QueryInfo qi = qis[qis.Length - 1];
821             if (qi.ResultShape == ResultShape.Singleton) {
822                 subQueries = this.CompileSubQueries(qi.Query);
823                 factory = this.GetReaderFactory(qi.Query, qi.ResultType);
824             }
825             else if (qi.ResultShape == ResultShape.Sequence) {
826                 subQueries = this.CompileSubQueries(qi.Query);
827                 factory = this.GetReaderFactory(qi.Query, TypeSystem.GetElementType(qi.ResultType));
828             }
829
830             return new CompiledQuery(this, query, qis, factory, subQueries);
831         }
832
833         private ICompiledSubQuery CompileSubQuery(SqlNode query, Type elementType, ReadOnlyCollection<Me.SqlParameter> parameters) {
834             query = SqlDuplicator.Copy(query);
835             SqlNodeAnnotations annotations = new SqlNodeAnnotations();
836
837             QueryInfo[] qis = this.BuildQuery(ResultShape.Sequence, TypeSystem.GetSequenceType(elementType), query, parameters, annotations);
838             System.Diagnostics.Debug.Assert(qis.Length == 1);
839             QueryInfo qi = qis[0];
840             ICompiledSubQuery[] subQueries = this.CompileSubQueries(qi.Query);
841             IObjectReaderFactory factory = this.GetReaderFactory(qi.Query, elementType);
842
843             CheckSqlCompatibility(qis, annotations);
844
845             return new CompiledSubQuery(qi, factory, parameters, subQueries);
846         }
847
848         IExecuteResult IProvider.Execute(Expression query) {
849             this.CheckDispose();
850             this.CheckInitialized();
851             this.CheckNotDeleted();
852             if (query == null) {
853                 throw Error.ArgumentNull("query");
854             }
855             this.InitializeProviderMode();
856
857 #if PERFORMANCE_BUILD
858             PerformanceCounter pcBuildQuery = null, bpcBuildQuery = null, pcExecQuery = null, bpcExecQuery = null,
859                    pcSession = null, bpcSession = null;
860             PerfTimer timerAll = null, timer = null;
861             if (this.CollectPerfInfo) {
862                 string s = System.Environment.GetEnvironmentVariable("EnableDLinqQueryPerf");
863                 collectQueryPerf = (s != null && s == "On");
864             }
865             if (collectQueryPerf) {
866                 pcBuildQuery = new PerformanceCounter("DLinq", "BuildQueryElapsedTime", false);
867                 bpcBuildQuery = new PerformanceCounter("DLinq", "BuildQueryElapsedTimeBase", false);
868                 pcExecQuery = new PerformanceCounter("DLinq", "ExecuteQueryElapsedTime", false);
869                 bpcExecQuery = new PerformanceCounter("DLinq", "ExecuteQueryElapsedTimeBase", false);
870                 pcSession = new PerformanceCounter("DLinq", "SessionExecuteQueryElapsedTime", false);
871                 bpcSession = new PerformanceCounter("DLinq", "SessionExecuteQueryElapsedTimeBase", false);
872                 timerAll = new PerfTimer();
873                 timer = new PerfTimer();
874                 timerAll.Start();
875             }
876 #endif
877             query = Funcletizer.Funcletize(query);
878
879             if (this.EnableCacheLookup) {
880                 IExecuteResult cached = this.GetCachedResult(query);
881                 if (cached != null) {
882                     return cached;
883                 }
884             }
885
886 #if PERFORMANCE_BUILD
887             if (collectQueryPerf) {
888                 timer.Start();
889             }
890 #endif
891             SqlNodeAnnotations annotations = new SqlNodeAnnotations();
892             QueryInfo[] qis = this.BuildQuery(query, annotations);
893             CheckSqlCompatibility(qis, annotations);
894
895             LambdaExpression lambda = query as LambdaExpression;
896             if (lambda != null) {
897                 query = lambda.Body;
898             }
899
900             IObjectReaderFactory factory = null;
901             ICompiledSubQuery[] subQueries = null;
902             QueryInfo qi = qis[qis.Length - 1];
903             if (qi.ResultShape == ResultShape.Singleton) {
904                 subQueries = this.CompileSubQueries(qi.Query);
905                 factory = this.GetReaderFactory(qi.Query, qi.ResultType);
906             }
907             else if (qi.ResultShape == ResultShape.Sequence) {
908                 subQueries = this.CompileSubQueries(qi.Query);
909                 factory = this.GetReaderFactory(qi.Query, TypeSystem.GetElementType(qi.ResultType));
910             }
911
912 #if PERFORMANCE_BUILD
913                 if (collectQueryPerf) {
914                     timer.Stop();
915                     pcBuildQuery.IncrementBy(timer.Duration);
916                     bpcBuildQuery.Increment();
917                 }
918 #endif
919
920 #if PERFORMANCE_BUILD
921             if (collectQueryPerf) {
922                 timer.Start();
923             }
924
925 #endif
926             IExecuteResult result = this.ExecuteAll(query, qis, factory, null, subQueries);
927
928 #if PERFORMANCE_BUILD
929             if (collectQueryPerf) {
930                 timer.Stop();
931                 pcSession.IncrementBy(timer.Duration);
932                 bpcSession.Increment();
933                 timerAll.Stop();
934                 pcExecQuery.IncrementBy(timerAll.Duration);
935                 bpcExecQuery.Increment();
936             }
937 #endif
938             return result;
939         }
940
941         private ICompiledSubQuery[] CompileSubQueries(SqlNode query) {
942             return new SubQueryCompiler(this).Compile(query);            
943         }
944
945         class SubQueryCompiler : SqlVisitor {
946             SqlProvider provider;
947             List<ICompiledSubQuery> subQueries;
948
949             internal SubQueryCompiler(SqlProvider provider) {
950                 this.provider = provider;
951             }
952
953             internal ICompiledSubQuery[] Compile(SqlNode node) {
954                 this.subQueries = new List<ICompiledSubQuery>();
955                 this.Visit(node);
956                 return this.subQueries.ToArray();
957             }
958
959             internal override SqlSelect VisitSelect(SqlSelect select) {
960                 this.Visit(select.Selection);
961                 return select;
962             }
963
964             internal override SqlExpression VisitSubSelect(SqlSubSelect ss) {
965                 return ss;
966             }
967
968             internal override SqlExpression VisitClientQuery(SqlClientQuery cq) {
969                 Type clientElementType = cq.Query.NodeType == SqlNodeType.Multiset ? TypeSystem.GetElementType(cq.ClrType) : cq.ClrType;
970                 ICompiledSubQuery c = this.provider.CompileSubQuery(cq.Query.Select, clientElementType, cq.Parameters.AsReadOnly());
971                 cq.Ordinal = this.subQueries.Count;
972                 this.subQueries.Add(c);
973                 return cq;
974             }
975         }
976
977         /// <summary>
978         /// Look for compatibility annotations for the set of providers we
979         /// add annotations for.
980         /// </summary>
981         private void CheckSqlCompatibility(QueryInfo[] queries, SqlNodeAnnotations annotations) {
982             if (this.Mode == ProviderMode.Sql2000 ||
983                 this.Mode == ProviderMode.SqlCE) {
984                 for (int i = 0, n = queries.Length; i < n; i++) {
985                     SqlServerCompatibilityCheck.ThrowIfUnsupported(queries[i].Query, annotations, this.Mode);
986                 }
987             }
988         }
989
990         private IExecuteResult ExecuteAll(Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, object[] userArguments, ICompiledSubQuery[] subQueries) {
991             IExecuteResult result = null;
992             object lastResult = null;
993             for (int i = 0, n = queryInfos.Length; i < n; i++) {
994                 if (i < n - 1) {
995                     result = this.Execute(query, queryInfos[i], null, null, userArguments, subQueries, lastResult);
996                 }
997                 else {
998                     result = this.Execute(query, queryInfos[i], factory, null, userArguments, subQueries, lastResult);
999                 }
1000                 if (queryInfos[i].ResultShape == ResultShape.Return) {
1001                     lastResult = result.ReturnValue;
1002                 }
1003             }
1004             return result;
1005         }
1006
1007         [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
1008         private IExecuteResult GetCachedResult(Expression query) {
1009             object obj = this.services.GetCachedObject(query);
1010             if (obj != null) {
1011                 switch (this.GetResultShape(query)) {
1012                     case ResultShape.Singleton:
1013                         return new ExecuteResult(null, null, null, obj);
1014                     case ResultShape.Sequence:
1015                         return new ExecuteResult(null, null, null,
1016                             Activator.CreateInstance(
1017                                 typeof(SequenceOfOne<>).MakeGenericType(TypeSystem.GetElementType(this.GetResultType(query))),
1018                                 BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { obj }, null
1019                                 ));
1020                 }
1021             }
1022             return null;
1023         }
1024
1025         [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling", Justification="Unknown reason.")]
1026         [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
1027         private IExecuteResult Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, object[] parentArgs, object[] userArgs, ICompiledSubQuery[] subQueries, object lastResult) {
1028             this.InitializeProviderMode();
1029
1030             DbConnection con = this.conManager.UseConnection(this);
1031             try {
1032                 DbCommand cmd = con.CreateCommand();
1033                 cmd.CommandText = queryInfo.CommandText;
1034                 cmd.Transaction = this.conManager.Transaction;
1035                 cmd.CommandTimeout = this.commandTimeout;
1036                 AssignParameters(cmd, queryInfo.Parameters, userArgs, lastResult);
1037                 LogCommand(this.log, cmd);
1038                 this.queryCount += 1;
1039
1040                 switch (queryInfo.ResultShape) {
1041                     default:
1042                     case ResultShape.Return: {
1043                             return new ExecuteResult(cmd, queryInfo.Parameters, null, cmd.ExecuteNonQuery(), true);
1044                         }
1045                     case ResultShape.Singleton: {
1046                             DbDataReader reader = cmd.ExecuteReader();                            
1047                             IObjectReader objReader = factory.Create(reader, true, this, parentArgs, userArgs, subQueries);                        
1048                             this.conManager.UseConnection(objReader.Session);
1049                             try {
1050                                 IEnumerable sequence = (IEnumerable)Activator.CreateInstance(
1051                                     typeof(OneTimeEnumerable<>).MakeGenericType(queryInfo.ResultType),
1052                                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
1053                                     new object[] { objReader }, null
1054                                     );
1055                                 object value = null;
1056                                 MethodCallExpression mce = query as MethodCallExpression;
1057                                 MethodInfo sequenceMethod = null;
1058                                 if (mce != null && (
1059                                     mce.Method.DeclaringType == typeof(Queryable) ||
1060                                     mce.Method.DeclaringType == typeof(Enumerable))
1061                                     ) {
1062                                     switch (mce.Method.Name) {
1063                                         case "First":
1064                                         case "FirstOrDefault":
1065                                         case "SingleOrDefault":
1066                                             sequenceMethod = TypeSystem.FindSequenceMethod(mce.Method.Name, sequence);
1067                                             break;
1068                                         case "Single":
1069                                         default:
1070                                             sequenceMethod = TypeSystem.FindSequenceMethod("Single", sequence);
1071                                             break;
1072                                     }
1073                                 }
1074                                 else {
1075                                     sequenceMethod = TypeSystem.FindSequenceMethod("SingleOrDefault", sequence);
1076                                 }
1077
1078                                 // When dynamically invoking the sequence method, we want to
1079                                 // return the inner exception if the invocation fails
1080                                 if (sequenceMethod != null) {
1081                                     try {
1082                                         value = sequenceMethod.Invoke(null, new object[] { sequence });
1083                                     }
1084                                     catch (TargetInvocationException tie) {
1085                                         if (tie.InnerException != null) {
1086                                             throw tie.InnerException;
1087                                         }
1088                                         throw;
1089                                     }
1090                                 }
1091
1092                                 return new ExecuteResult(cmd, queryInfo.Parameters, objReader.Session, value);
1093                             }
1094                             finally {
1095                                 objReader.Dispose();
1096                             }
1097                         }
1098                     case ResultShape.Sequence: {
1099                             DbDataReader reader = cmd.ExecuteReader();
1100                             IObjectReader objReader = factory.Create(reader, true, this, parentArgs, userArgs, subQueries);
1101                             this.conManager.UseConnection(objReader.Session);
1102                             IEnumerable sequence = (IEnumerable)Activator.CreateInstance(
1103                                 typeof(OneTimeEnumerable<>).MakeGenericType(TypeSystem.GetElementType(queryInfo.ResultType)),
1104                                 BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
1105                                 new object[] { objReader }, null
1106                                 );
1107                             if (typeof(IQueryable).IsAssignableFrom(queryInfo.ResultType)) {
1108                                 sequence = sequence.AsQueryable();
1109                             }
1110                             ExecuteResult result = new ExecuteResult(cmd, queryInfo.Parameters, objReader.Session);
1111                             MetaFunction function = this.GetFunction(query);
1112                             if (function != null && !function.IsComposable) {
1113                                 sequence = (IEnumerable)Activator.CreateInstance(
1114                                 typeof(SingleResult<>).MakeGenericType(TypeSystem.GetElementType(queryInfo.ResultType)),
1115                                 BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null,
1116                                 new object[] { sequence, result, this.services.Context }, null
1117                                 );
1118                             }
1119                             result.ReturnValue = sequence;
1120                             return result;
1121                         }
1122                     case ResultShape.MultipleResults: {
1123                             DbDataReader reader = cmd.ExecuteReader();
1124                             IObjectReaderSession session = this.readerCompiler.CreateSession(reader, this, parentArgs, userArgs, subQueries);
1125                             this.conManager.UseConnection(session);
1126                             MetaFunction function = this.GetFunction(query);
1127                             ExecuteResult result = new ExecuteResult(cmd, queryInfo.Parameters, session);
1128                             result.ReturnValue = new MultipleResults(this, function, session, result);
1129                             return result;
1130                         }
1131                 }
1132             }
1133             finally {
1134                 this.conManager.ReleaseConnection(this);
1135             }
1136         }
1137
1138         private MetaFunction GetFunction(Expression query) {
1139             LambdaExpression lambda = query as LambdaExpression;
1140             if (lambda != null) {
1141                 query = lambda.Body;
1142             }
1143             MethodCallExpression mc = query as MethodCallExpression;
1144             if (mc != null && typeof(DataContext).IsAssignableFrom(mc.Method.DeclaringType)) {
1145                 return this.services.Model.GetFunction(mc.Method);
1146             }
1147             return null;
1148         }
1149
1150         private void LogCommand(TextWriter writer, DbCommand cmd) {
1151             if (writer != null) {
1152                 writer.WriteLine(cmd.CommandText);
1153                 foreach (DbParameter p in cmd.Parameters) {
1154                     int prec = 0;
1155                     int scale = 0;
1156                     PropertyInfo piPrecision = p.GetType().GetProperty("Precision");
1157                     if (piPrecision != null) {
1158                         prec = (int)Convert.ChangeType(piPrecision.GetValue(p, null), typeof(int), CultureInfo.InvariantCulture);
1159                     }
1160                     PropertyInfo piScale = p.GetType().GetProperty("Scale");
1161                     if (piScale != null) {
1162                         scale = (int)Convert.ChangeType(piScale.GetValue(p, null), typeof(int), CultureInfo.InvariantCulture);
1163                     }                
1164                     var sp = p as System.Data.SqlClient.SqlParameter;
1165                     writer.WriteLine("-- {0}: {1} {2} (Size = {3}; Prec = {4}; Scale = {5}) [{6}]", 
1166                         p.ParameterName, 
1167                         p.Direction, 
1168                         sp == null ? p.DbType.ToString() : sp.SqlDbType.ToString(),
1169                         p.Size.ToString(System.Globalization.CultureInfo.CurrentCulture), 
1170                         prec, 
1171                         scale, 
1172                         sp == null ? p.Value : sp.SqlValue);
1173                 }
1174                 writer.WriteLine("-- Context: {0}({1}) Model: {2} Build: {3}", this.GetType().Name, this.Mode, this.services.Model.GetType().Name, ThisAssembly.InformationalVersion);
1175                 writer.WriteLine();
1176             }
1177         }
1178
1179         private void AssignParameters(DbCommand cmd, ReadOnlyCollection<SqlParameterInfo> parms, object[] userArguments, object lastResult) {
1180             if (parms != null) {
1181                 foreach (SqlParameterInfo pi in parms) {
1182                     DbParameter p = cmd.CreateParameter();
1183                     p.ParameterName = pi.Parameter.Name;
1184                     p.Direction = pi.Parameter.Direction;
1185                     if (pi.Parameter.Direction == ParameterDirection.Input ||
1186                         pi.Parameter.Direction == ParameterDirection.InputOutput) {
1187                         object value = pi.Value;
1188                         switch (pi.Type) {
1189                             case SqlParameterType.UserArgument:
1190                                 try {
1191                                     value = pi.Accessor.DynamicInvoke(new object[] { userArguments });
1192                                 } catch (System.Reflection.TargetInvocationException e) {
1193                                     throw e.InnerException;
1194                                 }
1195                                 break;
1196                             case SqlParameterType.PreviousResult:
1197                                 value = lastResult;
1198                                 break;
1199                         }
1200                         this.typeProvider.InitializeParameter(pi.Parameter.SqlType, p, value);
1201                     }
1202                     else {
1203                         this.typeProvider.InitializeParameter(pi.Parameter.SqlType, p, null);
1204                     }
1205                     cmd.Parameters.Add(p);
1206                 }
1207             }
1208         }
1209
1210         [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
1211         IEnumerable IProvider.Translate(Type elementType, DbDataReader reader) {
1212             this.CheckDispose();
1213             this.CheckInitialized();
1214             this.InitializeProviderMode();
1215             if (elementType == null) {
1216                 throw Error.ArgumentNull("elementType");
1217             }
1218             if (reader == null) {
1219                 throw Error.ArgumentNull("reader");
1220             }
1221             MetaType rowType = services.Model.GetMetaType(elementType);
1222             IObjectReaderFactory factory = this.GetDefaultFactory(rowType);
1223             IEnumerator e = factory.Create(reader, true, this, null, null, null);
1224             Type enumerableType = typeof(OneTimeEnumerable<>).MakeGenericType(elementType);
1225             return (IEnumerable)Activator.CreateInstance(enumerableType, BindingFlags.Instance | BindingFlags.NonPublic, null, new object[] { e }, null);
1226         }
1227
1228         IMultipleResults IProvider.Translate(DbDataReader reader) {
1229             this.CheckDispose();
1230             this.CheckInitialized();
1231             this.InitializeProviderMode();
1232             if (reader == null) {
1233                 throw Error.ArgumentNull("reader");
1234             }
1235             IObjectReaderSession session = this.readerCompiler.CreateSession(reader, this, null, null, null);
1236             return new MultipleResults(this, null, session, null);
1237         }
1238
1239          string IProvider.GetQueryText(Expression query) {
1240             this.CheckDispose();
1241             this.CheckInitialized();
1242             if (query == null) {
1243                 throw Error.ArgumentNull("query");
1244             }
1245             this.InitializeProviderMode();
1246             SqlNodeAnnotations annotations = new SqlNodeAnnotations();
1247             QueryInfo[] qis = this.BuildQuery(query, annotations);
1248
1249             StringBuilder sb = new StringBuilder();
1250             for (int i = 0, n = qis.Length; i < n; i++) {
1251                 QueryInfo qi = qis[i];
1252 #if DEBUG
1253                 StringWriter writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture);
1254                 DbCommand cmd = this.conManager.Connection.CreateCommand();
1255                 cmd.CommandText = qi.CommandText;
1256                 AssignParameters(cmd, qi.Parameters, null, null);
1257                 LogCommand(writer, cmd);
1258                 sb.Append(writer.ToString());
1259 #else
1260                 sb.Append(qi.CommandText);
1261                 sb.AppendLine();
1262 #endif
1263             }
1264             return sb.ToString();
1265         }
1266
1267         DbCommand IProvider.GetCommand(Expression query) {
1268             this.CheckDispose();
1269             this.CheckInitialized();
1270             if (query == null) {
1271                 throw Error.ArgumentNull("query");
1272             }
1273             this.InitializeProviderMode();
1274             SqlNodeAnnotations annotations = new SqlNodeAnnotations();
1275             QueryInfo[] qis = this.BuildQuery(query, annotations);
1276             QueryInfo qi = qis[qis.Length - 1];
1277             DbCommand cmd = this.conManager.Connection.CreateCommand();
1278             cmd.CommandText = qi.CommandText;
1279             cmd.Transaction = this.conManager.Transaction;
1280             cmd.CommandTimeout = this.commandTimeout;
1281             AssignParameters(cmd, qi.Parameters, null, null);
1282             return cmd;
1283         }
1284
1285         internal class QueryInfo {
1286             SqlNode query;
1287             string commandText;
1288             ReadOnlyCollection<SqlParameterInfo> parameters;
1289             ResultShape resultShape;
1290             Type resultType;
1291
1292             internal QueryInfo(SqlNode query, string commandText, ReadOnlyCollection<SqlParameterInfo> parameters, ResultShape resultShape, Type resultType) {
1293                 this.query = query;
1294                 this.commandText = commandText;
1295                 this.parameters = parameters;
1296                 this.resultShape = resultShape;
1297                 this.resultType = resultType;
1298             }
1299             internal SqlNode Query {
1300                 get { return this.query; }
1301             }
1302             internal string CommandText {
1303                 get { return this.commandText; }
1304             }
1305             internal ReadOnlyCollection<SqlParameterInfo> Parameters {
1306                 get { return this.parameters; }
1307             }
1308             internal ResultShape ResultShape {
1309                 get { return this.resultShape; }
1310             }
1311             internal Type ResultType {
1312                 get { return this.resultType; }
1313             }
1314         }
1315
1316         internal enum ResultShape {
1317             Return,
1318             Singleton,
1319             Sequence,
1320             MultipleResults
1321         }
1322
1323         private ResultShape GetResultShape(Expression query) {
1324             LambdaExpression lambda = query as LambdaExpression;
1325             if (lambda != null) {
1326                 query = lambda.Body;
1327             }
1328
1329             if (query.Type == typeof(void)) {
1330                 return ResultShape.Return;
1331             }
1332             else if (query.Type == typeof(IMultipleResults)) {
1333                 return ResultShape.MultipleResults;
1334             }
1335
1336             bool isSequence = typeof(IEnumerable).IsAssignableFrom(query.Type);
1337             ProviderType pt = this.typeProvider.From(query.Type);
1338             bool isScalar = !pt.IsRuntimeOnlyType && !pt.IsApplicationType;
1339             bool isSingleton = isScalar || !isSequence;
1340
1341             MethodCallExpression mce = query as MethodCallExpression;
1342             if (mce != null) {
1343                 // query operators
1344                 if (mce.Method.DeclaringType == typeof(Queryable) ||
1345                     mce.Method.DeclaringType == typeof(Enumerable)) {
1346                     switch (mce.Method.Name) {
1347                         // methods known to produce singletons
1348                         case "First":
1349                         case "FirstOrDefault":
1350                         case "Single":
1351                         case "SingleOrDefault":
1352                             isSingleton = true;
1353                             break;
1354                     }
1355                 }
1356                 else if (mce.Method.DeclaringType == typeof(DataContext)) {
1357                     if (mce.Method.Name == "ExecuteCommand") {
1358                         return ResultShape.Return;
1359                     }
1360                 }
1361                 else if (mce.Method.DeclaringType.IsSubclassOf(typeof(DataContext))) {
1362                     MetaFunction f = this.GetFunction(query);
1363                     if (f != null) {
1364                         if (!f.IsComposable) {
1365                             isSingleton = false;
1366                         }
1367                         else if (isScalar) {
1368                             isSingleton = true;
1369                         }
1370                     }
1371                 }
1372                 else if (mce.Method.DeclaringType == typeof(DataManipulation) && mce.Method.ReturnType == typeof(int)) {
1373                     return ResultShape.Return;
1374                 }
1375             }
1376
1377             if (isSingleton) {
1378                 return ResultShape.Singleton;
1379             }
1380             else if (isScalar) {
1381                 return ResultShape.Return;
1382             }
1383             else {
1384                 return ResultShape.Sequence;
1385             }
1386         }
1387
1388         [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification="Unknown reason.")]
1389         private Type GetResultType(Expression query) {
1390             LambdaExpression lambda = query as LambdaExpression;
1391             if (lambda != null) {
1392                 query = lambda.Body;
1393             }
1394             return query.Type;
1395         }
1396
1397         internal QueryInfo[] BuildQuery(Expression query, SqlNodeAnnotations annotations) {
1398             this.CheckDispose();
1399
1400             // apply maximal funcletization
1401             query = Funcletizer.Funcletize(query);
1402
1403             // convert query nodes into sql nodes
1404             QueryConverter converter = new QueryConverter(this.services, this.typeProvider, this.translator, this.sqlFactory);
1405             switch (this.Mode) {
1406                 case ProviderMode.Sql2000:
1407                     converter.ConverterStrategy =
1408                         ConverterStrategy.CanUseScopeIdentity |
1409                         ConverterStrategy.CanUseJoinOn |
1410                         ConverterStrategy.CanUseRowStatus;
1411                     break;
1412                 case ProviderMode.Sql2005:
1413                 case ProviderMode.Sql2008:
1414                     converter.ConverterStrategy =
1415                         ConverterStrategy.CanUseScopeIdentity |
1416                         ConverterStrategy.SkipWithRowNumber |
1417                         ConverterStrategy.CanUseRowStatus |
1418                         ConverterStrategy.CanUseJoinOn |
1419                         ConverterStrategy.CanUseOuterApply |
1420                         ConverterStrategy.CanOutputFromInsert;
1421                     break;
1422                 case ProviderMode.SqlCE:
1423                     converter.ConverterStrategy = ConverterStrategy.CanUseOuterApply;
1424                     // Can't set ConverterStrategy.CanUseJoinOn because scalar subqueries in the ON clause
1425                     // can't be converted into anything.
1426                     break;
1427             }
1428             SqlNode node = converter.ConvertOuter(query);
1429
1430             return this.BuildQuery(this.GetResultShape(query), this.GetResultType(query), node, null, annotations);
1431         }
1432
1433         [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.")]
1434         private QueryInfo[] BuildQuery(ResultShape resultShape, Type resultType, SqlNode node, ReadOnlyCollection<Me.SqlParameter> parentParameters, SqlNodeAnnotations annotations) {
1435             System.Diagnostics.Debug.Assert(resultType != null);
1436             System.Diagnostics.Debug.Assert(node != null);
1437
1438             SqlSupersetValidator validator = new SqlSupersetValidator();
1439
1440             // These are the rules that apply to every SQL tree.
1441             if (this.checkQueries) {
1442                 validator.AddValidator(new ColumnTypeValidator()); /* Column CLR Type must agree with its Expressions CLR Type */
1443                 validator.AddValidator(new LiteralValidator()); /* Constrain literal Types */
1444             }
1445
1446             validator.Validate(node);
1447
1448             SqlColumnizer columnizer = new SqlColumnizer();
1449
1450             // resolve member references
1451             bool canUseOuterApply = (this.Mode == ProviderMode.Sql2005 || this.Mode == ProviderMode.Sql2008 || this.Mode == ProviderMode.SqlCE);
1452             SqlBinder binder = new SqlBinder(this.translator, this.sqlFactory, this.services.Model, this.services.Context.LoadOptions, columnizer, canUseOuterApply);
1453             binder.OptimizeLinkExpansions = (optimizationFlags & OptimizationFlags.OptimizeLinkExpansions) != 0;
1454             binder.SimplifyCaseStatements = (optimizationFlags & OptimizationFlags.SimplifyCaseStatements) != 0;
1455             binder.PreBinder = delegate(SqlNode n) {
1456                 // convert methods into known reversable operators
1457                 return PreBindDotNetConverter.Convert(n, this.sqlFactory, this.services.Model);
1458             };
1459             node = binder.Bind(node);
1460             if (this.checkQueries) {
1461                 validator.AddValidator(new ExpectNoAliasRefs());
1462                 validator.AddValidator(new ExpectNoSharedExpressions());
1463             }
1464             validator.Validate(node);
1465
1466             node = PostBindDotNetConverter.Convert(node, this.sqlFactory, this.Mode);
1467
1468             // identify true flow of sql data types 
1469             SqlRetyper retyper = new SqlRetyper(this.typeProvider, this.services.Model);
1470             node = retyper.Retype(node);
1471             validator.Validate(node);
1472
1473             // change CONVERT to special conversions like UNICODE,CHAR,...
1474             SqlTypeConverter converter = new SqlTypeConverter(this.sqlFactory);
1475             node = converter.Visit(node);
1476             validator.Validate(node);
1477
1478             // transform type-sensitive methods such as LEN (to DATALENGTH), ...
1479             SqlMethodTransformer methodTransformer = new SqlMethodTransformer(this.sqlFactory);
1480             node = methodTransformer.Visit(node);
1481             validator.Validate(node);
1482
1483             // convert multisets into separate queries
1484             SqlMultiplexer.Options options = (this.Mode == ProviderMode.Sql2008 || 
1485                                               this.Mode == ProviderMode.Sql2005 ||
1486                                               this.Mode == ProviderMode.SqlCE) 
1487                 ? SqlMultiplexer.Options.EnableBigJoin : SqlMultiplexer.Options.None;
1488             SqlMultiplexer mux = new SqlMultiplexer(options, parentParameters, this.sqlFactory);
1489             node = mux.Multiplex(node);
1490             validator.Validate(node);
1491
1492             // convert object construction expressions into flat row projections
1493             SqlFlattener flattener = new SqlFlattener(this.sqlFactory, columnizer);
1494             node = flattener.Flatten(node);
1495             validator.Validate(node);
1496
1497             if (this.mode == ProviderMode.SqlCE) {
1498                 SqlRewriteScalarSubqueries rss = new SqlRewriteScalarSubqueries(this.sqlFactory);
1499                 node = rss.Rewrite(node);
1500             }
1501
1502             // Simplify case statements where all alternatives map to the same thing.
1503             // Doing this before deflator because the simplified results may lead to
1504             // more deflation opportunities.
1505             // Doing this before booleanizer because it may convert CASE statements (non-predicates) into
1506             // predicate expressions.
1507             // Doing this before reorderer because it may reduce some orders to constant nodes which should not
1508             // be passed onto ROW_NUMBER.
1509             node = SqlCaseSimplifier.Simplify(node, this.sqlFactory);
1510
1511             // Rewrite order-by clauses so that they only occur at the top-most select 
1512             // or in selects with TOP
1513             SqlReorderer reorderer = new SqlReorderer(this.typeProvider, this.sqlFactory);
1514             node = reorderer.Reorder(node);
1515             validator.Validate(node);
1516
1517             // Inject code to turn predicates into bits, and bits into predicates where necessary
1518             node = SqlBooleanizer.Rationalize(node, this.typeProvider, this.services.Model);
1519             if (this.checkQueries) {
1520                 validator.AddValidator(new ExpectRationalizedBooleans()); /* From now on all boolean expressions should remain rationalized. */
1521             }
1522             validator.Validate(node);
1523
1524             if (this.checkQueries) {
1525                 validator.AddValidator(new ExpectNoFloatingColumns());
1526             }
1527
1528             // turning predicates into bits/ints can change Sql types, propagate changes            
1529             node = retyper.Retype(node);
1530             validator.Validate(node);
1531
1532             // assign aliases to columns
1533             // we need to do this now so that the sql2k lifters will work
1534             SqlAliaser aliaser = new SqlAliaser();
1535             node = aliaser.AssociateColumnsWithAliases(node);
1536             validator.Validate(node);
1537
1538             // SQL2K enablers.
1539             node = SqlLiftWhereClauses.Lift(node, this.typeProvider, this.services.Model);
1540             node = SqlLiftIndependentRowExpressions.Lift(node);
1541             node = SqlOuterApplyReducer.Reduce(node, this.sqlFactory, annotations);
1542             node = SqlTopReducer.Reduce(node, annotations, this.sqlFactory);
1543
1544             // resolve references to columns in other scopes by adding them
1545             // to the intermediate selects
1546             SqlResolver resolver = new SqlResolver();
1547             node = resolver.Resolve(node);
1548             validator.Validate(node);
1549
1550             // re-assign aliases after resolving (new columns may have been added)
1551             node = aliaser.AssociateColumnsWithAliases(node);
1552             validator.Validate(node);
1553
1554             // fixup union projections
1555             node = SqlUnionizer.Unionize(node);
1556
1557             // remove order-by of literals
1558             node = SqlRemoveConstantOrderBy.Remove(node);
1559
1560             // throw out unused columns and redundant sub-queries...
1561             SqlDeflator deflator = new SqlDeflator();
1562             node = deflator.Deflate(node);
1563             validator.Validate(node);
1564
1565             // Positioning after deflator because it may remove unnecessary columns
1566             // from SELECT projection lists and allow more CROSS APPLYs to be reduced
1567             // to CROSS JOINs.
1568             node = SqlCrossApplyToCrossJoin.Reduce(node, annotations);
1569
1570             // fixup names for aliases, columns, locals, etc..
1571             SqlNamer namer = new SqlNamer();
1572             node = namer.AssignNames(node);
1573             validator.Validate(node);
1574
1575             // Convert [N]Text,Image to [N]VarChar(MAX),VarBinary(MAX) where necessary.
1576             // These new types do not exist on SQL2k, so add annotations.
1577             LongTypeConverter longTypeConverter = new LongTypeConverter(this.sqlFactory);
1578             node = longTypeConverter.AddConversions(node, annotations);
1579    
1580             // final validation            
1581             validator.AddValidator(new ExpectNoMethodCalls());
1582             validator.AddValidator(new ValidateNoInvalidComparison());
1583             validator.Validate(node);
1584
1585             SqlParameterizer parameterizer = new SqlParameterizer(this.typeProvider, annotations);
1586             SqlFormatter formatter = new SqlFormatter();
1587             if (this.mode == ProviderMode.SqlCE ||
1588                 this.mode == ProviderMode.Sql2005 ||
1589                 this.mode == ProviderMode.Sql2008) {
1590                 formatter.ParenthesizeTop = true;
1591             }
1592
1593             SqlBlock block = node as SqlBlock;
1594             if (block != null && this.mode == ProviderMode.SqlCE) {
1595                 // SQLCE cannot batch multiple statements.
1596                 ReadOnlyCollection<ReadOnlyCollection<SqlParameterInfo>> parameters = parameterizer.ParameterizeBlock(block);
1597                 string[] commands = formatter.FormatBlock(block, false);
1598                 QueryInfo[] queries = new QueryInfo[commands.Length];
1599                 for (int i = 0, n = commands.Length; i < n; i++) {
1600                     queries[i] = new QueryInfo(
1601                         block.Statements[i],
1602                         commands[i],
1603                         parameters[i],
1604                         (i < n - 1) ? ResultShape.Return : resultShape,
1605                         (i < n - 1) ? typeof(int) : resultType
1606                         );
1607                 }
1608                 return queries;
1609             }
1610             else {
1611                 // build only one result
1612                 ReadOnlyCollection<SqlParameterInfo> parameters = parameterizer.Parameterize(node);
1613                 string commandText = formatter.Format(node);
1614                 return new QueryInfo[] {
1615                     new QueryInfo(node, commandText, parameters, resultShape, resultType)
1616                     };
1617             }
1618         }
1619
1620         private SqlSelect GetFinalSelect(SqlNode node) {
1621             switch (node.NodeType) {
1622                 case SqlNodeType.Select:
1623                     return (SqlSelect)node;
1624                 case SqlNodeType.Block: {
1625                         SqlBlock b = (SqlBlock)node;
1626                         return GetFinalSelect(b.Statements[b.Statements.Count - 1]);
1627                     }
1628             }
1629             return null;
1630         }
1631
1632         private IObjectReaderFactory GetReaderFactory(SqlNode node, Type elemType) {
1633             SqlSelect sel = node as SqlSelect;
1634             SqlExpression projection = null;
1635             if (sel == null && node.NodeType == SqlNodeType.Block) {
1636                 sel = this.GetFinalSelect(node);
1637             }
1638             if (sel != null) {
1639                 projection = sel.Selection;
1640             }
1641             else {
1642                 SqlUserQuery suq = node as SqlUserQuery;
1643                 if (suq != null && suq.Projection != null) {
1644                     projection = suq.Projection;
1645                 }
1646             }
1647             IObjectReaderFactory factory;
1648             if (projection != null) {
1649                 factory = this.readerCompiler.Compile(projection, elemType);
1650             }
1651             else {
1652                 return this.GetDefaultFactory(services.Model.GetMetaType(elemType));
1653             }
1654             return factory;
1655         }
1656
1657         private IObjectReaderFactory GetDefaultFactory(MetaType rowType) {
1658             if (rowType == null) {
1659                 throw Error.ArgumentNull("rowType");
1660             }
1661             SqlNodeAnnotations annotations = new SqlNodeAnnotations();
1662             Expression tmp = Expression.Constant(null);
1663             SqlUserQuery suq = new SqlUserQuery(string.Empty, null, null, tmp);
1664             if (TypeSystem.IsSimpleType(rowType.Type)) {
1665                 // if the element type is a simple type (int, bool, etc.) we create
1666                 // a single column binding
1667                 SqlUserColumn col = new SqlUserColumn(rowType.Type, typeProvider.From(rowType.Type), suq, "", false, suq.SourceExpression);
1668                 suq.Columns.Add(col);
1669                 suq.Projection = col;
1670             }
1671             else {
1672                 // ... otherwise we generate a default projection
1673                 SqlUserRow rowExp = new SqlUserRow(rowType.InheritanceRoot, this.typeProvider.GetApplicationType((int)ConverterSpecialTypes.Row), suq, tmp);
1674                 suq.Projection = this.translator.BuildProjection(rowExp, rowType, true, null, tmp);
1675             }
1676             Type resultType = TypeSystem.GetSequenceType(rowType.Type);
1677             QueryInfo[] qis = this.BuildQuery(ResultShape.Sequence, resultType, suq, null, annotations);
1678             return this.GetReaderFactory(qis[qis.Length - 1].Query, rowType.Type);
1679         }
1680
1681         class CompiledQuery : ICompiledQuery {
1682             DataLoadOptions originalShape;
1683             Expression query;
1684             QueryInfo[] queryInfos;
1685             IObjectReaderFactory factory;
1686             ICompiledSubQuery[] subQueries;
1687
1688             internal CompiledQuery(SqlProvider provider, Expression query, QueryInfo[] queryInfos, IObjectReaderFactory factory, ICompiledSubQuery[] subQueries) {
1689                 this.originalShape = provider.services.Context.LoadOptions;
1690                 this.query = query;
1691                 this.queryInfos = queryInfos;
1692                 this.factory = factory;
1693                 this.subQueries = subQueries;
1694             }
1695
1696             public IExecuteResult Execute(IProvider provider, object[] arguments) {
1697                 if (provider == null) {
1698                     throw Error.ArgumentNull("provider");
1699                 }
1700
1701                 SqlProvider sqlProvider = provider as SqlProvider;
1702                 if (sqlProvider == null) {
1703                     throw Error.ArgumentTypeMismatch("provider");
1704                 }
1705
1706                 // verify shape is compatibile with original.
1707                 if (!AreEquivalentShapes(this.originalShape, sqlProvider.services.Context.LoadOptions)) {
1708                     throw Error.CompiledQueryAgainstMultipleShapesNotSupported();
1709                 }
1710
1711                 // execute query (only last query produces results)
1712                 return sqlProvider.ExecuteAll(this.query, this.queryInfos, this.factory, arguments, subQueries);
1713             }
1714
1715             private static bool AreEquivalentShapes(DataLoadOptions shape1, DataLoadOptions shape2) {
1716                 if (shape1 == shape2) {
1717                     return true;
1718                 }
1719                 else if (shape1 == null) {
1720                     return shape2.IsEmpty;
1721                 }
1722                 else if (shape2 == null) {
1723                     return shape1.IsEmpty;
1724                 }
1725                 else if (shape1.IsEmpty && shape2.IsEmpty) {
1726                     return true;
1727                 }
1728                 return false;
1729             }
1730         }
1731
1732         class CompiledSubQuery : ICompiledSubQuery {
1733             QueryInfo queryInfo;
1734             IObjectReaderFactory factory;
1735             ReadOnlyCollection<Me.SqlParameter> parameters;
1736             ICompiledSubQuery[] subQueries;
1737
1738             internal CompiledSubQuery(QueryInfo queryInfo, IObjectReaderFactory factory, ReadOnlyCollection<Me.SqlParameter> parameters, ICompiledSubQuery[] subQueries) {
1739                 this.queryInfo = queryInfo;
1740                 this.factory = factory;
1741                 this.parameters = parameters;
1742                 this.subQueries = subQueries;
1743             }
1744
1745             public IExecuteResult Execute(IProvider provider, object[] parentArgs, object[] userArgs) {
1746                 if (parentArgs == null && !(this.parameters == null || this.parameters.Count == 0)) {
1747                     throw Error.ArgumentNull("arguments");
1748                 }
1749
1750                 SqlProvider sqlProvider = provider as SqlProvider;
1751                 if (sqlProvider == null) {
1752                     throw Error.ArgumentTypeMismatch("provider");
1753                 }
1754
1755                 // construct new copy of query info
1756                 List<SqlParameterInfo> spis = new List<SqlParameterInfo>(this.queryInfo.Parameters);
1757
1758                 // add call arguments
1759                 for (int i = 0, n = this.parameters.Count; i < n; i++) {
1760                     spis.Add(new SqlParameterInfo(this.parameters[i], parentArgs[i]));
1761                 }
1762
1763                 QueryInfo qi = new QueryInfo(
1764                     this.queryInfo.Query,
1765                     this.queryInfo.CommandText,
1766                     spis.AsReadOnly(),
1767                     this.queryInfo.ResultShape,
1768                     this.queryInfo.ResultType
1769                     );
1770
1771                 // execute query
1772                 return sqlProvider.Execute(null, qi, this.factory, parentArgs, userArgs, subQueries, null);
1773             }
1774         }
1775
1776         class ExecuteResult : IExecuteResult, IDisposable {
1777             DbCommand command;
1778             ReadOnlyCollection<SqlParameterInfo> parameters;
1779             IObjectReaderSession session;
1780             int iReturnParameter = -1;
1781             object value;
1782             [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Microsoft: used in an assert in ReturnValue.set")]
1783             bool useReturnValue;
1784             bool isDisposed;
1785
1786             internal ExecuteResult(DbCommand command, ReadOnlyCollection<SqlParameterInfo> parameters, IObjectReaderSession session, object value, bool useReturnValue)
1787                 : this(command, parameters, session) {
1788                 this.value = value;
1789                 this.useReturnValue = useReturnValue;
1790                 if (this.command != null && this.parameters != null && useReturnValue) {
1791                     iReturnParameter = GetParameterIndex("@RETURN_VALUE");
1792                 }
1793             }
1794
1795             internal ExecuteResult(DbCommand command, ReadOnlyCollection<SqlParameterInfo> parameters, IObjectReaderSession session) {
1796                 this.command = command;
1797                 this.parameters = parameters;
1798                 this.session = session;
1799             }
1800
1801             internal ExecuteResult(DbCommand command, ReadOnlyCollection<SqlParameterInfo> parameters, IObjectReaderSession session, object value)
1802                 : this(command, parameters, session, value, false) {
1803             }
1804
1805             [SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "value", Justification="FxCop Error -- False positive during code analysis")]
1806             public object ReturnValue {
1807                 get {
1808                     if (this.iReturnParameter >= 0) {
1809                         return this.GetParameterValue(this.iReturnParameter);
1810                     }
1811                     return this.value;
1812                 }
1813                 internal set {
1814                     Debug.Assert(!useReturnValue);
1815                     this.value = value;
1816                 }
1817             }
1818
1819             private int GetParameterIndex(string paramName) {
1820                 int idx = -1;
1821                 for (int i = 0, n = this.parameters.Count; i < n; i++) {
1822                     if (string.Compare(parameters[i].Parameter.Name, paramName, StringComparison.OrdinalIgnoreCase) == 0) {
1823                         idx = i;
1824                         break;
1825                     }
1826                 }
1827                 return idx;
1828             }
1829
1830             internal object GetParameterValue(string paramName) {
1831                 int idx = GetParameterIndex(paramName);
1832                 if (idx >= 0) {
1833                     return GetParameterValue(idx);
1834                 }
1835                 return null;
1836             }
1837
1838             public object GetParameterValue(int parameterIndex) {
1839                 if (this.parameters == null || parameterIndex < 0 || parameterIndex > this.parameters.Count) {
1840                     throw Error.ArgumentOutOfRange("parameterIndex");
1841                 }
1842
1843                 // SQL server requires all results to be read before output parameters are visible
1844                 if (this.session != null && !this.session.IsBuffered) {
1845                     this.session.Buffer();
1846                 }
1847
1848                 SqlParameterInfo pi = this.parameters[parameterIndex];
1849                 object parameterValue = this.command.Parameters[parameterIndex].Value;
1850                 if (parameterValue == DBNull.Value) parameterValue = null;
1851                 if (parameterValue != null && parameterValue.GetType() != pi.Parameter.ClrType) {
1852                     return DBConvert.ChangeType(parameterValue, pi.Parameter.ClrType);
1853                 }
1854
1855                 return parameterValue;
1856             }
1857
1858             public void Dispose() {
1859                 if (!this.isDisposed) {
1860                     // Technically, calling GC.SuppressFinalize is not required because the class does not
1861                     // have a finalizer, but it does no harm, protects against the case where a finalizer is added
1862                     // in the future, and prevents an FxCop warning.
1863                     GC.SuppressFinalize(this);
1864                     this.isDisposed = true;
1865                     if (this.session!=null) {
1866                         this.session.Dispose();
1867                     }
1868                 }
1869             }
1870         }
1871
1872         class SequenceOfOne<T> : IEnumerable<T>, IEnumerable {
1873             T[] sequence;
1874             internal SequenceOfOne(T value) {
1875                 this.sequence = new T[] { value };
1876             }
1877             public IEnumerator<T> GetEnumerator() {
1878                 return ((IEnumerable<T>)this.sequence).GetEnumerator();
1879             }
1880
1881             IEnumerator IEnumerable.GetEnumerator() {
1882                 return this.GetEnumerator();
1883             }
1884         }
1885
1886         class OneTimeEnumerable<T> : IEnumerable<T>, IEnumerable {
1887             IEnumerator<T> enumerator;
1888
1889             internal OneTimeEnumerable(IEnumerator<T> enumerator) {
1890                 System.Diagnostics.Debug.Assert(enumerator != null);
1891                 this.enumerator = enumerator;
1892             }
1893
1894             public IEnumerator<T> GetEnumerator() {
1895                 if (this.enumerator == null) {
1896                     throw Error.CannotEnumerateResultsMoreThanOnce();
1897                 }
1898                 IEnumerator<T> e = this.enumerator;
1899                 this.enumerator = null;
1900                 return e;
1901             }
1902
1903             IEnumerator IEnumerable.GetEnumerator() {
1904                 return this.GetEnumerator();
1905             }
1906         }
1907
1908         /// <summary>
1909         /// Result type for single rowset returning stored procedures.
1910         /// </summary>
1911         class SingleResult<T> : ISingleResult<T>, IDisposable, IListSource {
1912             private IEnumerable<T> enumerable;
1913             private ExecuteResult executeResult;
1914             private DataContext context;
1915             private IBindingList cachedList;
1916
1917             internal SingleResult(IEnumerable<T> enumerable, ExecuteResult executeResult, DataContext context) {
1918                 System.Diagnostics.Debug.Assert(enumerable != null);
1919                 System.Diagnostics.Debug.Assert(executeResult != null);
1920                 this.enumerable = enumerable;
1921                 this.executeResult = executeResult;
1922                 this.context = context;
1923             }
1924
1925             public IEnumerator<T> GetEnumerator() {
1926                 return enumerable.GetEnumerator();
1927             }
1928
1929             IEnumerator IEnumerable.GetEnumerator() {
1930                 return this.GetEnumerator();
1931             }
1932
1933             public object ReturnValue {
1934                 get {
1935                     return executeResult.GetParameterValue("@RETURN_VALUE");
1936                 }
1937             }
1938
1939             public void Dispose() {
1940                 // Technically, calling GC.SuppressFinalize is not required because the class does not
1941                 // have a finalizer, but it does no harm, protects against the case where a finalizer is added
1942                 // in the future, and prevents an FxCop warning.
1943                 GC.SuppressFinalize(this);
1944                 this.executeResult.Dispose();
1945             }
1946
1947             IList IListSource.GetList() {
1948                 if (this.cachedList == null) {
1949                     this.cachedList = BindingList.Create<T>(this.context, this);
1950                 }
1951                 return this.cachedList;
1952             }
1953
1954             bool IListSource.ContainsListCollection {
1955                 get { return false; }
1956             }
1957         }
1958
1959         class MultipleResults : IMultipleResults, IDisposable {
1960             SqlProvider provider;
1961             MetaFunction function;
1962             IObjectReaderSession session;
1963             bool isDisposed;
1964             private ExecuteResult executeResult;
1965
1966             internal MultipleResults(SqlProvider provider, MetaFunction function, IObjectReaderSession session, ExecuteResult executeResult) {
1967                 this.provider = provider;
1968                 this.function = function;
1969                 this.session = session;
1970                 this.executeResult = executeResult;
1971             }
1972
1973             public IEnumerable<T> GetResult<T>() {
1974                 MetaType metaType = null;
1975                 // Check the inheritance hierarchy of each mapped result row type
1976                 // for the function.
1977                 if (this.function != null) {
1978                     foreach (MetaType mt in function.ResultRowTypes) {
1979                         metaType = mt.InheritanceTypes.SingleOrDefault(it => it.Type == typeof(T));
1980                         if (metaType != null) {
1981                             break;
1982                         }
1983                     }
1984                 }
1985                 if (metaType == null) {
1986                     metaType = this.provider.services.Model.GetMetaType(typeof(T));
1987                 }
1988                 IObjectReaderFactory factory = this.provider.GetDefaultFactory(metaType);
1989                 IObjectReader objReader = factory.GetNextResult(this.session, false);
1990                 if (objReader == null) {
1991                     this.Dispose();
1992                     return null;
1993                 }
1994                 return new SingleResult<T>(new OneTimeEnumerable<T>((IEnumerator<T>)objReader), this.executeResult, this.provider.services.Context);
1995             }
1996
1997             public void Dispose() {
1998                 if (!this.isDisposed) {
1999                     // Technically, calling GC.SuppressFinalize is not required because the class does not
2000                     // have a finalizer, but it does no harm, protects against the case where a finalizer is added
2001                     // in the future, and prevents an FxCop warning.
2002                     GC.SuppressFinalize(this);
2003                     this.isDisposed = true;
2004                     if (this.executeResult != null) {
2005                         this.executeResult.Dispose();
2006                     }
2007                     else {
2008                         this.session.Dispose();
2009                     }
2010                 }
2011             }
2012
2013             public object ReturnValue {
2014                 get {
2015                     if (this.executeResult != null) {
2016                         return executeResult.GetParameterValue("@RETURN_VALUE");
2017                     } else {
2018                         return null;
2019                     }
2020                 }
2021             }
2022         }
2023     }
2024 }