Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Objects / ObjectQuery_EntitySqlExtensions.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ObjectQuery_EntitySqlExtensions.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.Objects
11 {
12     using System;
13     using System.Collections.Generic;
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.Utils;
19     using System.Data.Metadata.Edm;
20     using System.Data.Objects.ELinq;
21     using System.Data.Objects.Internal;
22     using System.Globalization;
23     using System.Linq;
24
25     /// <summary>
26     ///   ObjectQuery implements strongly-typed queries at the object-layer.
27     ///   Queries are specified using Entity-SQL strings and may be created by calling
28     ///   the Entity-SQL-based query builder methods declared by ObjectQuery.
29     /// </summary>
30     /// <typeparam name="T">The result type of this ObjectQuery</typeparam>
31     public partial class ObjectQuery<T> : IEnumerable<T>
32     {
33         #region Private Static Members
34
35         // -----------------
36         // Static Fields
37         // -----------------
38
39         /// <summary>
40         ///   The default query name, which is used in query-building to refer to an
41         ///   element of the ObjectQuery; e.g., in a call to ObjectQuery.Where(), a predicate of
42         ///   the form "it.Name = 'Foo'" can be specified, where "it" refers to a T.
43         ///   Note that the query name may eventually become a parameter in the command
44         ///   tree, so it must conform to the parameter name restrictions enforced by
45         ///   ObjectParameter.ValidateParameterName(string).
46         /// </summary>
47         private const string DefaultName = "it";
48
49         private static bool IsLinqQuery(ObjectQuery query)
50         {
51             return (query.QueryState is ELinqQueryState);
52         }
53
54         #endregion
55
56         #region Private Instance Members
57         
58         // -------------------
59         // Private Fields
60         // -------------------
61
62         /// <summary>
63         ///   The name of the current sequence, which defaults to "it". Used in query-
64         ///   builder methods that process an Entity-SQL command text fragment to refer to an
65         ///   instance of the return type of this query.
66         /// </summary>
67         private string _name = ObjectQuery<T>.DefaultName;
68
69         #endregion
70
71         #region Public Constructors
72
73         // -------------------
74         // Public Constructors
75         // -------------------
76
77         #region ObjectQuery (string, ObjectContext)
78
79         /// <summary>
80         ///   This constructor creates a new ObjectQuery instance using the specified Entity-SQL
81         ///   command as the initial query. The context specifies the connection on
82         ///   which to execute the query as well as the metadata and result cache.
83         /// </summary>
84         /// <param name="commandText">
85         ///   The Entity-SQL query string that initially defines the query.
86         /// </param>
87         /// <param name="context">
88         ///   The ObjectContext containing the metadata workspace the query will
89         ///   be built against, the connection on which to execute the query, and the
90         ///   cache to store the results in.
91         /// </param>
92         /// <returns>
93         ///   A new ObjectQuery instance.
94         /// </returns>
95         public ObjectQuery (string commandText, ObjectContext context)
96             : this(new EntitySqlQueryState(typeof(T), commandText, false, context, null, null))
97         {
98             // SQLBUDT 447285: Ensure the assembly containing the entity's CLR type
99             // is loaded into the workspace. If the schema types are not loaded
100             // metadata, cache & query would be unable to reason about the type. We
101             // either auto-load <T>'s assembly into the ObjectItemCollection or we
102             // auto-load the user's calling assembly and its referenced assemblies.
103             // If the entities in the user's result spans multiple assemblies, the
104             // user must manually call LoadFromAssembly. *GetCallingAssembly returns
105             // the assembly of the method that invoked the currently executing method.
106             context.MetadataWorkspace.ImplicitLoadAssemblyForType(typeof(T), System.Reflection.Assembly.GetCallingAssembly());
107         }
108
109         #endregion
110
111         #region ObjectQuery (string, ObjectContext, MergeOption)
112
113         /// <summary>
114         ///   This constructor creates a new ObjectQuery instance using the specified Entity-SQL
115         ///   command as the initial query. The context specifies the connection on
116         ///   which to execute the query as well as the metadata and result cache.
117         ///   The merge option specifies how the cache should be populated/updated.
118         /// </summary>
119         /// <param name="commandText">
120         ///   The Entity-SQL query string that initially defines the query.
121         /// </param>
122         /// <param name="context">
123         ///   The ObjectContext containing the metadata workspace the query will
124         ///   be built against, the connection on which to execute the query, and the
125         ///   cache to store the results in.
126         /// </param>
127         /// <param name="mergeOption">
128         ///   The MergeOption to use when executing the query.
129         /// </param>
130         /// <returns>
131         ///   A new ObjectQuery instance.
132         /// </returns>
133         public ObjectQuery (string commandText, ObjectContext context, MergeOption mergeOption)
134             : this(new EntitySqlQueryState(typeof(T), commandText, false, context, null, null))
135         {
136             EntityUtil.CheckArgumentMergeOption(mergeOption);
137             this.QueryState.UserSpecifiedMergeOption = mergeOption;
138
139             // SQLBUDT 447285: Ensure the assembly containing the entity's CLR type
140             // is loaded into the workspace. If the schema types are not loaded
141             // metadata, cache & query would be unable to reason about the type. We
142             // either auto-load <T>'s assembly into the ObjectItemCollection or we
143             // auto-load the user's calling assembly and its referenced assemblies.
144             // If the entities in the user's result spans multiple assemblies, the
145             // user must manually call LoadFromAssembly. *GetCallingAssembly returns
146             // the assembly of the method that invoked the currently executing method.
147             context.MetadataWorkspace.ImplicitLoadAssemblyForType(typeof(T), System.Reflection.Assembly.GetCallingAssembly());
148         }
149
150         #endregion
151
152         #endregion
153
154         #region internal ObjectQuery (EntitySet, ObjectContext, MergeOption) constructor.
155
156         /// <summary>
157         ///   This method creates a new ObjectQuery instance that represents a scan over
158         ///   the specified <paramref name="entitySet"/>. This ObjectQuery carries the scan as <see cref="DbExpression"/> 
159         ///   and as Entity SQL. This is needed to allow case-sensitive metadata access (provided by the <see cref="DbExpression"/> by default).
160         ///   The context specifies the connection on which to execute the query as well as the metadata and result cache.
161         ///   The merge option specifies how the cache should be populated/updated.
162         /// </summary>
163         /// <param name="entitySet">
164         ///   The entity set this query scans.
165         /// </param>
166         /// <param name="context">
167         ///   The ObjectContext containing the metadata workspace the query will
168         ///   be built against, the connection on which to execute the query, and the
169         ///   cache to store the results in.
170         /// </param>
171         /// <param name="mergeOption">
172         ///   The MergeOption to use when executing the query.
173         /// </param>
174         /// <returns>
175         ///   A new ObjectQuery instance.
176         /// </returns>
177         internal ObjectQuery (EntitySetBase entitySet, ObjectContext context, MergeOption mergeOption)
178             : this(new EntitySqlQueryState(typeof(T), BuildScanEntitySetEsql(entitySet), entitySet.Scan(), false, context, null, null))
179         {
180             EntityUtil.CheckArgumentMergeOption(mergeOption);
181             this.QueryState.UserSpecifiedMergeOption = mergeOption;
182
183             // SQLBUDT 447285: Ensure the assembly containing the entity's CLR type
184             // is loaded into the workspace. If the schema types are not loaded
185             // metadata, cache & query would be unable to reason about the type. We
186             // either auto-load <T>'s assembly into the ObjectItemCollection or we
187             // auto-load the user's calling assembly and its referenced assemblies.
188             // If the entities in the user's result spans multiple assemblies, the
189             // user must manually call LoadFromAssembly. *GetCallingAssembly returns
190             // the assembly of the method that invoked the currently executing method.
191             context.MetadataWorkspace.ImplicitLoadAssemblyForType(typeof(T), System.Reflection.Assembly.GetCallingAssembly());
192         }
193
194         private static string BuildScanEntitySetEsql(EntitySetBase entitySet)
195         {
196             EntityUtil.CheckArgumentNull(entitySet, "entitySet");
197             return String.Format(
198                     CultureInfo.InvariantCulture,
199                     "{0}.{1}",
200                     EntityUtil.QuoteIdentifier(entitySet.EntityContainer.Name),
201                     EntityUtil.QuoteIdentifier(entitySet.Name));
202         }
203
204         #endregion
205
206         #region Public Properties
207         
208         /// <summary>
209         ///   The name of the query, which can be used to identify the current sequence
210         ///   by name in query-builder methods. By default, the value is "it".
211         /// </summary>
212         /// <exception cref="ArgumentException">
213         ///   If the value specified on set is invalid.
214         /// </exception>
215         public string Name
216         {
217             get
218             {
219                 return this._name;
220             }
221             set
222             {
223                 EntityUtil.CheckArgumentNull(value, "value");
224
225                 if (!ObjectParameter.ValidateParameterName(value))
226                 {
227                     throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectQuery_InvalidQueryName(value), "value");
228                 }
229
230                 this._name = value;
231             }
232         }
233
234         #endregion
235
236         #region Query-builder Methods
237
238         // ---------------------
239         // Query-builder Methods
240         // ---------------------
241
242         /// <summary>
243         ///   This query-builder method creates a new query whose results are the
244         ///   unique results of this query.
245         /// </summary>
246         /// <returns>
247         ///   a new ObjectQuery instance.
248         /// </returns>
249         public ObjectQuery<T> Distinct ()
250         {
251             if (IsLinqQuery(this))
252             {
253                 return (ObjectQuery<T>)Queryable.Distinct<T>(this);
254             }
255             return new ObjectQuery<T>(EntitySqlQueryBuilder.Distinct(this.QueryState));
256         }
257
258         /// <summary>
259         ///   This query-builder method creates a new query whose results are all of
260         ///   the results of this query, except those that are also part of the other
261         ///   query specified.
262         /// </summary>
263         /// <param name="query">
264         ///   A query representing the results to exclude.
265         /// </param>
266         /// <returns>
267         ///   a new ObjectQuery instance.
268         /// </returns>
269         /// <exception cref="ArgumentNullException">
270         ///   If the query parameter is null.
271         /// </exception>
272         public ObjectQuery<T> Except(ObjectQuery<T> query)
273         {
274             EntityUtil.CheckArgumentNull(query, "query");
275
276             if (IsLinqQuery(this) || IsLinqQuery(query))
277             {
278                 return (ObjectQuery<T>)Queryable.Except(this, query);
279             }
280             return new ObjectQuery<T>(EntitySqlQueryBuilder.Except(this.QueryState, query.QueryState));
281         }
282                 
283         /// <summary>
284         ///   This query-builder method creates a new query whose results are the results
285         ///   of this query, grouped by some criteria.
286         /// </summary>
287         /// <param name="keys">
288         ///   The group keys.
289         /// </param>
290         /// <param name="projection">
291         ///   The projection list. To project the group, use the keyword "group".
292         /// </param>
293         /// <param name="parameters">
294         ///   An optional set of query parameters that should be in scope when parsing.
295         /// </param>
296         /// <returns>
297         ///   a new ObjectQuery instance.
298         /// </returns>
299         public ObjectQuery<DbDataRecord> GroupBy(string keys, string projection, params ObjectParameter[] parameters)
300         {
301             EntityUtil.CheckArgumentNull(keys, "keys");
302             EntityUtil.CheckArgumentNull(projection, "projection");
303             EntityUtil.CheckArgumentNull(parameters, "parameters");
304             
305             if (StringUtil.IsNullOrEmptyOrWhiteSpace(keys))
306             {
307                 throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectQuery_QueryBuilder_InvalidGroupKeyList, "keys");
308             }
309
310             if (StringUtil.IsNullOrEmptyOrWhiteSpace(projection))
311             {
312                 throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectQuery_QueryBuilder_InvalidProjectionList, "projection");
313             }
314
315             return new ObjectQuery<DbDataRecord>(EntitySqlQueryBuilder.GroupBy(this.QueryState, this.Name, keys, projection, parameters));
316         }
317
318         /// <summary>
319         ///   This query-builder method creates a new query whose results are those that
320         ///   are both in this query and the other query specified.
321         /// </summary>
322         /// <param name="query">
323         ///   A query representing the results to intersect with.
324         /// </param>
325         /// <returns>
326         ///   a new ObjectQuery instance.
327         /// </returns>
328         /// <exception cref="ArgumentNullException">
329         ///   If the query parameter is null.
330         /// </exception>
331         public ObjectQuery<T> Intersect (ObjectQuery<T> query)
332         {
333             EntityUtil.CheckArgumentNull(query, "query");
334
335             if (IsLinqQuery(this) || IsLinqQuery(query))
336             {
337                 return (ObjectQuery<T>)Queryable.Intersect(this, query);
338             }
339             return new ObjectQuery<T>(EntitySqlQueryBuilder.Intersect(this.QueryState, query.QueryState));
340         }
341         
342         /// <summary>
343         ///   This query-builder method creates a new query whose results are filtered
344         ///   to include only those of the specified type.
345         /// </summary>
346         /// <returns>
347         ///   a new ObjectQuery instance.
348         /// </returns>
349         /// <exception cref="EntitySqlException">
350         ///   If the type specified is invalid.
351         /// </exception>
352         public ObjectQuery<TResultType> OfType<TResultType>()
353         {
354             if (IsLinqQuery(this))
355             {
356                 return (ObjectQuery<TResultType>)Queryable.OfType<TResultType>(this);
357             }
358
359             // SQLPUDT 484477: Make sure TResultType is loaded.
360             this.QueryState.ObjectContext.MetadataWorkspace.ImplicitLoadAssemblyForType(typeof(TResultType), System.Reflection.Assembly.GetCallingAssembly());
361
362             // Retrieve the O-Space type metadata for the result type specified. If no
363             // metadata can be found for the specified type, fail. Otherwise, if the
364             // type metadata found for TResultType is not either an EntityType or a
365             // ComplexType, fail - OfType() is not a valid operation on scalars,
366             // enumerations, collections, etc.
367             Type clrOfType = typeof(TResultType);
368             EdmType ofType = null;
369             if (!this.QueryState.ObjectContext.MetadataWorkspace.GetItemCollection(DataSpace.OSpace).TryGetType(clrOfType.Name, clrOfType.Namespace ?? string.Empty, out ofType) ||
370                 !(Helper.IsEntityType(ofType) || Helper.IsComplexType(ofType)))
371             {
372                 throw EntityUtil.EntitySqlError(System.Data.Entity.Strings.ObjectQuery_QueryBuilder_InvalidResultType(typeof(TResultType).FullName));
373             }
374
375             return new ObjectQuery<TResultType>(EntitySqlQueryBuilder.OfType(this.QueryState, ofType, clrOfType));
376         }
377         
378         /// <summary>
379         ///   This query-builder method creates a new query whose results are the
380         ///   results of this query, ordered by some criteria. Note that any relational
381         ///   operations performed after an OrderBy have the potential to "undo" the
382         ///   ordering, so OrderBy should be considered a terminal query-building
383         ///   operation.
384         /// </summary>
385         /// <param name="keys">
386         ///   The sort keys.
387         /// </param>
388         /// <param name="parameters">
389         ///   An optional set of query parameters that should be in scope when parsing.
390         /// </param>
391         /// <returns>
392         ///   a new ObjectQuery instance.
393         /// </returns>
394         /// <exception cref="ArgumentNullException">
395         ///   If either argument is null.
396         /// </exception>
397         /// <exception cref="ArgumentException">
398         ///   If the sort key command text is empty.
399         /// </exception>
400         public ObjectQuery<T> OrderBy (string keys, params ObjectParameter[] parameters)
401         {
402             EntityUtil.CheckArgumentNull(keys, "keys");
403             EntityUtil.CheckArgumentNull(parameters, "parameters");
404
405             if (StringUtil.IsNullOrEmptyOrWhiteSpace(keys))
406             {
407                 throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectQuery_QueryBuilder_InvalidSortKeyList, "keys");
408             }
409
410             return new ObjectQuery<T>(EntitySqlQueryBuilder.OrderBy(this.QueryState, this.Name, keys, parameters));
411         }
412         
413         /// <summary>
414         ///   This query-builder method creates a new query whose results are data
415         ///   records containing selected fields of the results of this query.
416         /// </summary>
417         /// <param name="projection">
418         ///   The projection list.
419         /// </param>
420         /// <param name="parameters">
421         ///   An optional set of query parameters that should be in scope when parsing.
422         /// </param>
423         /// <returns>
424         ///   a new ObjectQuery instance.
425         /// </returns>
426         /// <exception cref="ArgumentNullException">
427         ///   If either argument is null.
428         /// </exception>
429         /// <exception cref="ArgumentException">
430         ///   If the projection list command text is empty.
431         /// </exception>
432         public ObjectQuery<DbDataRecord> Select (string projection, params ObjectParameter[] parameters)
433         {
434             EntityUtil.CheckArgumentNull(projection, "projection");
435             EntityUtil.CheckArgumentNull(parameters, "parameters");
436
437             if (StringUtil.IsNullOrEmptyOrWhiteSpace(projection))
438             {
439                 throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectQuery_QueryBuilder_InvalidProjectionList, "projection");
440             }
441
442             return new ObjectQuery<DbDataRecord>(EntitySqlQueryBuilder.Select(this.QueryState, this.Name, projection, parameters));
443         }
444          
445         /// <summary>
446         ///   This query-builder method creates a new query whose results are a sequence
447         ///   of values projected from the results of this query.
448         /// </summary>
449         /// <param name="projection">
450         ///   The projection list.
451         /// </param>
452         /// <param name="parameters">
453         ///   An optional set of query parameters that should be in scope when parsing.
454         /// </param>
455         /// <returns>
456         ///   a new ObjectQuery instance.
457         /// </returns>
458         /// <exception cref="ArgumentNullException">
459         ///   If either argument is null.
460         /// </exception>
461         /// <exception cref="ArgumentException">
462         ///   If the projection list command text is empty.
463         /// </exception>
464         public ObjectQuery<TResultType> SelectValue<TResultType> (string projection, params ObjectParameter[] parameters)
465         {
466             EntityUtil.CheckArgumentNull(projection, "projection");
467             EntityUtil.CheckArgumentNull(parameters, "parameters");
468
469             if (StringUtil.IsNullOrEmptyOrWhiteSpace(projection))
470             {
471                 throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectQuery_QueryBuilder_InvalidProjectionList, "projection");
472             }
473
474             // SQLPUDT 484974: Make sure TResultType is loaded.
475             this.QueryState.ObjectContext.MetadataWorkspace.ImplicitLoadAssemblyForType(typeof(TResultType), System.Reflection.Assembly.GetCallingAssembly());
476
477             return new ObjectQuery<TResultType>(EntitySqlQueryBuilder.SelectValue(this.QueryState, this.Name, projection, parameters, typeof(TResultType)));
478         }
479
480         /// <summary>
481         ///   This query-builder method creates a new query whose results are the
482         ///   results of this query, ordered by some criteria and with the specified
483         ///   number of results 'skipped', or paged-over.
484         /// </summary>
485         /// <param name="keys">
486         ///   The sort keys.
487         /// </param>
488         /// <param name="count">
489         ///   Specifies the number of results to skip. This must be either a constant or
490         ///   a parameter reference.
491         /// </param>
492         /// <param name="parameters">
493         ///   An optional set of query parameters that should be in scope when parsing.
494         /// </param>
495         /// <returns>
496         ///   a new ObjectQuery instance.
497         /// </returns>
498         /// <exception cref="ArgumentNullException">
499         ///   If any argument is null.
500         /// </exception>
501         /// <exception cref="ArgumentException">
502         ///   If the sort key or skip count command text is empty.
503         /// </exception>
504         public ObjectQuery<T> Skip (string keys, string count, params ObjectParameter[] parameters)
505         {
506             EntityUtil.CheckArgumentNull(keys, "keys");
507             EntityUtil.CheckArgumentNull(count, "count");
508             EntityUtil.CheckArgumentNull(parameters, "parameters");
509
510             if (StringUtil.IsNullOrEmptyOrWhiteSpace(keys))
511             {
512                 throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectQuery_QueryBuilder_InvalidSortKeyList, "keys");
513             }
514
515             if (StringUtil.IsNullOrEmptyOrWhiteSpace(count))
516             {
517                 throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectQuery_QueryBuilder_InvalidSkipCount, "count");
518             }
519
520             return new ObjectQuery<T>(EntitySqlQueryBuilder.Skip(this.QueryState, this.Name, keys, count, parameters));
521         }
522         
523         /// <summary>
524         ///   This query-builder method creates a new query whose results are the
525         ///   first 'count' results of this query.
526         /// </summary>
527         /// <param name="count">
528         ///   Specifies the number of results to return. This must be either a constant or
529         ///   a parameter reference.
530         /// </param>
531         /// <param name="parameters">
532         ///   An optional set of query parameters that should be in scope when parsing.
533         /// </param>
534         /// <returns>
535         ///   a new ObjectQuery instance.
536         /// </returns>
537         /// <exception cref="ArgumentNullException">
538         ///   If the top count command text is null.
539         /// </exception>
540         /// <exception cref="ArgumentException">
541         ///   If the top count command text is empty.
542         /// </exception>
543         public ObjectQuery<T> Top (string count, params ObjectParameter[] parameters)
544         {
545             EntityUtil.CheckArgumentNull(count, "count");
546
547             if (StringUtil.IsNullOrEmptyOrWhiteSpace(count))
548             {
549                 throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectQuery_QueryBuilder_InvalidTopCount, "count");
550             }
551
552             return new ObjectQuery<T>(EntitySqlQueryBuilder.Top(this.QueryState, this.Name, count, parameters));
553         }
554
555         /// <summary>
556         ///   This query-builder method creates a new query whose results are all of
557         ///   the results of this query, plus all of the results of the other query,
558         ///   without duplicates (i.e., results are unique).
559         /// </summary>
560         /// <param name="query">
561         ///   A query representing the results to add.
562         /// </param>
563         /// <returns>
564         ///   a new ObjectQuery instance.
565         /// </returns>
566         /// <exception cref="ArgumentNullException">
567         ///   If the query parameter is null.
568         /// </exception>
569         public ObjectQuery<T> Union (ObjectQuery<T> query)
570         {
571             EntityUtil.CheckArgumentNull(query, "query");
572
573             if (IsLinqQuery(this) || IsLinqQuery(query))
574             {
575                 return (ObjectQuery<T>)Queryable.Union(this, query);
576             }
577             return new ObjectQuery<T>(EntitySqlQueryBuilder.Union(this.QueryState, query.QueryState));
578         }
579         
580         /// <summary>
581         ///   This query-builder method creates a new query whose results are all of
582         ///   the results of this query, plus all of the results of the other query,
583         ///   including any duplicates (i.e., results are not necessarily unique).
584         /// </summary>
585         /// <param name="query">
586         ///   A query representing the results to add.
587         /// </param>
588         /// <returns>
589         ///   a new ObjectQuery instance.
590         /// </returns>
591         /// <exception cref="ArgumentNullException">
592         ///   If the query parameter is null.
593         /// </exception>
594         public ObjectQuery<T> UnionAll (ObjectQuery<T> query)
595         {
596             EntityUtil.CheckArgumentNull(query, "query");
597             
598             return new ObjectQuery<T>(EntitySqlQueryBuilder.UnionAll(this.QueryState, query.QueryState));
599         }
600         
601         /// <summary>
602         ///   This query-builder method creates a new query whose results are the
603         ///   results of this query filtered by some criteria.
604         /// </summary>
605         /// <param name="predicate">
606         ///   The filter predicate.
607         /// </param>
608         /// <param name="parameters">
609         ///   An optional set of query parameters that should be in scope when parsing.
610         /// </param>
611         /// <returns>
612         ///   a new ObjectQuery instance.
613         /// </returns>
614         /// <exception cref="ArgumentNullException">
615         ///   If either argument is null.
616         /// </exception>
617         /// <exception cref="ArgumentException">
618         ///   If the filter predicate command text is empty.
619         /// </exception>
620         public ObjectQuery<T> Where (string predicate, params ObjectParameter[] parameters)
621         {
622             EntityUtil.CheckArgumentNull(predicate, "predicate");
623             EntityUtil.CheckArgumentNull(parameters, "parameters");
624             
625             if (StringUtil.IsNullOrEmptyOrWhiteSpace(predicate))
626             {
627                 throw EntityUtil.Argument(System.Data.Entity.Strings.ObjectQuery_QueryBuilder_InvalidFilterPredicate, "predicate");
628             }
629
630             return new ObjectQuery<T>(EntitySqlQueryBuilder.Where(this.QueryState, this.Name, predicate, parameters));
631         }
632
633         #endregion        
634     }
635 }