Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / EntityClient / EntityCommand.cs
1 //---------------------------------------------------------------------
2 // <copyright file="EntityCommand.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner  Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 namespace System.Data.EntityClient
11 {
12     using System.Collections.Generic;
13     using System.ComponentModel;
14     using System.Data;
15     using System.Data.Common;
16     using System.Data.Common.CommandTrees;
17     using System.Data.Common.CommandTrees.ExpressionBuilder;
18     using System.Data.Common.EntitySql;
19     using System.Data.Common.QueryCache;
20     using System.Data.Common.Utils;
21     using System.Data.Metadata.Edm;
22     using System.Diagnostics;
23     using System.Linq;
24
25     /// <summary>
26     /// Class representing a command for the conceptual layer
27     /// </summary>
28     public sealed class EntityCommand : DbCommand
29     {
30         #region Fields
31         private const int InvalidCloseCount = -1;
32
33         private bool _designTimeVisible;
34         private string _esqlCommandText;
35         private EntityConnection _connection;
36         private DbCommandTree _preparedCommandTree;
37         private EntityParameterCollection _parameters;
38         private int? _commandTimeout;
39         private CommandType _commandType;
40         private EntityTransaction _transaction;
41         private UpdateRowSource _updatedRowSource;
42         private EntityCommandDefinition _commandDefinition;
43         private bool _isCommandDefinitionBased;
44         private DbCommandTree _commandTreeSetByUser;
45         private DbDataReader _dataReader;
46         private bool _enableQueryPlanCaching;
47         private DbCommand _storeProviderCommand;
48         #endregion
49
50         /// <summary>
51         /// Constructs the EntityCommand object not yet associated to a connection object
52         /// </summary>
53         public EntityCommand()
54         {
55             GC.SuppressFinalize(this);
56
57             // Initalize the member field with proper default values
58             this._designTimeVisible = true;
59             this._commandType = CommandType.Text;
60             this._updatedRowSource = UpdateRowSource.Both;
61             this._parameters = new EntityParameterCollection();
62
63             // Future Enhancement: (See SQLPT #300004256) At some point it would be  
64             // really nice to read defaults from a global configuration, but we're not 
65             // doing that today.  
66             this._enableQueryPlanCaching = true;
67         }
68
69         /// <summary>
70         /// Constructs the EntityCommand object with the given eSQL statement, but not yet associated to a connection object
71         /// </summary>
72         /// <param name="statement">The eSQL command text to execute</param>
73         public EntityCommand(string statement)
74             : this()
75         {
76             // Assign other member fields from the parameters
77             this._esqlCommandText = statement;
78         }
79
80         /// <summary>
81         /// Constructs the EntityCommand object with the given eSQL statement and the connection object to use
82         /// </summary>
83         /// <param name="statement">The eSQL command text to execute</param>
84         /// <param name="connection">The connection object</param>
85         public EntityCommand(string statement, EntityConnection connection)
86             : this(statement)
87         {
88             // Assign other member fields from the parameters
89             this._connection = connection;
90         }
91
92         /// <summary>
93         /// Constructs the EntityCommand object with the given eSQL statement and the connection object to use
94         /// </summary>
95         /// <param name="statement">The eSQL command text to execute</param>
96         /// <param name="connection">The connection object</param>
97         /// <param name="transaction">The transaction object this command executes in</param>
98         public EntityCommand(string statement, EntityConnection connection, EntityTransaction transaction)
99             : this(statement, connection)
100         {
101             // Assign other member fields from the parameters
102             this._transaction = transaction;
103         }
104
105         /// <summary>
106         /// Internal constructor used by EntityCommandDefinition
107         /// </summary>
108         /// <param name="commandDefinition">The prepared command definition that can be executed using this EntityCommand</param>
109         internal EntityCommand(EntityCommandDefinition commandDefinition)
110             : this()
111         {
112             // Assign other member fields from the parameters
113             this._commandDefinition = commandDefinition; 
114             this._parameters = new EntityParameterCollection();
115
116             // Make copies of the parameters
117             foreach (EntityParameter parameter in commandDefinition.Parameters)
118             {
119                 this._parameters.Add(parameter.Clone());
120             }
121
122             // Reset the dirty flag that was set to true when the parameters were added so that it won't say
123             // it's dirty to start with
124             this._parameters.ResetIsDirty();
125
126             // Track the fact that this command was created from and represents an already prepared command definition
127             this._isCommandDefinitionBased = true;
128         }
129
130         /// <summary>
131         /// Constructs a new EntityCommand given a EntityConnection and an EntityCommandDefition. This 
132         /// constructor is used by ObjectQueryExecution plan to execute an ObjectQuery.
133         /// </summary>
134         /// <param name="connection">The connection against which this EntityCommand should execute</param>
135         /// <param name="commandDefinition">The prepared command definition that can be executed using this EntityCommand</param>
136         internal EntityCommand(EntityConnection connection, EntityCommandDefinition entityCommandDefinition )
137             : this(entityCommandDefinition)
138         {
139             this._connection = connection;
140         }
141
142         /// <summary>
143         /// The connection object used for executing the command
144         /// </summary>
145         public new EntityConnection Connection
146         {
147             get
148             {
149                 return this._connection;
150             }
151             set
152             {
153                 ThrowIfDataReaderIsOpen();
154                 if (this._connection != value)
155                 {
156                     if (null != this._connection)
157                     {
158                         Unprepare();
159                     }
160                     this._connection = value;
161
162                     this._transaction = null;
163                 }
164             }
165         }
166
167         /// <summary>
168         /// The connection object used for executing the command
169         /// </summary>
170         protected override DbConnection DbConnection
171         {
172             get
173             {
174                 return this.Connection;
175             }
176             set
177             {
178                 this.Connection = (EntityConnection)value;
179             }
180         }
181
182         /// <summary>
183         /// The eSQL statement to execute, only one of the command tree or the command text can be set, not both
184         /// </summary>
185         public override string CommandText
186         {
187             get
188             {
189                 // If the user set the command tree previously, then we cannot retrieve the command text
190                 if (this._commandTreeSetByUser != null)
191                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_CannotGetCommandText);
192
193                 return this._esqlCommandText ?? "";
194             }
195             set
196             {
197                 ThrowIfDataReaderIsOpen();
198
199                 // If the user set the command tree previously, then we cannot set the command text
200                 if (this._commandTreeSetByUser != null)
201                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_CannotSetCommandText);
202
203                 if (this._esqlCommandText != value)
204                 {
205                     this._esqlCommandText = value;
206
207                     // Wipe out any preparation work we have done
208                     Unprepare();
209
210                     // If the user-defined command text or tree has been set (even to null or empty),
211                     // then this command can no longer be considered command definition-based
212                     this._isCommandDefinitionBased = false;
213                 }
214             }
215         }
216
217         /// <summary>
218         /// The command tree to execute, only one of the command tree or the command text can be set, not both.
219         /// </summary>
220         public DbCommandTree CommandTree
221         {
222             get
223             {
224                 // If the user set the command text previously, then we cannot retrieve the command tree
225                 if (!string.IsNullOrEmpty(this._esqlCommandText))
226                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_CannotGetCommandTree);
227
228                 return this._commandTreeSetByUser;
229             }
230             set
231             {
232                 ThrowIfDataReaderIsOpen();
233
234                 // If the user set the command text previously, then we cannot set the command tree
235                 if (!string.IsNullOrEmpty(this._esqlCommandText))
236                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_CannotSetCommandTree);
237
238                 // If the command type is not Text, CommandTree cannot be set
239                 if (CommandType.Text != CommandType)
240                 {
241                     throw EntityUtil.InternalError(EntityUtil.InternalErrorCode.CommandTreeOnStoredProcedureEntityCommand);
242                 }
243
244                 if (this._commandTreeSetByUser != value)
245                 {
246                     this._commandTreeSetByUser = value;
247
248                     // Wipe out any preparation work we have done
249                     Unprepare();
250
251                     // If the user-defined command text or tree has been set (even to null or empty),
252                     // then this command can no longer be considered command definition-based
253                     this._isCommandDefinitionBased = false;
254                 }
255             }
256         }
257
258         /// <summary>
259         /// Get or set the time in seconds to wait for the command to execute
260         /// </summary>
261         public override int CommandTimeout
262         {
263             get
264             {
265                 // Returns the timeout value if it has been set
266                 if (this._commandTimeout != null)
267                 {
268                     return this._commandTimeout.Value;
269                 }
270                 
271                 // Create a provider command object just so we can ask the default timeout
272                 if (this._connection != null && this._connection.StoreProviderFactory != null)
273                 {
274                     DbCommand storeCommand = this._connection.StoreProviderFactory.CreateCommand();
275                     if (storeCommand != null)
276                     {
277                         return storeCommand.CommandTimeout;
278                     }
279                 }
280
281                 return 0;
282             }
283             set
284             {
285                 ThrowIfDataReaderIsOpen();
286                 this._commandTimeout = value;
287             }
288         }
289
290         /// <summary>
291         /// The type of command being executed, only applicable when the command is using an eSQL statement and not the tree
292         /// </summary>
293         public override CommandType CommandType
294         {
295             get
296             {
297                 return this._commandType;
298             }
299             set
300             {
301                 ThrowIfDataReaderIsOpen();
302
303                 // For now, command type other than Text is not supported
304                 if (value != CommandType.Text && value != CommandType.StoredProcedure)
305                 {
306                     throw EntityUtil.NotSupported(System.Data.Entity.Strings.EntityClient_UnsupportedCommandType);
307                 }
308
309                 this._commandType = value;
310             }
311         }
312
313         /// <summary>
314         /// The collection of parameters for this command
315         /// </summary>
316         public new EntityParameterCollection Parameters
317         {
318             get
319             {
320                 return this._parameters;
321             }
322         }
323
324         /// <summary>
325         /// The collection of parameters for this command
326         /// </summary>
327         protected override DbParameterCollection DbParameterCollection
328         {
329             get
330             {
331                 return this.Parameters;
332             }
333         }
334
335         /// <summary>
336         /// The transaction object used for executing the command
337         /// </summary>
338         public new EntityTransaction Transaction
339         {
340             get
341             {
342                 return this._transaction;   // SQLBU 496829
343             }
344             set
345             {
346                 ThrowIfDataReaderIsOpen();
347                 this._transaction = value;
348             }
349         }
350
351         /// <summary>
352         /// The transaction that this command executes in
353         /// </summary>
354         protected override DbTransaction DbTransaction
355         {
356             get
357             {
358                 return this.Transaction;
359             }
360             set
361             {
362                 this.Transaction = (EntityTransaction)value;
363             }
364         }
365
366         /// <summary>
367         /// Gets or sets how command results are applied to the DataRow when used by the Update method of a DbDataAdapter
368         /// </summary>
369         public override UpdateRowSource UpdatedRowSource
370         {
371             get
372             {
373                 return this._updatedRowSource;
374             }
375             set
376             {
377                 ThrowIfDataReaderIsOpen();
378                 this._updatedRowSource = value;
379             }
380         }
381
382         /// <summary>
383         /// Hidden property used by the designers
384         /// </summary>
385         public override bool DesignTimeVisible
386         {
387             get
388             {
389                 return this._designTimeVisible;
390             }
391             set
392             {
393                 ThrowIfDataReaderIsOpen();
394                 this._designTimeVisible = value;
395                 TypeDescriptor.Refresh(this);
396             }
397         }
398
399         /// <summary>
400         /// Enables/Disables query plan caching for this EntityCommand
401         /// </summary>
402         public bool EnablePlanCaching
403         {
404             get
405             {
406                 return this._enableQueryPlanCaching;
407             }
408
409             set
410             {
411                 ThrowIfDataReaderIsOpen();
412                 this._enableQueryPlanCaching = value;
413             }
414         }
415
416         /// <summary>
417         /// Cancel the execution of the command
418         /// </summary>
419         public override void Cancel()
420         {
421         }
422
423         /// <summary>
424         /// Create and return a new parameter object representing a parameter in the eSQL statement
425         /// </summary>
426         ///
427         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
428         public new EntityParameter CreateParameter()
429         {
430             return new EntityParameter();
431         }
432
433         /// <summary>
434         /// Create and return a new parameter object representing a parameter in the eSQL statement
435         /// </summary>
436         protected override DbParameter CreateDbParameter()
437         {
438             return CreateParameter();
439         }
440
441         /// <summary>
442         /// Executes the command and returns a data reader for reading the results
443         /// </summary>
444         /// <returns>A data readerobject</returns>
445         public new EntityDataReader ExecuteReader()
446         {
447             return ExecuteReader(CommandBehavior.Default);
448         }
449
450         /// <summary>
451         /// Executes the command and returns a data reader for reading the results. May only
452         /// be called on CommandType.CommandText (otherwise, use the standard Execute* methods)
453         /// </summary>
454         /// <param name="behavior">The behavior to use when executing the command</param>
455         /// <returns>A data readerobject</returns>
456         /// <exception cref="InvalidOperationException">For stored procedure commands, if called
457         /// for anything but an entity collection result</exception>
458         public new EntityDataReader ExecuteReader(CommandBehavior behavior)
459         {
460             Prepare(); // prepare the query first
461
462             EntityDataReader reader = new EntityDataReader(this, _commandDefinition.Execute(this, behavior), behavior);
463             _dataReader = reader;
464             return reader;
465         }
466
467         /// <summary>
468         /// Executes the command and returns a data reader for reading the results
469         /// </summary>
470         /// <param name="behavior">The behavior to use when executing the command</param>
471         /// <returns>A data readerobject</returns>
472         protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
473         {
474             return ExecuteReader(behavior);
475         }
476
477         /// <summary>
478         /// Executes the command and discard any results returned from the command
479         /// </summary>
480         /// <returns>Number of rows affected</returns>
481         public override int ExecuteNonQuery()
482         {
483             return ExecuteScalar<int>(reader => 
484             {
485                 // consume reader before checking records affected
486                 CommandHelper.ConsumeReader(reader);
487                 return reader.RecordsAffected;
488             });
489         }
490
491         /// <summary>
492         /// Executes the command and return the first column in the first row of the result, extra results are ignored
493         /// </summary>
494         /// <returns>The result in the first column in the first row</returns>
495         public override object ExecuteScalar()
496         {
497             return ExecuteScalar<object>(reader => 
498             {
499                 object result = reader.Read() ? reader.GetValue(0) : null;
500                 // consume reader before retrieving parameters
501                 CommandHelper.ConsumeReader(reader);
502                 return result;
503             });
504         }
505
506         /// <summary>
507         /// Executes a reader and retrieves a scalar value using the given resultSelector delegate
508         /// </summary>
509         private T_Result ExecuteScalar<T_Result>(Func<DbDataReader, T_Result> resultSelector)
510         {
511             T_Result result;
512             using (EntityDataReader reader = ExecuteReader(CommandBehavior.SequentialAccess))
513             {
514                 result = resultSelector(reader);
515             }
516             return result;
517         }
518
519         /// <summary>
520         /// Clear out any "compile" state
521         /// </summary>
522         internal void Unprepare()
523         {
524             this._commandDefinition = null;
525             this._preparedCommandTree = null;
526
527             // Clear the dirty flag on the parameters and parameter collection
528             _parameters.ResetIsDirty();            
529         }
530
531         /// <summary>
532         /// Creates a prepared version of this command
533         /// </summary>
534         public override void Prepare()
535         {
536             ThrowIfDataReaderIsOpen();
537             CheckIfReadyToPrepare();
538
539             InnerPrepare();
540         }
541
542         /// <summary>
543         /// Creates a prepared version of this command without regard to the current connection state.
544         /// Called by both <see cref="Prepare"/> and <see cref="ToTraceString"/>.
545         /// </summary>
546         private void InnerPrepare()
547         {
548             // Unprepare if the parameters have changed to force a reprepare
549             if (_parameters.IsDirty)
550             {
551                 Unprepare();
552             }
553
554             _commandDefinition = GetCommandDefinition();
555             Debug.Assert(null != _commandDefinition, "_commandDefinition cannot be null");
556         }
557
558         /// <summary>
559         /// Ensures we have the command tree, either the user passed us the tree, or an eSQL statement that we need to parse
560         /// </summary>
561         private void MakeCommandTree()
562         {
563             // We must have a connection before we come here
564             Debug.Assert(this._connection != null);
565
566             // Do the work only if we don't have a command tree yet
567             if (this._preparedCommandTree == null)
568             {
569                 DbCommandTree resultTree = null;
570                 if (this._commandTreeSetByUser != null)
571                 {
572                     resultTree = this._commandTreeSetByUser;
573                 }
574                 else
575                 if (CommandType.Text == CommandType)
576                 {
577                     if (!string.IsNullOrEmpty(this._esqlCommandText))
578                     {
579                         // The perspective to be used for the query compilation
580                         Perspective perspective = (Perspective)new ModelPerspective(_connection.GetMetadataWorkspace());
581
582                         // get a dictionary of names and typeusage from entity parameter collection
583                         Dictionary<string, TypeUsage> queryParams = GetParameterTypeUsage();
584
585                         resultTree = CqlQuery.Compile(
586                             this._esqlCommandText, 
587                             perspective, 
588                             null /*parser option - use default*/, 
589                             queryParams.Select(paramInfo => paramInfo.Value.Parameter(paramInfo.Key))).CommandTree;
590                     }
591                     else
592                     {
593                         // We have no command text, no command tree, so throw an exception
594                         if (this._isCommandDefinitionBased)
595                         {
596                             // This command was based on a prepared command definition and has no command text,
597                             // so reprepare is not possible. To create a new command with different parameters
598                             // requires creating a new entity command definition and calling it's CreateCommand method.
599                             throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_CannotReprepareCommandDefinitionBasedCommand);
600                         }
601                         else
602                         {
603                             throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_NoCommandText);
604                         }
605                     }
606                 }
607                 else if (CommandType.StoredProcedure == CommandType)
608                 {
609                     // get a dictionary of names and typeusage from entity parameter collection
610                     IEnumerable<KeyValuePair<string, TypeUsage>> queryParams = GetParameterTypeUsage();
611                     EdmFunction function = DetermineFunctionImport();
612                     resultTree = new DbFunctionCommandTree(this.Connection.GetMetadataWorkspace(), DataSpace.CSpace, function, null, queryParams);
613                 }
614
615                 // After everything is good and succeeded, assign the result to our field
616                 this._preparedCommandTree = resultTree;
617             }
618         }
619
620         // requires: this must be a StoreProcedure command
621         // effects: determines the EntityContainer function import referenced by this.CommandText
622         private EdmFunction DetermineFunctionImport()
623         {
624             Debug.Assert(CommandType.StoredProcedure == this.CommandType);
625
626             if (string.IsNullOrEmpty(this.CommandText) ||
627                 string.IsNullOrEmpty(this.CommandText.Trim()))
628             {
629                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_FunctionImportEmptyCommandText);
630             }
631
632             MetadataWorkspace workspace = _connection.GetMetadataWorkspace();
633
634             // parse the command text
635             string containerName;
636             string functionImportName;
637             string defaultContainerName = null; // no default container in EntityCommand
638             CommandHelper.ParseFunctionImportCommandText(this.CommandText, defaultContainerName, out containerName, out functionImportName);
639
640             return CommandHelper.FindFunctionImport(_connection.GetMetadataWorkspace(), containerName, functionImportName);
641         }
642
643         /// <summary>
644         /// Get the command definition for the command; will construct one if there is not already
645         /// one constructed, which means it will prepare the command on the client.
646         /// </summary>
647         /// <returns>the command definition</returns>
648         internal EntityCommandDefinition GetCommandDefinition()
649         {
650             EntityCommandDefinition entityCommandDefinition = _commandDefinition;
651
652             // Construct the command definition using no special options;
653             if (null == entityCommandDefinition)
654             {
655                 //
656                 // check if the _commandDefinition is in cache
657                 //
658                 if (!TryGetEntityCommandDefinitionFromQueryCache(out entityCommandDefinition))
659                 {
660                     //
661                     // if not, construct the command definition using no special options;
662                     //
663                     entityCommandDefinition = CreateCommandDefinition();
664                 }
665
666                 _commandDefinition = entityCommandDefinition;
667             }
668
669             return entityCommandDefinition;
670         }
671
672         /// <summary>
673         /// Returns the store command text.
674         /// </summary>
675         /// <returns></returns>
676         [Browsable(false)]
677         public string ToTraceString() 
678         {
679             CheckConnectionPresent();
680
681             InnerPrepare();
682
683             EntityCommandDefinition commandDefinition = _commandDefinition;
684             if (null != commandDefinition) 
685             {
686                 return commandDefinition.ToTraceString();
687             }
688             return string.Empty;
689         }
690
691         /// <summary>
692         /// Gets an entitycommanddefinition from cache if a match is found for the given cache key.
693         /// </summary>
694         /// <param name="entityCommandDefinition">out param. returns the entitycommanddefinition for a given cache key</param>
695         /// <returns>true if a match is found in cache, false otherwise</returns>
696         private bool TryGetEntityCommandDefinitionFromQueryCache( out EntityCommandDefinition entityCommandDefinition )
697         {
698             Debug.Assert(null != _connection, "Connection must not be null at this point");
699             entityCommandDefinition = null;
700
701             //
702             // if EnableQueryCaching is false, then just return to force the CommandDefinition to be created
703             //
704             if (!this._enableQueryPlanCaching || string.IsNullOrEmpty(this._esqlCommandText))
705             {
706                 return false;
707             }
708
709             //
710             // Create cache key
711             //
712             EntityClientCacheKey queryCacheKey = new EntityClientCacheKey(this);
713
714             //
715             // Try cache lookup
716             //
717             QueryCacheManager queryCacheManager = _connection.GetMetadataWorkspace().GetQueryCacheManager();
718             Debug.Assert(null != queryCacheManager,"QuerycacheManager instance cannot be null");
719             if (!queryCacheManager.TryCacheLookup(queryCacheKey, out entityCommandDefinition))
720             {
721                 //
722                 // if not, construct the command definition using no special options;
723                 //
724                 entityCommandDefinition = CreateCommandDefinition();
725
726                 //
727                 // add to the cache
728                 //
729                 QueryCacheEntry outQueryCacheEntry = null;
730                 if (queryCacheManager.TryLookupAndAdd(new QueryCacheEntry(queryCacheKey, entityCommandDefinition), out outQueryCacheEntry))
731                 {
732                     entityCommandDefinition = (EntityCommandDefinition)outQueryCacheEntry.GetTarget();
733                 }
734             }
735             
736             Debug.Assert(null != entityCommandDefinition, "out entityCommandDefinition must not be null");
737
738             return true;
739         }
740
741         /// <summary>
742         /// Creates a commandDefinition for the command, using the options specified.  
743         /// 
744         /// Note: This method must not be side-effecting of the command
745         /// </summary>
746         /// <returns>the command definition</returns>
747         private EntityCommandDefinition CreateCommandDefinition() 
748         {
749             MakeCommandTree();
750             // Always check the CQT metadata against the connection metadata (internally, CQT already
751             // validates metadata consistency)
752             if (!_preparedCommandTree.MetadataWorkspace.IsMetadataWorkspaceCSCompatible(this.Connection.GetMetadataWorkspace()))
753             {
754                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_CommandTreeMetadataIncompatible);
755             }
756             EntityCommandDefinition result = EntityProviderServices.Instance.CreateCommandDefinition(this._connection.StoreProviderFactory, this._preparedCommandTree);
757             return result;
758         }
759
760         private void CheckConnectionPresent()
761         {
762             if (this._connection == null)
763             {
764                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_NoConnectionForCommand);
765             }
766         }
767
768         /// <summary>
769         /// Checking the integrity of this command object to see if it's ready to be prepared or executed
770         /// </summary>
771         private void CheckIfReadyToPrepare()
772         {
773             // Check that we have a connection
774             CheckConnectionPresent();
775
776             if (this._connection.StoreProviderFactory == null || this._connection.StoreConnection == null)
777             {
778                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_ConnectionStringNeededBeforeOperation);
779             }
780
781             // Make sure the connection is not closed or broken
782             if ((this._connection.State == ConnectionState.Closed) || (this._connection.State == ConnectionState.Broken))
783             {
784                 string message = System.Data.Entity.Strings.EntityClient_ExecutingOnClosedConnection(
785                     this._connection.State == ConnectionState.Closed ?
786                     System.Data.Entity.Strings.EntityClient_ConnectionStateClosed :
787                     System.Data.Entity.Strings.EntityClient_ConnectionStateBroken);
788                 throw EntityUtil.InvalidOperation(message);
789             }
790         }
791
792         /// <summary>
793         /// Checking if the command is still tied to a data reader, if so, then the reader must still be open and we throw
794         /// </summary>
795         private void ThrowIfDataReaderIsOpen()
796         {
797             if (this._dataReader != null)
798             {
799                 throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_DataReaderIsStillOpen);
800             }
801         }
802
803         /// <summary>
804         /// Returns a dictionary of parameter name and parameter typeusage in s-space from the entity parameter 
805         /// collection given by the user.
806         /// </summary>
807         /// <returns></returns>
808         internal Dictionary<string, TypeUsage> GetParameterTypeUsage()
809         {
810             Debug.Assert(null != _parameters, "_parameters must not be null");
811             // Extract type metadata objects from the parameters to be used by CqlQuery.Compile
812             Dictionary<string, TypeUsage> queryParams = new Dictionary<string, TypeUsage>(_parameters.Count);
813             foreach (EntityParameter parameter in this._parameters)
814             {
815                 // Validate that the parameter name has the format: A character followed by alphanumerics or
816                 // underscores
817                 string parameterName = parameter.ParameterName;
818                 if (string.IsNullOrEmpty(parameterName))
819                 {
820                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_EmptyParameterName);
821                 }
822
823                 // Check each parameter to make sure it's an input parameter, currently EntityCommand doesn't support
824                 // anything else
825                 if (this.CommandType == CommandType.Text && parameter.Direction != ParameterDirection.Input)
826                 {
827                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_InvalidParameterDirection(parameter.ParameterName));
828                 }
829
830                 // Checking that we can deduce the type from the parameter if the type is not set
831                 if (parameter.EdmType == null && parameter.DbType == DbType.Object && (parameter.Value == null || parameter.Value is DBNull))
832                 {
833                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_UnknownParameterType(parameterName));
834                 }
835
836                 // Validate that the parameter has an appropriate type and value
837                 // Any failures in GetTypeUsage will be surfaced as exceptions to the user
838                 TypeUsage typeUsage = null;
839                 typeUsage = parameter.GetTypeUsage();
840
841                 // Add the query parameter, add the same time detect if this parameter has the same name of a previous parameter
842                 try
843                 {
844                     queryParams.Add(parameterName, typeUsage);
845                 }
846                 catch (ArgumentException e)
847                 {
848                     throw EntityUtil.InvalidOperation(System.Data.Entity.Strings.EntityClient_DuplicateParameterNames(parameter.ParameterName), e);
849                 }
850             }
851             
852             return queryParams;
853         }
854
855         /// <summary>
856         /// Call only when the reader associated with this command is closing. Copies parameter values where necessary.
857         /// </summary>
858         internal void NotifyDataReaderClosing()
859         {
860             // Disassociating the data reader with this command
861             this._dataReader = null;
862
863             if (null != _storeProviderCommand)
864             {
865                 CommandHelper.SetEntityParameterValues(this, _storeProviderCommand, _connection);
866                 _storeProviderCommand = null;
867             }
868             if (null != this.OnDataReaderClosing)
869             {
870                 this.OnDataReaderClosing(this, new EventArgs());
871             }
872         }
873
874         /// <summary>
875         /// Tells the EntityCommand about the underlying store provider command in case it needs to pull parameter values
876         /// when the reader is closing.
877         /// </summary>
878         internal void SetStoreProviderCommand(DbCommand storeProviderCommand)
879         {
880             _storeProviderCommand = storeProviderCommand;
881         }
882
883         /// <summary>
884         /// Event raised when the reader is closing.
885         /// </summary>
886         internal event EventHandler OnDataReaderClosing;
887     }
888 }