Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / CommandTrees / ExpressionBuilder / Internal / ArgumentValidation.cs
1 //---------------------------------------------------------------------
2 // <copyright file="ArgumentValidation.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.Common.CommandTrees.ExpressionBuilder.Internal
11 {
12     using System.Collections.Generic;
13     using System.Data.Common;
14     using System.Data.Common.CommandTrees;
15     using System.Data.Common.CommandTrees.Internal;
16     using System.Data.Common.Utils;
17     using System.Data.Metadata.Edm; // for TypeHelpers
18     using System.Diagnostics;
19     using System.Globalization;
20     using System.Linq;
21
22     internal static class ArgumentValidation
23     {
24         private static TypeUsage _booleanType = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(PrimitiveTypeKind.Boolean);
25
26         // The Metadata ReadOnlyCollection class conflicts with System.Collections.ObjectModel.ReadOnlyCollection...
27         internal static System.Collections.ObjectModel.ReadOnlyCollection<TElement> NewReadOnlyCollection<TElement>(IList<TElement> list)
28         {
29             return new System.Collections.ObjectModel.ReadOnlyCollection<TElement>(list);
30         }
31
32         private static void RequirePolymorphicType(TypeUsage type, string typeArgumentName)
33         {
34             Debug.Assert(type != null, "Ensure type is non-null before calling RequirePolymorphicType");
35
36             if (!TypeSemantics.IsPolymorphicType(type))
37             {
38                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicTypeRequired(TypeHelpers.GetFullName(type)), "type");
39             } 
40         }
41
42         private static void RequireCompatibleType(DbExpression expression, TypeUsage requiredResultType, string argumentName)
43         {
44             RequireCompatibleType(expression, requiredResultType, argumentName, -1);
45         }
46
47         private static void RequireCompatibleType(DbExpression expression, TypeUsage requiredResultType, string argumentName, int argumentIndex)
48         {
49             Debug.Assert(expression != null, "Ensure expression is non-null before checking for type compatibility");
50             Debug.Assert(requiredResultType != null, "Ensure type is non-null before checking for type compatibility");
51
52             if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(expression.ResultType, requiredResultType))
53             {
54                 // Don't call FormatIndex unless an exception is actually being thrown
55                 if (argumentIndex != -1)
56                 {
57                     argumentName = StringUtil.FormatIndex(argumentName, argumentIndex);
58                 }
59
60                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_ExpressionLink_TypeMismatch(
61                         TypeHelpers.GetFullName(expression.ResultType),
62                         TypeHelpers.GetFullName(requiredResultType)
63                     ),
64                     argumentName
65                 );
66             }
67         }
68
69         private static void RequireCompatibleType(DbExpression expression, PrimitiveTypeKind requiredResultType, string argumentName)
70         {
71             RequireCompatibleType(expression, requiredResultType, argumentName, -1);
72         }
73
74         private static void RequireCompatibleType(DbExpression expression, PrimitiveTypeKind requiredResultType, string argumentName, int index)
75         {
76             Debug.Assert(expression != null, "Ensure expression is non-null before checking for type compatibility");
77             
78             PrimitiveTypeKind valueTypeKind;
79             bool valueIsPrimitive = TypeHelpers.TryGetPrimitiveTypeKind(expression.ResultType, out valueTypeKind);
80             if (!valueIsPrimitive ||
81                 valueTypeKind != requiredResultType)
82             {
83                 if (index != -1)
84                 {
85                     argumentName = StringUtil.FormatIndex(argumentName, index);
86                 }
87
88                 throw EntityUtil.Argument(
89                     System.Data.Entity.Strings.Cqt_ExpressionLink_TypeMismatch(
90                         (valueIsPrimitive ?
91                           Enum.GetName(typeof(PrimitiveTypeKind), valueTypeKind)
92                         : TypeHelpers.GetFullName(expression.ResultType)),
93                         Enum.GetName(typeof(PrimitiveTypeKind), requiredResultType)
94                     ),
95                     argumentName
96                 );
97             }
98         }
99
100         private static void RequireCompatibleType(DbExpression from, RelationshipEndMember end, bool allowAllRelationshipsInSameTypeHierarchy)
101         {
102             Debug.Assert(from != null, "Ensure navigation source expression is non-null before calling RequireCompatibleType");
103             Debug.Assert(end != null, "Ensure navigation start end is non-null before calling RequireCompatibleType");
104             
105             TypeUsage endType = end.TypeUsage;
106             if (!TypeSemantics.IsReferenceType(endType))
107             {
108                 //
109                 // The only relation end that is currently allowed to have a non-Reference type is the Child end of
110                 // a composition, in which case the end type must be an entity type. 
111                 //
112                 // Debug.Assert(end.Relation.IsComposition && !end.IsParent && (end.Type is EntityType), "Relation end can only have non-Reference type if it is a Composition child end");
113
114                 endType = TypeHelpers.CreateReferenceTypeUsage(TypeHelpers.GetEdmType<EntityType>(endType));
115             }
116
117             if (allowAllRelationshipsInSameTypeHierarchy)
118             {
119                 if (TypeHelpers.GetCommonTypeUsage(endType, from.ResultType) == null)
120                 {
121                     throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_RelNav_WrongSourceType(TypeHelpers.GetFullName(endType)), "from");
122                 }
123             }
124             else if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(from.ResultType.EdmType, endType.EdmType))
125             {
126                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_RelNav_WrongSourceType(TypeHelpers.GetFullName(endType)), "from");
127             }
128         }
129
130         private static void RequireCollectionArgument<TExpressionType>(DbExpression argument)
131         {
132             Debug.Assert(argument != null, "Validate argument is non-null before calling CheckCollectionArgument");
133
134             if (!TypeSemantics.IsCollectionType(argument.ResultType))
135             {
136                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Unary_CollectionRequired(typeof(TExpressionType).Name), "argument");
137             }
138         }
139
140         private static TypeUsage RequireCollectionArguments<TExpressionType>(DbExpression left, DbExpression right)
141         {
142             Debug.Assert(left != null && right != null, "Ensure left and right are non-null before calling RequireCollectionArguments");
143
144             if (!TypeSemantics.IsCollectionType(left.ResultType) || !TypeSemantics.IsCollectionType(right.ResultType))
145             {
146                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binary_CollectionsRequired(typeof(TExpressionType).Name));
147             }
148
149             TypeUsage commonType = TypeHelpers.GetCommonTypeUsage(left.ResultType, right.ResultType);
150             if (null == commonType)
151             {
152                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binary_CollectionsRequired(typeof(TExpressionType).Name));
153             }
154
155             return commonType;
156         }
157
158         private static TypeUsage RequireComparableCollectionArguments<TExpressionType>(DbExpression left, DbExpression right)
159         {
160             TypeUsage resultType = RequireCollectionArguments<TExpressionType>(left, right);
161
162             if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(left.ResultType)))
163             {
164                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_InvalidTypeForSetOperation(TypeHelpers.GetElementTypeUsage(left.ResultType).Identity, typeof(TExpressionType).Name), "left");
165             }
166
167             if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(right.ResultType)))
168             {
169                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_InvalidTypeForSetOperation(TypeHelpers.GetElementTypeUsage(right.ResultType).Identity, typeof(TExpressionType).Name), "right");
170             }
171
172             return resultType;
173         }
174
175         private static EnumerableValidator<TElementIn, TElementOut, TResult> CreateValidator<TElementIn, TElementOut, TResult>(IEnumerable<TElementIn> argument, string argumentName, Func<TElementIn, int, TElementOut> convertElement, Func<List<TElementOut>, TResult> createResult)
176         {
177             EnumerableValidator<TElementIn, TElementOut, TResult> ret = new EnumerableValidator<TElementIn, TElementOut, TResult>(argument, argumentName);
178             ret.ConvertElement = convertElement;
179             ret.CreateResult = createResult;
180             return ret;
181         }
182
183         private static DbExpressionList CreateExpressionList(IEnumerable<DbExpression> arguments, string argumentName, Action<DbExpression, int> validationCallback)
184         {
185             return CreateExpressionList(arguments, argumentName, false, validationCallback);
186         }
187     
188         private static DbExpressionList CreateExpressionList(IEnumerable<DbExpression> arguments, string argumentName, bool allowEmpty, Action<DbExpression, int> validationCallback)
189         {
190             var ev = CreateValidator(arguments, argumentName,
191                 (exp, idx) => 
192                 {
193                     if (validationCallback != null)
194                     {
195                         validationCallback(exp, idx);
196                     }
197                     return exp;
198                 },
199                 expList => new DbExpressionList(expList)
200             );
201
202             ev.AllowEmpty = allowEmpty;
203
204             return ev.Validate();
205         }
206
207         private static DbExpressionList CreateExpressionList(IEnumerable<DbExpression> arguments, string argumentName, int expectedElementCount, Action<DbExpression, int> validationCallback)
208         {
209             var ev = CreateValidator(arguments, argumentName,
210                 (exp, idx) =>
211                 {
212                     if (validationCallback != null)
213                     {
214                         validationCallback(exp, idx);
215                     }
216                     return exp;
217                 },
218                 (expList) => new DbExpressionList(expList)
219             );
220             
221             ev.ExpectedElementCount = expectedElementCount;
222             ev.AllowEmpty = false;
223             
224             return ev.Validate();
225         }
226
227         private static TypeUsage ValidateBinary(DbExpression left, DbExpression right)
228         {
229             EntityUtil.CheckArgumentNull(left, "left");
230             EntityUtil.CheckArgumentNull(right, "right");
231
232             return TypeHelpers.GetCommonTypeUsage(left.ResultType, right.ResultType);
233         }
234
235         private static void ValidateUnary(DbExpression argument)
236         {
237             EntityUtil.CheckArgumentNull(argument, "argument");
238         }
239
240         private static void ValidateTypeUnary(DbExpression argument, TypeUsage type, string typeArgumentName)
241         {
242             ValidateUnary(argument);
243             CheckType(type, typeArgumentName);
244         }
245
246         #region Bindings - Expression and Group
247
248         internal static TypeUsage ValidateBindAs(DbExpression input, string varName)
249         {
250             //
251             // Ensure no argument is null
252             //
253             EntityUtil.CheckArgumentNull(varName, "varName");
254             EntityUtil.CheckArgumentNull(input, "input");
255
256             //
257             // Ensure Variable name is non-empty
258             //
259             if (string.IsNullOrEmpty(varName))
260             {
261                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_VariableNameNotValid, "varName");
262             }
263
264             //
265             // Ensure the DbExpression has a collection result type
266             //
267             TypeUsage elementType = null;
268             if (!TypeHelpers.TryGetCollectionElementType(input.ResultType, out elementType))
269             {
270                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_CollectionRequired, "input");
271             }
272
273             Debug.Assert(elementType.IsReadOnly, "DbExpressionBinding Expression ResultType has editable element type");
274
275             return elementType;
276         }
277                 
278         internal static TypeUsage ValidateGroupBindAs(DbExpression input, string varName, string groupVarName)
279         {
280             //
281             // Ensure no argument is null
282             //
283             EntityUtil.CheckArgumentNull(varName, "varName");
284             EntityUtil.CheckArgumentNull(groupVarName, "groupVarName");
285             EntityUtil.CheckArgumentNull(input, "input");
286
287             //
288             // Ensure Variable and Group names are both non-empty
289             //
290             if (string.IsNullOrEmpty(varName))
291             {
292                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_VariableNameNotValid, "varName");
293             }
294
295             if (string.IsNullOrEmpty(groupVarName))
296             {
297                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBinding_GroupVariableNameNotValid, "groupVarName");
298             }
299
300             //
301             // Ensure the DbExpression has a collection result type
302             //
303             TypeUsage elementType = null;
304             if (!TypeHelpers.TryGetCollectionElementType(input.ResultType, out elementType))
305             {
306                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBinding_CollectionRequired, "input");
307             }
308
309             Debug.Assert((elementType.IsReadOnly), "DbGroupExpressionBinding Expression ResultType has editable element type");
310             
311             return elementType;
312         }
313
314         #endregion
315
316         #region Aggregates and Sort Keys
317
318         private static FunctionParameter[] GetExpectedParameters(EdmFunction function)
319         {
320             Debug.Assert(function != null, "Ensure function is non-null before calling GetExpectedParameters");
321             return function.Parameters.Where(p => p.Mode == ParameterMode.In || p.Mode == ParameterMode.InOut).ToArray();
322         }
323
324         internal static DbExpressionList ValidateFunctionAggregate(EdmFunction function, IEnumerable<DbExpression> args)
325         {
326             //
327             // Verify that the aggregate function is from the metadata collection and data space of the command tree.
328             //
329             ArgumentValidation.CheckFunction(function);
330
331             // Verify that the function is actually a valid aggregate function.
332             // For now, only a single argument is allowed.
333             if (!TypeSemantics.IsAggregateFunction(function) || null == function.ReturnParameter)
334             {
335                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Aggregate_InvalidFunction, "function");
336             }
337
338             FunctionParameter[] expectedParams = GetExpectedParameters(function);
339             DbExpressionList funcArgs = CreateExpressionList(args, "argument", expectedParams.Length, (exp, idx) =>
340                 {
341                     TypeUsage paramType = expectedParams[idx].TypeUsage;
342                     TypeUsage elementType = null;
343                     if (TypeHelpers.TryGetCollectionElementType(paramType, out elementType))
344                     {
345                         paramType = elementType;
346                     }
347
348                     ArgumentValidation.RequireCompatibleType(exp, paramType, "argument");
349                 }
350             );
351
352             return funcArgs;
353         }
354
355         internal static DbExpressionList ValidateGroupAggregate(DbExpression argument)
356         {
357             EntityUtil.CheckArgumentNull(argument, "argument");
358             return new DbExpressionList(new[] { argument });
359         }
360
361         internal static void ValidateSortClause(DbExpression key)
362         {
363             EntityUtil.CheckArgumentNull(key, "key");
364
365             if (!TypeHelpers.IsValidSortOpKeyType(key.ResultType))
366             {
367                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Sort_OrderComparable, "key");
368             }
369         }
370
371         internal static void ValidateSortClause(DbExpression key, string collation)
372         {
373             ValidateSortClause(key);
374
375             EntityUtil.CheckArgumentNull(collation, "collation");
376             if (StringUtil.IsNullOrEmptyOrWhiteSpace(collation))
377             {
378                 throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Sort_EmptyCollationInvalid, "collation");
379             }
380
381             if (!TypeSemantics.IsPrimitiveType(key.ResultType, PrimitiveTypeKind.String))
382             {
383                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Sort_NonStringCollationInvalid, "collation");
384             }
385         }
386                 
387         #endregion
388
389         #region DbLambda
390
391         internal static System.Collections.ObjectModel.ReadOnlyCollection<DbVariableReferenceExpression> ValidateLambda(IEnumerable<DbVariableReferenceExpression> variables, DbExpression body)
392         {
393             EntityUtil.CheckArgumentNull(body, "body");
394             
395             var varVal = CreateValidator(variables, "variables",
396                 (varExp, idx) =>
397                 {
398                     if (null == varExp)
399                     {
400                         throw EntityUtil.ArgumentNull(StringUtil.FormatIndex("variables", idx));
401                     }
402                     return varExp;
403                 },
404                 (varList) => new System.Collections.ObjectModel.ReadOnlyCollection<DbVariableReferenceExpression>(varList)
405             );
406             varVal.AllowEmpty = true;
407             varVal.GetName = (varDef, idx) => varDef.VariableName;
408
409             var result = varVal.Validate();
410             return result;
411         }
412
413         #endregion
414
415         #region Binding-based methods: All, Any, Cross|OuterApply, Cross|FullOuter|Inner|LeftOuterJoin, Filter, GroupBy, Project, Skip, Sort
416
417         private static void ValidateBinding(DbExpressionBinding binding, string argumentName)
418         {
419             EntityUtil.CheckArgumentNull(binding, argumentName);
420         }
421
422         private static void ValidateGroupBinding(DbGroupExpressionBinding binding, string argumentName)
423         {
424             EntityUtil.CheckArgumentNull(binding, argumentName);
425         }
426
427         private static void ValidateBound(DbExpressionBinding input, DbExpression argument, string argumentName)
428         {
429             ValidateBinding(input, "input");
430             EntityUtil.CheckArgumentNull(argument, argumentName);
431         }
432
433         internal static TypeUsage ValidateQuantifier(DbExpressionBinding input, DbExpression predicate)
434         {
435             ValidateBound(input, predicate, "predicate");
436             RequireCompatibleType(predicate, PrimitiveTypeKind.Boolean, "predicate");
437
438             return predicate.ResultType;
439         }
440
441         internal static TypeUsage ValidateApply(DbExpressionBinding input, DbExpressionBinding apply)
442         {
443             ValidateBinding(input, "input");
444             ValidateBinding(apply, "apply");
445
446             //
447             // Duplicate Input and Apply binding names are not allowed
448             //
449             if (input.VariableName.Equals(apply.VariableName, StringComparison.Ordinal))
450             {
451                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Apply_DuplicateVariableNames);
452             }
453
454             //
455             // Initialize the result type
456             //
457             List<KeyValuePair<string, TypeUsage>> recordCols = new List<KeyValuePair<string, TypeUsage>>();
458             recordCols.Add(new KeyValuePair<string, TypeUsage>(input.VariableName, input.VariableType));
459             recordCols.Add(new KeyValuePair<string, TypeUsage>(apply.VariableName, apply.VariableType));
460
461             return CreateCollectionOfRowResultType(recordCols);
462         }
463                 
464         internal static System.Collections.ObjectModel.ReadOnlyCollection<DbExpressionBinding> ValidateCrossJoin(IEnumerable<DbExpressionBinding> inputs, out TypeUsage resultType)
465         {
466             //
467             // Ensure that the list of input expression bindings is non-null.
468             //
469             EntityUtil.CheckArgumentNull(inputs, "inputs");
470
471             //
472             // Validate the input expression bindings and build the column types for the record type
473             // that will be the element type of the collection of record type result type of the join.
474             //
475             List<DbExpressionBinding> inputList = new List<DbExpressionBinding>();
476             List<KeyValuePair<string, TypeUsage>> columns = new List<KeyValuePair<string, TypeUsage>>();
477             Dictionary<string, int> bindingNames = new Dictionary<string, int>();
478             IEnumerator<DbExpressionBinding> inputEnum = inputs.GetEnumerator();
479             int iPos = 0;
480             while (inputEnum.MoveNext())
481             {
482                 DbExpressionBinding input = inputEnum.Current;
483
484                 //
485                 // Validate the DbExpressionBinding before accessing its properties
486                 //
487                 ValidateBinding(input, StringUtil.FormatIndex("inputs", iPos));
488
489                 //
490                 // Duplicate binding names are not allowed
491                 //
492                 int nameIndex = -1;
493                 if (bindingNames.TryGetValue(input.VariableName, out nameIndex))
494                 {
495                     throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_CrossJoin_DuplicateVariableNames(nameIndex, iPos, input.VariableName));
496                 }
497
498                 inputList.Add(input);
499                 bindingNames.Add(input.VariableName, iPos);
500
501                 columns.Add(new KeyValuePair<string, TypeUsage>(input.VariableName, input.VariableType));
502
503                 iPos++;
504             }
505
506             if (inputList.Count < 2)
507             {
508                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_CrossJoin_AtLeastTwoInputs, "inputs");
509             }
510
511             //
512             // Initialize the result type
513             //
514             resultType = CreateCollectionOfRowResultType(columns);
515
516             //
517             // Initialize state
518             //
519             return inputList.AsReadOnly();
520         }
521
522         internal static TypeUsage ValidateJoin(DbExpressionBinding left, DbExpressionBinding right, DbExpression joinCondition)
523         {
524             //
525             // Validate
526             //
527             ValidateBinding(left, "left");
528             ValidateBinding(left, "right");
529             EntityUtil.CheckArgumentNull(joinCondition, "joinCondition");
530
531             //
532             // Duplicate Left and Right binding names are not allowed
533             //
534             if (left.VariableName.Equals(right.VariableName, StringComparison.Ordinal))
535             {
536                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Join_DuplicateVariableNames);
537             }
538
539             //
540             // Validate the JoinCondition)
541             //
542             RequireCompatibleType(joinCondition, PrimitiveTypeKind.Boolean, "joinCondition");
543             
544             //
545             // Initialize the result type
546             //
547             List<KeyValuePair<string, TypeUsage>> columns = new List<KeyValuePair<string, TypeUsage>>(2);
548             columns.Add(new KeyValuePair<string, TypeUsage>(left.VariableName, left.VariableType));
549             columns.Add(new KeyValuePair<string, TypeUsage>(right.VariableName, right.VariableType));
550
551             return CreateCollectionOfRowResultType(columns);
552         }
553
554         internal static TypeUsage ValidateFilter(DbExpressionBinding input, DbExpression predicate)
555         {
556             ValidateBound(input, predicate, "predicate");
557             RequireCompatibleType(predicate, PrimitiveTypeKind.Boolean, "predicate");
558             return input.Expression.ResultType;
559         }
560         
561         internal static TypeUsage ValidateGroupBy(DbGroupExpressionBinding input, IEnumerable<KeyValuePair<string, DbExpression>> keys, IEnumerable<KeyValuePair<string, DbAggregate>> aggregates, out DbExpressionList validKeys, out System.Collections.ObjectModel.ReadOnlyCollection<DbAggregate> validAggregates)
562         {
563             //
564             // Validate the input set
565             //
566             ValidateGroupBinding(input, "input");
567
568             //
569             // Track the cumulative set of column names and types, as well as key column names
570             //
571             List<KeyValuePair<string, TypeUsage>> columns = new List<KeyValuePair<string, TypeUsage>>();
572             HashSet<string> keyNames = new HashSet<string>();
573
574             //
575             // Validate the grouping keys
576             //
577             var keyValidator = CreateValidator(keys, "keys",
578                 (keyInfo, index) =>
579                 {
580                     ArgumentValidation.CheckNamed(keyInfo, "keys", index);
581
582                     //
583                     // The result Type of an expression used as a group key must be equality comparable
584                     //
585                     if (!TypeHelpers.IsValidGroupKeyType(keyInfo.Value.ResultType))
586                     {
587                         throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_KeyNotEqualityComparable(keyInfo.Key));
588                     }
589
590                     keyNames.Add(keyInfo.Key);
591                     columns.Add(new KeyValuePair<string, TypeUsage>(keyInfo.Key, keyInfo.Value.ResultType));
592
593                     return keyInfo.Value;
594                 },
595                 expList => new DbExpressionList(expList)
596             );
597             keyValidator.AllowEmpty = true;
598             keyValidator.GetName = (keyInfo, idx) => keyInfo.Key;
599             validKeys = keyValidator.Validate();
600
601             bool hasGroupAggregate = false;
602             var aggValidator = CreateValidator(aggregates, "aggregates",
603                 (aggInfo, idx) =>
604                 {
605                     ArgumentValidation.CheckNamed(aggInfo, "aggregates", idx);
606                                         
607                     //
608                     // Is there a grouping key with the same name?
609                     //
610                     if (keyNames.Contains(aggInfo.Key))
611                     {
612                         throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_AggregateColumnExistsAsGroupColumn(aggInfo.Key));
613                     }
614
615                     //
616                     // At most one group aggregate can be specified
617                     //
618                     if (aggInfo.Value is DbGroupAggregate)
619                     {
620                         if (hasGroupAggregate)
621                         {
622                             throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_MoreThanOneGroupAggregate);
623                         }
624                         else
625                         {
626                             hasGroupAggregate = true;
627                         }
628                     }
629
630                     columns.Add(new KeyValuePair<string, TypeUsage>(aggInfo.Key, aggInfo.Value.ResultType));
631                     return aggInfo.Value;
632                 },
633                 aggList => NewReadOnlyCollection(aggList)
634             );
635             aggValidator.AllowEmpty = true;
636             aggValidator.GetName = (aggInfo, idx) => aggInfo.Key;
637             validAggregates = aggValidator.Validate();
638
639             //
640             // Either the Keys or Aggregates may be omitted, but not both
641             //
642             if (0 == validKeys.Count && 0 == validAggregates.Count)
643             {
644                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_AtLeastOneKeyOrAggregate);
645             }
646                         
647             //
648             // Create the result type. This is a collection of the record type produced by the group keys and aggregates.
649             //
650             return CreateCollectionOfRowResultType(columns);
651         }
652
653         internal static TypeUsage ValidateProject(DbExpressionBinding input, DbExpression projection)
654         {
655             ValidateBound(input, projection, "projection");
656             return  CreateCollectionResultType(projection.ResultType);
657         }
658
659         /// <summary>
660         /// Validates the input and sort key arguments to both DbSkipExpression and DbSortExpression.
661         /// </summary>
662         /// <param name="input">A DbExpressionBinding that provides the collection to be ordered</param>
663         /// <param name="keys">A list of SortClauses that specifies the sort order to apply to the input collection</param>
664         private static System.Collections.ObjectModel.ReadOnlyCollection<DbSortClause> ValidateSortArguments(DbExpressionBinding input, IEnumerable<DbSortClause> sortOrder)
665         {
666             ValidateBinding(input, "input");
667
668             var ev = CreateValidator(sortOrder, "sortOrder",
669                 (key, idx) => key,
670                 keyList => NewReadOnlyCollection(keyList)
671             );
672             ev.AllowEmpty = false;
673             return ev.Validate();
674         }
675     
676         internal static System.Collections.ObjectModel.ReadOnlyCollection<DbSortClause> ValidateSkip(DbExpressionBinding input, IEnumerable<DbSortClause> sortOrder, DbExpression count)
677         {
678             //
679             // Validate the input expression binding and sort keys
680             //
681             var sortKeys = ValidateSortArguments(input, sortOrder);
682
683             //
684             // Initialize the Count ExpressionLink. In addition to being non-null and from the same command tree,
685             // the Count expression must also have an integer result type.
686             //
687             EntityUtil.CheckArgumentNull(count, "count");
688             if (!TypeSemantics.IsIntegerNumericType(count.ResultType))
689             {
690                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Skip_IntegerRequired, "count");
691             }
692
693             //
694             // Currently the Count expression is also required to be either a DbConstantExpression or a DbParameterReferenceExpression.
695             //
696             if (count.ExpressionKind != DbExpressionKind.Constant &&
697                 count.ExpressionKind != DbExpressionKind.ParameterReference)
698             {
699                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Skip_ConstantOrParameterRefRequired, "count");
700             }
701
702             //
703             // For constants, verify the count is non-negative.
704             //
705             if (ArgumentValidation.IsConstantNegativeInteger(count))
706             {
707                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Skip_NonNegativeCountRequired, "count");
708             }
709
710             return sortKeys;
711         }
712
713         internal static System.Collections.ObjectModel.ReadOnlyCollection<DbSortClause> ValidateSort(DbExpressionBinding input, IEnumerable<DbSortClause> sortOrder)
714         {
715             //
716             // Validate the input expression binding and sort keys
717             //
718             return ValidateSortArguments(input, sortOrder);
719         }
720
721         #endregion
722
723         #region Leaf Expressions - Null, Constant, Parameter, Scan
724         
725         internal static void ValidateNull(TypeUsage nullType)
726         {
727             CheckType(nullType, "nullType");
728         }
729
730         internal static TypeUsage ValidateConstant(object value)
731         {
732             EntityUtil.CheckArgumentNull(value, "value");
733
734             //
735             // Check that typeof(value) is actually a valid constant (i.e. primitive) type
736             //
737             PrimitiveTypeKind primitiveTypeKind;
738             if (!ArgumentValidation.TryGetPrimitiveTypeKind(value.GetType(), out primitiveTypeKind))
739             {
740                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Constant_InvalidType, "value");
741             }
742
743             return TypeHelpers.GetLiteralTypeUsage(primitiveTypeKind);
744         }
745
746         internal static void ValidateConstant(TypeUsage constantType, object value)
747         {
748             //
749             // Basic validation of constant value and constant type (non-null, read-only, etc)
750             //
751             EntityUtil.CheckArgumentNull(value, "value");
752             ArgumentValidation.CheckType(constantType, "constantType");
753
754             //
755             // Verify that constantType is a primitive or enum type and that the value is an instance of that type
756             // Note that the value is not validated against applicable facets (such as MaxLength for a string value),
757             // this is left to the server.
758             //
759             EnumType edmEnumType;
760             if(TypeHelpers.TryGetEdmType<EnumType>(constantType, out edmEnumType))
761             {
762                 var clrEnumUnderlyingType = edmEnumType.UnderlyingType.ClrEquivalentType;
763
764                 // type of the value has to match the edm enum type or underlying types have to be the same
765                  if((value.GetType().IsEnum || clrEnumUnderlyingType != value.GetType()) && !ClrEdmEnumTypesMatch(edmEnumType, value.GetType()))
766                  {
767                     throw EntityUtil.Argument(
768                         System.Data.Entity.Strings.Cqt_Constant_ClrEnumTypeDoesNotMatchEdmEnumType(
769                             value.GetType().Name,
770                             edmEnumType.Name,
771                             clrEnumUnderlyingType.Name),
772                         "value");
773                 }
774             }
775             else
776             {
777                 PrimitiveType primitiveType;
778                 if (!TypeHelpers.TryGetEdmType<PrimitiveType>(constantType, out primitiveType))
779                 {
780                     throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Constant_InvalidConstantType(constantType.ToString()), "constantType");
781                 }
782
783                 PrimitiveTypeKind valueKind;
784                 if (!ArgumentValidation.TryGetPrimitiveTypeKind(value.GetType(), out valueKind) ||
785                     primitiveType.PrimitiveTypeKind != valueKind)
786                 {
787                     // there are only two O-space types for the 16 C-space spatial types.   Allow constants of any geography type to be represented as DbGeography, and
788                     // any geometric type to be represented by Dbgeometry.
789                     if (!(Helper.IsGeographicType(primitiveType) && valueKind == PrimitiveTypeKind.Geography)
790                         && !(Helper.IsGeometricType(primitiveType) && valueKind == PrimitiveTypeKind.Geometry))
791                     {
792                         throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Constant_InvalidValueForType(constantType.ToString()), "value");
793                     }
794                 }
795             }
796         }
797
798         internal static void ValidateParameter(TypeUsage type, string name)
799         {
800             ArgumentValidation.CheckType(type);
801
802             EntityUtil.CheckArgumentNull(name, "name");
803             if (!DbCommandTree.IsValidParameterName(name))
804             {
805                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_CommandTree_InvalidParameterName(name), "name");
806             }            
807         }
808
809         internal static TypeUsage ValidateScan(EntitySetBase entitySet)
810         {
811             ArgumentValidation.CheckEntitySet(entitySet, "targetSet");
812             return ArgumentValidation.CreateCollectionResultType(entitySet.ElementType);
813         }
814
815         internal static void ValidateVariable(TypeUsage type, string name)
816         {
817             CheckType(type);
818             
819             EntityUtil.CheckArgumentNull(name, "name");
820             if (string.IsNullOrEmpty(name))
821             {
822                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_VariableNameNotValid, "name");
823             }
824         }
825
826         #endregion
827
828         #region Boolean Operators - And, Or, Not
829
830         internal static TypeUsage ValidateAnd(DbExpression left, DbExpression right)
831         {
832             TypeUsage resultType = ValidateBinary(left, right);
833             if (null == resultType || !TypeSemantics.IsPrimitiveType(resultType, PrimitiveTypeKind.Boolean))
834             {
835                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_And_BooleanArgumentsRequired);
836             }
837
838             return resultType;
839         }
840
841         internal static TypeUsage ValidateOr(DbExpression left, DbExpression right)
842         {
843             TypeUsage resultType = ValidateBinary(left, right);
844             if (null == resultType || !TypeSemantics.IsPrimitiveType(resultType, PrimitiveTypeKind.Boolean))
845             {
846                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Or_BooleanArgumentsRequired);
847             }
848
849             return resultType;
850         }
851
852         internal static TypeUsage ValidateNot(DbExpression argument)
853         {
854             EntityUtil.CheckArgumentNull(argument, "argument");
855
856             //
857             // Argument to Not must have Boolean result type
858             //
859             if (!TypeSemantics.IsPrimitiveType(argument.ResultType, PrimitiveTypeKind.Boolean))
860             {
861                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Not_BooleanArgumentRequired);
862             }
863
864             return argument.ResultType;
865         }
866
867         #endregion
868
869         #region Arithmetic Operators
870
871         internal static DbExpressionList ValidateArithmetic(DbExpression argument, out TypeUsage resultType)
872         {
873             ValidateUnary(argument);
874             resultType = argument.ResultType;
875             if (!TypeSemantics.IsNumericType(resultType))
876             {
877                 // 
878                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Arithmetic_NumericCommonType);
879             }
880             //If argument to UnaryMinus is an unsigned type, promote return type to next higher, signed type.
881             if (TypeSemantics.IsUnsignedNumericType(argument.ResultType))
882             {
883                 TypeUsage closestPromotableType = null;
884                 if (TypeHelpers.TryGetClosestPromotableType(argument.ResultType, out closestPromotableType))
885                 {
886                     resultType = closestPromotableType;
887                 }
888                 else
889                 {
890                     throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Arithmetic_InvalidUnsignedTypeForUnaryMinus(argument.ResultType.EdmType.FullName));
891                 }
892             }
893             return new DbExpressionList(new[] { argument });
894         }
895
896         internal static DbExpressionList ValidateArithmetic(DbExpression left, DbExpression right, out TypeUsage resultType)
897         {
898             resultType = ValidateBinary(left, right);
899             if (null == resultType || !TypeSemantics.IsNumericType(resultType))
900             {
901                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Arithmetic_NumericCommonType);
902             }
903
904             return new DbExpressionList(new[] { left, right });
905         }
906
907         #endregion
908
909         #region Comparison
910         
911         internal  static TypeUsage ValidateComparison(DbExpressionKind kind, DbExpression left, DbExpression right)
912         {
913             EntityUtil.CheckArgumentNull(left, "left");
914             EntityUtil.CheckArgumentNull(right, "right");
915
916             //
917             // A comparison of the specified kind must exist between the left and right arguments
918             //
919             bool equality = true;
920             bool order = true;
921             if (DbExpressionKind.GreaterThanOrEquals == kind ||
922                 DbExpressionKind.LessThanOrEquals == kind)
923             {
924                 equality = TypeSemantics.IsEqualComparableTo(left.ResultType, right.ResultType);
925                 order = TypeSemantics.IsOrderComparableTo(left.ResultType, right.ResultType);
926             }
927             else if (DbExpressionKind.Equals == kind ||
928                      DbExpressionKind.NotEquals == kind)
929             {
930                 equality = TypeSemantics.IsEqualComparableTo(left.ResultType, right.ResultType);
931             }
932             else
933             {
934                 order = TypeSemantics.IsOrderComparableTo(left.ResultType, right.ResultType);
935             }
936
937             if (!equality || !order)
938             {
939                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Comparison_ComparableRequired);
940             }
941
942             return _booleanType;
943         }
944
945         internal static TypeUsage ValidateIsNull(DbExpression argument)
946         {
947             return ValidateIsNull(argument, false);
948         }
949
950         internal static TypeUsage ValidateIsNull(DbExpression argument, bool allowRowType)
951         {
952             EntityUtil.CheckArgumentNull(argument, "argument");
953
954             //
955             // The argument cannot be of a collection type
956             //
957             if (TypeSemantics.IsCollectionType(argument.ResultType))
958             {
959                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_IsNull_CollectionNotAllowed);
960             }
961
962             //
963             // ensure argument type is valid for this operation
964             //
965             if (!TypeHelpers.IsValidIsNullOpType(argument.ResultType))
966             {
967                 // 
968                 if (!allowRowType || !TypeSemantics.IsRowType(argument.ResultType))
969                 {
970                     throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_IsNull_InvalidType);
971                 }
972             }
973
974             return _booleanType;
975         }
976
977         internal static TypeUsage ValidateLike(DbExpression argument, DbExpression pattern)
978         {
979             EntityUtil.CheckArgumentNull(argument, "argument");
980             EntityUtil.CheckArgumentNull(pattern, "pattern");
981
982             RequireCompatibleType(argument, PrimitiveTypeKind.String, "argument");
983             RequireCompatibleType(pattern, PrimitiveTypeKind.String, "pattern");
984
985             return _booleanType;
986         }
987
988         internal static TypeUsage ValidateLike(DbExpression argument, DbExpression pattern, DbExpression escape)
989         {
990             TypeUsage resultType = ValidateLike(argument, pattern);
991
992             EntityUtil.CheckArgumentNull(escape, "escape");
993             RequireCompatibleType(escape, PrimitiveTypeKind.String, "escape");
994
995             return resultType;
996         }
997
998         #endregion
999
1000         #region Type Operators - Cast, Treat, OfType, OfTypeOnly, IsOf, IsOfOnly
1001
1002         internal static void ValidateCastTo(DbExpression argument, TypeUsage toType)
1003         {
1004             ValidateTypeUnary(argument, toType, "toType");
1005
1006             //
1007             // Verify that the cast is allowed
1008             //
1009             if (!TypeSemantics.IsCastAllowed(argument.ResultType, toType))
1010             {
1011                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Cast_InvalidCast(TypeHelpers.GetFullName(argument.ResultType), TypeHelpers.GetFullName(toType)));
1012             }
1013         }
1014
1015         internal static void ValidateTreatAs(DbExpression argument, TypeUsage asType)
1016         {
1017             ValidateTypeUnary(argument, asType, "asType");
1018
1019             //
1020             // Verify the type to treat as. Treat-As (NullType) is not allowed.
1021             //
1022             RequirePolymorphicType(asType, "asType");
1023
1024             //
1025             // Verify that the Treat operation is allowed
1026             //
1027             if (!TypeSemantics.IsValidPolymorphicCast(argument.ResultType, asType))
1028             {
1029                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicArgRequired(typeof(DbTreatExpression).Name));
1030             }
1031         }
1032
1033         internal static TypeUsage ValidateOfType(DbExpression argument, TypeUsage type)
1034         {
1035             ValidateTypeUnary(argument, type, "type");
1036
1037             //
1038             // Ensure that the type is non-null and valid - from the same metadata collection and dataspace and the command tree.
1039             // The type is also not allowed to be NullType.
1040             //
1041             RequirePolymorphicType(type, "type");
1042
1043             //
1044             // Ensure that the argument is actually of a collection type.
1045             //
1046             RequireCollectionArgument<DbOfTypeExpression>(argument);
1047
1048             //
1049             // Verify that the OfType operation is allowed
1050             //
1051             TypeUsage elementType = null;
1052             if (!TypeHelpers.TryGetCollectionElementType(argument.ResultType, out elementType) ||
1053                 !TypeSemantics.IsValidPolymorphicCast(elementType, type))
1054             {
1055                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicArgRequired(typeof(DbOfTypeExpression).Name));
1056             }
1057
1058             //
1059             // The type of this DbExpression is a new collection type based on the requested element type.
1060             //
1061             return CreateCollectionResultType(type);
1062         }
1063
1064         internal static TypeUsage ValidateIsOf(DbExpression argument, TypeUsage type)
1065         {
1066             ValidateTypeUnary(argument, type, "type");
1067
1068             //
1069             // Ensure the ofType is non-null, associated with the correct metadata workspace/dataspace,
1070             // is not NullType, and is polymorphic
1071             //
1072             RequirePolymorphicType(type, "type");
1073
1074             //
1075             // Verify that the IsOf operation is allowed
1076             //
1077             if (!TypeSemantics.IsValidPolymorphicCast(argument.ResultType, type))
1078             {
1079                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicArgRequired(typeof(DbIsOfExpression).Name));
1080             }
1081
1082             return _booleanType;
1083         }
1084
1085         #endregion
1086
1087         #region Ref Operators - Deref, EntityRef, Ref, RefKey, RelationshipNavigation
1088
1089         internal static TypeUsage ValidateDeref(DbExpression argument)
1090         {
1091             ValidateUnary(argument);
1092             
1093             //
1094             // Ensure that the operand is actually of a reference type.
1095             //
1096             EntityType entityType;
1097             if (!TypeHelpers.TryGetRefEntityType(argument.ResultType, out entityType))
1098             {
1099                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_DeRef_RefRequired, "argument");
1100             }
1101
1102             //
1103             // Result Type is the element type of the reference type
1104             //
1105             return CreateResultType(entityType);
1106         }
1107
1108         internal static TypeUsage ValidateGetEntityRef(DbExpression argument)
1109         {
1110             ValidateUnary(argument);
1111
1112             EntityType entityType = null;
1113             if (!TypeHelpers.TryGetEdmType<EntityType>(argument.ResultType, out entityType) || null == entityType)
1114             {
1115                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GetEntityRef_EntityRequired, "argument");
1116             }
1117
1118             return CreateReferenceResultType(entityType);
1119         }
1120
1121         internal static TypeUsage ValidateCreateRef(EntitySet entitySet, IEnumerable<DbExpression> keyValues, out DbExpression keyConstructor)
1122         {
1123             EntityUtil.CheckArgumentNull(entitySet, "entitySet");
1124             return ValidateCreateRef(entitySet, entitySet.ElementType, keyValues, out keyConstructor);
1125         }
1126
1127         internal static TypeUsage ValidateCreateRef(EntitySet entitySet, EntityType entityType, IEnumerable<DbExpression> keyValues, out DbExpression keyConstructor)
1128         {
1129             CheckEntitySet(entitySet, "entitySet");
1130             CheckType(entityType, "entityType");
1131
1132             //
1133             // Verify that the specified return type of the Ref operation is actually in
1134             // the same hierarchy as the Entity type of the specified Entity set.
1135             //
1136             if (!TypeSemantics.IsValidPolymorphicCast(entitySet.ElementType, entityType))
1137             {
1138                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Ref_PolymorphicArgRequired);
1139             }
1140
1141             // Validate the key values. The count of values must match the count of key members,
1142             // and each key value must have a result type that is compatible with the type of
1143             // the corresponding key member.
1144             IList<EdmMember> keyMembers = entityType.KeyMembers;
1145             var keyValueValidator = CreateValidator(keyValues, "keyValues",
1146                 (valueExp, idx) =>
1147                 {
1148                     RequireCompatibleType(valueExp, keyMembers[idx].TypeUsage, "keyValues", idx);
1149                     return new KeyValuePair<string, DbExpression>(keyMembers[idx].Name, valueExp);
1150                 },
1151                 (columnList) => columnList
1152             );
1153             keyValueValidator.ExpectedElementCount = keyMembers.Count;
1154             var keyColumns = keyValueValidator.Validate();
1155
1156             keyConstructor = DbExpressionBuilder.NewRow(keyColumns);
1157             return CreateReferenceResultType(entityType);
1158         }
1159         
1160         internal static TypeUsage ValidateRefFromKey(EntitySet entitySet, DbExpression keyValues)
1161         {
1162             EntityUtil.CheckArgumentNull(entitySet, "entitySet");
1163             return ValidateRefFromKey(entitySet, keyValues, entitySet.ElementType);
1164         }
1165
1166         internal static TypeUsage ValidateRefFromKey(EntitySet entitySet, DbExpression keyValues, EntityType entityType)
1167         {
1168             CheckEntitySet(entitySet, "entitySet");
1169             EntityUtil.CheckArgumentNull(keyValues, "keyValues");
1170             CheckType(entityType);
1171             
1172             //
1173             // Verify that the specified return type of the Ref operation is actually in
1174             // the same hierarchy as the Entity type of the specified Entity set.
1175             //
1176             if (!TypeSemantics.IsValidPolymorphicCast(entitySet.ElementType, entityType))
1177             {
1178                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Ref_PolymorphicArgRequired);
1179             }
1180
1181             //
1182             // The Argument DbExpression must construct a set of values of the same types as the Key members of the Entity
1183             // The names of the columns in the record type constructed by the Argument are not important, only that the
1184             // number of columns is the same as the number of Key members and that for each Key member the corresponding
1185             // column (based on order) is of a promotable type. 
1186             // To enforce this, the argument's result type is compared to a record type based on the names and types of
1187             // the Key members. Since the promotability check used in RequireCompatibleType will ignore the names of the
1188             // expected type's columns, RequireCompatibleType will therefore enforce the required level of type correctness
1189             //
1190             // Set the expected type to be the record type created based on the Key members
1191             //
1192             TypeUsage keyType = CreateResultType(TypeHelpers.CreateKeyRowType(entitySet.ElementType));
1193             RequireCompatibleType(keyValues, keyType, "keyValues");
1194                         
1195             return CreateReferenceResultType(entityType);
1196         }
1197
1198         internal static TypeUsage ValidateGetRefKey(DbExpression argument)
1199         {
1200             ValidateUnary(argument);
1201
1202             RefType refType = null;
1203             if (!TypeHelpers.TryGetEdmType<RefType>(argument.ResultType, out refType) || null == refType)
1204             {
1205                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GetRefKey_RefRequired, "argument");
1206             }
1207
1208             // RefType is responsible for basic validation of ElementType
1209             Debug.Assert(refType.ElementType != null, "RefType constructor allowed null ElementType?");
1210             
1211             return CreateResultType(TypeHelpers.CreateKeyRowType(refType.ElementType));
1212         }
1213
1214         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
1215         internal static TypeUsage ValidateNavigate(DbExpression navigateFrom, RelationshipType type, string fromEndName, string toEndName, out RelationshipEndMember fromEnd, out RelationshipEndMember toEnd)
1216         {
1217             EntityUtil.CheckArgumentNull(navigateFrom, "navigateFrom");
1218
1219             //
1220             // Ensure that the relation type is non-null and from the same metadata workspace as the command tree
1221             //
1222             CheckType(type);
1223
1224             //
1225             // Verify that the from and to relation end names are not null
1226             //
1227             EntityUtil.CheckArgumentNull(fromEndName, "fromEndName");
1228             EntityUtil.CheckArgumentNull(toEndName, "toEndName");
1229
1230             //
1231             // Retrieve the relation end properties with the specified 'from' and 'to' names
1232             //
1233             if (!type.RelationshipEndMembers.TryGetValue(fromEndName, false /*ignoreCase*/, out fromEnd))
1234             {
1235                 throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Factory_NoSuchRelationEnd, fromEndName);
1236             }
1237
1238             if (!type.RelationshipEndMembers.TryGetValue(toEndName, false /*ignoreCase*/, out toEnd))
1239             {
1240                 throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Factory_NoSuchRelationEnd, toEndName);
1241             }
1242
1243             //
1244             // Validate the retrieved relation end against the navigation source
1245             //
1246             RequireCompatibleType(navigateFrom, fromEnd, allowAllRelationshipsInSameTypeHierarchy: false);
1247
1248             return CreateResultType(toEnd);
1249         }
1250
1251         internal static TypeUsage ValidateNavigate(DbExpression navigateFrom, RelationshipEndMember fromEnd, RelationshipEndMember toEnd, out RelationshipType relType, bool allowAllRelationshipsInSameTypeHierarchy)
1252         {
1253             EntityUtil.CheckArgumentNull(navigateFrom, "navigateFrom");
1254
1255             //
1256             // Validate the relationship ends before use
1257             //
1258             CheckMember(fromEnd, "fromEnd");
1259             CheckMember(toEnd, "toEnd");
1260
1261             relType = fromEnd.DeclaringType as RelationshipType;
1262
1263             //
1264             // Ensure that the relation type is non-null and read-only
1265             //
1266             CheckType(relType);
1267
1268             //
1269             // Validate that the 'to' relationship end is defined by the same relationship type as the 'from' end
1270             //
1271             if (!relType.Equals(toEnd.DeclaringType))
1272             {
1273                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Factory_IncompatibleRelationEnds, "toEnd");
1274             }
1275
1276             RequireCompatibleType(navigateFrom, fromEnd, allowAllRelationshipsInSameTypeHierarchy);
1277
1278             return CreateResultType(toEnd);
1279         }
1280
1281         #endregion
1282
1283         #region Unary and Binary Set Operators - Distinct, Element, IsEmpty, Except, Intersect, UnionAll, Limit
1284
1285         internal static TypeUsage ValidateDistinct(DbExpression argument)
1286         {
1287             ValidateUnary(argument);
1288
1289             //
1290             // Ensure that the Argument is of a collection type
1291             //
1292             RequireCollectionArgument<DbDistinctExpression>(argument);
1293
1294             //
1295             // Ensure that the Distinct operation is valid for the input
1296             //
1297             CollectionType inputType = TypeHelpers.GetEdmType<CollectionType>(argument.ResultType);
1298             if (!TypeHelpers.IsValidDistinctOpType(inputType.TypeUsage))
1299             {
1300                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Distinct_InvalidCollection, "argument");
1301             }
1302
1303             return argument.ResultType;
1304         }
1305
1306         internal static TypeUsage ValidateElement(DbExpression argument)
1307         {
1308             ValidateUnary(argument);
1309
1310             //
1311             // Ensure that the operand is actually of a collection type.
1312             //
1313             RequireCollectionArgument<DbElementExpression>(argument);
1314
1315             //
1316             // Result Type is the element type of the collection type
1317             //
1318             return TypeHelpers.GetEdmType<CollectionType>(argument.ResultType).TypeUsage;
1319         }
1320
1321         internal static TypeUsage ValidateIsEmpty(DbExpression argument)
1322         {
1323             ValidateUnary(argument);
1324
1325             //
1326             // Ensure that the Argument is of a collection type
1327             //
1328             RequireCollectionArgument<DbIsEmptyExpression>(argument);
1329
1330             return _booleanType;
1331         }
1332
1333         internal static TypeUsage ValidateExcept(DbExpression left, DbExpression right)
1334         {
1335             ValidateBinary(left, right);
1336
1337             //
1338             // Ensures the left and right operands are each of a comparable collection type
1339             //
1340             RequireComparableCollectionArguments<DbExceptExpression>(left, right);
1341
1342             return left.ResultType;
1343         }
1344
1345         internal static TypeUsage ValidateIntersect(DbExpression left, DbExpression right)
1346         {
1347             ValidateBinary(left, right);
1348
1349             //
1350             // Ensures the left and right operands are each of a comparable collection type
1351             //
1352             return RequireComparableCollectionArguments<DbIntersectExpression>(left, right);
1353         }
1354
1355         internal static TypeUsage ValidateUnionAll(DbExpression left, DbExpression right)
1356         {
1357             ValidateBinary(left, right);
1358
1359             //
1360             // Ensure that the left and right operands are each of a collection type and that a common type exists for those types.
1361             //
1362             return RequireCollectionArguments<DbUnionAllExpression>(left, right);
1363         }
1364
1365         internal static TypeUsage ValidateLimit(DbExpression argument, DbExpression limit)
1366         {
1367             //
1368             // Initialize the Argument ExpressionLink. In addition to being non-null and from the same command tree,
1369             // the Argument expression must have a collection result type.
1370             //
1371             EntityUtil.CheckArgumentNull(argument, "argument");
1372             RequireCollectionArgument<DbLimitExpression>(argument);
1373             
1374             //
1375             // Initialize the Limit ExpressionLink. In addition to being non-null and from the same command tree,
1376             // the Limit expression must also have an integer result type.
1377             //
1378             EntityUtil.CheckArgumentNull(limit, "count");
1379             if (!TypeSemantics.IsIntegerNumericType(limit.ResultType))
1380             {
1381                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Limit_IntegerRequired, "limit");
1382             }
1383
1384             //
1385             // Currently the Limit expression is also required to be either a DbConstantExpression or a DbParameterReferenceExpression.
1386             //
1387             if (limit.ExpressionKind != DbExpressionKind.Constant &&
1388                 limit.ExpressionKind != DbExpressionKind.ParameterReference)
1389             {
1390                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Limit_ConstantOrParameterRefRequired, "limit");
1391             }
1392
1393             //
1394             // For constants, verify the limit is non-negative.
1395             //
1396             if (ArgumentValidation.IsConstantNegativeInteger(limit))
1397             {
1398                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Limit_NonNegativeLimitRequired, "limit");
1399             }
1400
1401             return argument.ResultType;
1402         }
1403         #endregion
1404
1405         #region General Operators - Case, Function, NewInstance, Property
1406
1407         internal static TypeUsage ValidateCase(IEnumerable<DbExpression> whenExpressions, IEnumerable<DbExpression> thenExpressions, DbExpression elseExpression, out DbExpressionList validWhens, out DbExpressionList validThens)
1408         {
1409             EntityUtil.CheckArgumentNull(whenExpressions, "whenExpressions");
1410             EntityUtil.CheckArgumentNull(thenExpressions, "thenExpressions");
1411             EntityUtil.CheckArgumentNull(elseExpression, "elseExpression");
1412
1413             //
1414             // All 'When's must produce a Boolean result, and a common (non-null) result type must exist
1415             // for all 'Thens' and 'Else'. At least one When/Then clause is required and the number of
1416             // 'When's must equal the number of 'Then's.
1417             //
1418             validWhens = CreateExpressionList(whenExpressions, "whenExpressions", (exp, idx) =>
1419                 {
1420                     RequireCompatibleType(exp, PrimitiveTypeKind.Boolean, "whenExpressions", idx);
1421                 }
1422             );
1423             Debug.Assert(validWhens.Count > 0, "CreateExpressionList(arguments, argumentName, validationCallback) allowed empty Whens?");
1424
1425             TypeUsage commonResultType = null;
1426             validThens = CreateExpressionList(thenExpressions, "thenExpressions", (exp, idx) =>
1427                 {
1428                     if (null == commonResultType)
1429                     {
1430                         commonResultType = exp.ResultType;
1431                     }
1432                     else
1433                     {
1434                         commonResultType = TypeHelpers.GetCommonTypeUsage(exp.ResultType, commonResultType);
1435                         if (null == commonResultType)
1436                         {
1437                             throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Case_InvalidResultType);
1438                         }
1439                     }
1440                 }
1441             );
1442             Debug.Assert(validWhens.Count > 0, "CreateExpressionList(arguments, argumentName, validationCallback) allowed empty Thens?");
1443             
1444             commonResultType = TypeHelpers.GetCommonTypeUsage(elseExpression.ResultType, commonResultType);
1445             if (null == commonResultType)
1446             {
1447                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Case_InvalidResultType);
1448             }
1449
1450             //
1451             // The number of 'When's must equal the number of 'Then's.
1452             //
1453             if (validWhens.Count != validThens.Count)
1454             {
1455                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Case_WhensMustEqualThens);
1456             }
1457                                                 
1458             //
1459             // The result type of DbCaseExpression is the common result type
1460             //
1461             return commonResultType;
1462         }
1463
1464         internal static TypeUsage ValidateFunction(EdmFunction function, IEnumerable<DbExpression> arguments, out DbExpressionList validArgs)
1465         {
1466             //
1467             // Ensure that the function metadata is non-null and from the same metadata workspace and dataspace as the command tree.
1468             CheckFunction(function);
1469
1470             //
1471             // Non-composable functions or non-UDF functions including command text are not permitted in expressions -- they can only be 
1472             // executed independently
1473             //
1474             if (!function.IsComposableAttribute)
1475             {
1476                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Function_NonComposableInExpression, "function");
1477             }
1478             if (!String.IsNullOrEmpty(function.CommandTextAttribute) && !function.HasUserDefinedBody)
1479             {
1480                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Function_CommandTextInExpression, "function");
1481             }
1482
1483             //
1484             // Functions that return void are not allowed
1485             //
1486             if (null == function.ReturnParameter)
1487             {
1488                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Function_VoidResultInvalid, "function");
1489             }
1490
1491             //
1492             // Validate the arguments
1493             //
1494             FunctionParameter[] expectedParams = GetExpectedParameters(function);
1495             validArgs = CreateExpressionList(arguments, "arguments", expectedParams.Length, (exp, idx) =>
1496                 {
1497                     ArgumentValidation.RequireCompatibleType(exp, expectedParams[idx].TypeUsage, "arguments", idx);
1498                 }
1499             );
1500             
1501             return function.ReturnParameter.TypeUsage;
1502         }
1503
1504         internal static TypeUsage ValidateInvoke(DbLambda lambda, IEnumerable<DbExpression> arguments, out DbExpressionList validArguments)
1505         {
1506             EntityUtil.CheckArgumentNull(lambda, "lambda");
1507             EntityUtil.CheckArgumentNull(arguments, "arguments");
1508
1509             // Each argument must be type-compatible with the corresponding lambda variable for which it supplies the value
1510             validArguments = null;
1511             var argValidator = CreateValidator(arguments, "arguments", (exp, idx) =>
1512                 {
1513                     RequireCompatibleType(exp, lambda.Variables[idx].ResultType, "arguments", idx);
1514                     return exp;
1515                 },
1516                 expList => new DbExpressionList(expList)
1517             );
1518             argValidator.ExpectedElementCount = lambda.Variables.Count;
1519             validArguments = argValidator.Validate();
1520
1521             // The result type of the lambda expression is the result type of the lambda body
1522             return lambda.Body.ResultType;
1523         }
1524                 
1525         internal static TypeUsage ValidateNewCollection(IEnumerable<DbExpression> elements, out DbExpressionList validElements)
1526         {
1527             TypeUsage commonElementType = null;
1528             validElements = CreateExpressionList(elements, "elements", (exp, idx) =>
1529                 {
1530                     if (commonElementType == null)
1531                     {
1532                         commonElementType = exp.ResultType;
1533                     }
1534                     else
1535                     {
1536                         commonElementType = TypeSemantics.GetCommonType(commonElementType, exp.ResultType);
1537                     }
1538                     
1539                     if (null == commonElementType)
1540                     {
1541                         throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Factory_NewCollectionInvalidCommonType, "collectionElements");
1542                     }
1543                 }
1544             );
1545
1546             Debug.Assert(validElements.Count > 0, "CreateExpressionList(arguments, argumentName, validationCallback) allowed empty elements list?");
1547             
1548             return CreateCollectionResultType(commonElementType);
1549         }
1550
1551         internal static TypeUsage ValidateNewEmptyCollection(TypeUsage collectionType, out DbExpressionList validElements)
1552         {
1553             CheckType(collectionType, "collectionType");
1554             if (!TypeSemantics.IsCollectionType(collectionType))
1555             {
1556                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_CollectionTypeRequired, "collectionType");
1557             }
1558
1559             // 
1560
1561
1562
1563
1564
1565             validElements = new DbExpressionList(new DbExpression[] { });
1566             return collectionType;
1567         }
1568
1569         internal static TypeUsage ValidateNewRow(IEnumerable<KeyValuePair<string, DbExpression>> columnValues, out DbExpressionList validElements)
1570         {
1571             List<KeyValuePair<string, TypeUsage>> columnTypes = new List<KeyValuePair<string, TypeUsage>>();
1572             var columnValidator = CreateValidator(columnValues, "columnValues", (columnValue, idx) =>
1573                 {
1574                     CheckNamed(columnValue, "columnValues", idx);
1575                     columnTypes.Add(new KeyValuePair<string, TypeUsage>(columnValue.Key, columnValue.Value.ResultType));
1576                     return columnValue.Value;
1577                 },
1578                 expList => new DbExpressionList(expList)
1579             );
1580             columnValidator.GetName = ((columnValue, idx) => columnValue.Key);
1581             validElements = columnValidator.Validate();
1582             return CreateResultType(TypeHelpers.CreateRowType(columnTypes));
1583         }
1584
1585         internal static TypeUsage ValidateNew(TypeUsage instanceType, IEnumerable<DbExpression> arguments, out DbExpressionList validArguments)
1586         {
1587             //
1588             // Ensure that the type is non-null, valid and not NullType
1589             //
1590             CheckType(instanceType, "instanceType");
1591
1592             CollectionType collectionType = null;
1593             if (TypeHelpers.TryGetEdmType<CollectionType>(instanceType, out collectionType) &&
1594                 collectionType != null)
1595             {
1596                 // Collection arguments may have zero count for empty collection construction
1597                 TypeUsage elementType = collectionType.TypeUsage;
1598                 validArguments = CreateExpressionList(arguments, "arguments", true, (exp, idx) =>
1599                     {
1600                         RequireCompatibleType(exp, elementType, "arguments", idx);
1601                     });
1602             }
1603             else
1604             {
1605                 List<TypeUsage> expectedTypes = GetStructuralMemberTypes(instanceType);
1606                 int pos = 0;
1607                 validArguments = CreateExpressionList(arguments, "arguments", expectedTypes.Count, (exp, idx) =>
1608                     {
1609                         RequireCompatibleType(exp, expectedTypes[pos++], "arguments", idx);
1610                     });
1611             }
1612
1613             return instanceType;
1614         }
1615             
1616         private static List<TypeUsage> GetStructuralMemberTypes(TypeUsage instanceType)
1617         {
1618             StructuralType structType = instanceType.EdmType as StructuralType;
1619             if (null == structType)
1620             {
1621                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_StructuralTypeRequired, "instanceType");
1622             }
1623
1624             if (structType.Abstract)
1625             {
1626                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_CannotInstantiateAbstractType(TypeHelpers.GetFullName(instanceType)), "instanceType");
1627             }
1628
1629             var members = TypeHelpers.GetAllStructuralMembers(structType);
1630             if (members == null || members.Count < 1)
1631             {
1632                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_CannotInstantiateMemberlessType(TypeHelpers.GetFullName(instanceType)), "instanceType");
1633             }
1634
1635             List<TypeUsage> memberTypes = new List<TypeUsage>(members.Count);
1636             for (int idx = 0; idx < members.Count; idx++)
1637             {
1638                 memberTypes.Add(Helper.GetModelTypeUsage(members[idx]));
1639             }
1640             return memberTypes;
1641         }
1642
1643         internal static TypeUsage ValidateNewEntityWithRelationships(EntityType entityType, IEnumerable<DbExpression> attributeValues, IList<DbRelatedEntityRef> relationships, out DbExpressionList validArguments, out System.Collections.ObjectModel.ReadOnlyCollection<DbRelatedEntityRef> validRelatedRefs)
1644         {
1645             EntityUtil.CheckArgumentNull(entityType, "entityType");
1646             EntityUtil.CheckArgumentNull(attributeValues, "attributeValues");
1647             EntityUtil.CheckArgumentNull(relationships, "relationships");
1648
1649             TypeUsage resultType = CreateResultType(entityType);
1650             resultType = ArgumentValidation.ValidateNew(resultType, attributeValues, out validArguments);
1651
1652             if (relationships.Count > 0)
1653             {
1654                 List<DbRelatedEntityRef> relatedRefs = new List<DbRelatedEntityRef>(relationships.Count);
1655                 for (int idx = 0; idx < relationships.Count; idx++)
1656                 {
1657                     DbRelatedEntityRef relatedRef = relationships[idx];
1658                     EntityUtil.CheckArgumentNull(relatedRef, StringUtil.FormatIndex("relationships", idx));
1659
1660                     // The source end type must be the same type or a supertype of the Entity instance type
1661                     EntityTypeBase expectedSourceType = TypeHelpers.GetEdmType<RefType>(relatedRef.SourceEnd.TypeUsage).ElementType;
1662                     // 
1663                     if (!entityType.EdmEquals(expectedSourceType) &&
1664                        !entityType.IsSubtypeOf(expectedSourceType))
1665                     {
1666                         throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_IncompatibleRelatedEntity_SourceTypeNotValid, StringUtil.FormatIndex("relationships", idx));
1667                     }
1668
1669                     relatedRefs.Add(relatedRef);
1670                 }
1671                 validRelatedRefs = relatedRefs.AsReadOnly();
1672             }
1673             else
1674             {
1675                 validRelatedRefs = new System.Collections.ObjectModel.ReadOnlyCollection<DbRelatedEntityRef>(new DbRelatedEntityRef[] { });
1676             }
1677
1678             return resultType;
1679         }
1680
1681         internal static TypeUsage ValidateProperty(DbExpression instance, EdmMember property, string propertyArgumentName)
1682         {
1683             //
1684             // Validate the member
1685             //
1686             CheckMember(property, propertyArgumentName);
1687             
1688             //
1689             // Validate the instance
1690             //
1691             if (null == instance)
1692             {
1693                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Property_InstanceRequiredForInstance, "instance");
1694             }
1695
1696             TypeUsage expectedInstanceType = TypeUsage.Create(property.DeclaringType);
1697             RequireCompatibleType(instance, expectedInstanceType, "instance");
1698
1699             Debug.Assert(null != Helper.GetModelTypeUsage(property), "EdmMember metadata has a TypeUsage of null");
1700
1701             return Helper.GetModelTypeUsage(property);
1702         }
1703
1704         internal static TypeUsage ValidateProperty(DbExpression instance, string propertyName, bool ignoreCase, out EdmMember foundMember)
1705         {
1706             EntityUtil.CheckArgumentNull(instance, "instance");
1707             EntityUtil.CheckArgumentNull(propertyName, "propertyName");
1708             
1709             //
1710             // EdmProperty, NavigationProperty and RelationshipEndMember are the only valid members for DbPropertyExpression.
1711             // Since these all derive from EdmMember they are declared by subtypes of StructuralType,
1712             // so a non-StructuralType instance is invalid.
1713             //
1714             StructuralType structType;
1715             if (TypeHelpers.TryGetEdmType<StructuralType>(instance.ResultType, out structType))
1716             {
1717                 //
1718                 // Does the type declare a member with the given name?
1719                 //
1720                 if (structType.Members.TryGetValue(propertyName, ignoreCase, out foundMember) &&
1721                     foundMember != null)
1722                 {
1723                     //
1724                     // If the member is a RelationshipEndMember, call the corresponding overload.
1725                     //
1726                     if (Helper.IsRelationshipEndMember(foundMember) ||
1727                         Helper.IsEdmProperty(foundMember) ||
1728                         Helper.IsNavigationProperty(foundMember))
1729                     {
1730                         return Helper.GetModelTypeUsage(foundMember);
1731                     }    
1732                 }
1733             }
1734
1735             throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Factory_NoSuchProperty(propertyName, TypeHelpers.GetFullName(instance.ResultType)), "propertyName");
1736         }
1737
1738         #endregion
1739
1740         private static void CheckNamed<T>(KeyValuePair<string, T> element, string argumentName, int index)
1741         {
1742             if (string.IsNullOrEmpty(element.Key))
1743             {
1744                 if (index != -1)
1745                 {
1746                     argumentName = StringUtil.FormatIndex(argumentName, index);
1747                 }
1748                 throw EntityUtil.ArgumentNull(string.Format(CultureInfo.InvariantCulture, "{0}.Key", argumentName));
1749             }
1750
1751             if (null == element.Value)
1752             {
1753                 if (index != -1)
1754                 {
1755                     argumentName = StringUtil.FormatIndex(argumentName, index);
1756                 }
1757                 throw EntityUtil.ArgumentNull(string.Format(CultureInfo.InvariantCulture, "{0}.Value", argumentName));
1758             }
1759         }
1760
1761         private static void CheckReadOnly(GlobalItem item, string varName)
1762         {
1763             EntityUtil.CheckArgumentNull(item, varName);
1764             if (!(item.IsReadOnly))
1765             {
1766                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_MetadataNotReadOnly, varName);
1767             }
1768         }
1769
1770         private static void CheckReadOnly(TypeUsage item, string varName)
1771         {
1772             EntityUtil.CheckArgumentNull(item, varName);
1773             if (!(item.IsReadOnly))
1774             {
1775                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_MetadataNotReadOnly, varName);
1776             }
1777         }
1778
1779         private static void CheckReadOnly(EntitySetBase item, string varName)
1780         {
1781             EntityUtil.CheckArgumentNull(item, varName);
1782             if (!(item.IsReadOnly))
1783             {
1784                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_MetadataNotReadOnly, varName);
1785             }
1786         }
1787
1788         private static void CheckType(EdmType type)
1789         {
1790             CheckType(type, "type");
1791         }
1792
1793         private static void CheckType(EdmType type, string argumentName)
1794         {
1795             EntityUtil.CheckArgumentNull(type, argumentName);
1796             CheckReadOnly(type, argumentName);
1797         }
1798
1799         /// <summary>
1800         /// Ensures that the  specified type is non-null, associated with the correct metadata workspace/dataspace, and is not NullType.
1801         /// </summary>
1802         /// <param name="type">The type usage instance to verify.</param>
1803         /// <exception cref="ArgumentNullException">If the specified type metadata is null</exception>
1804         /// <exception cref="ArgumentException">If the specified type metadata belongs to a metadata workspace other than the workspace of the command tree</exception>
1805         /// <exception cref="ArgumentException">If the specified type metadata belongs to a dataspace other than the dataspace of the command tree</exception>
1806         private static void CheckType(TypeUsage type)
1807         {
1808             CheckType(type, "type");
1809         }
1810
1811         private static void CheckType(TypeUsage type, string varName)
1812         {
1813             EntityUtil.CheckArgumentNull(type, varName);
1814             CheckReadOnly(type, varName);
1815
1816             // TypeUsage constructor is responsible for basic validation of EdmType
1817             Debug.Assert(type.EdmType != null, "TypeUsage constructor allowed null EdmType?");
1818             
1819             if (!CheckDataSpace(type))
1820             {
1821                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_TypeUsageIncorrectSpace, "type");
1822             }
1823         }
1824         
1825         /// <summary>
1826         /// Verifies that the specified member is valid - non-null, from the same metadata workspace and data space as the command tree, etc
1827         /// </summary>
1828         /// <param name="memberMeta">The member to verify</param>
1829         /// <param name="varName">The name of the variable to which this member instance is being assigned</param>
1830         private static void CheckMember(EdmMember memberMeta, string varName)
1831         {
1832             EntityUtil.CheckArgumentNull(memberMeta, varName);
1833             CheckReadOnly(memberMeta.DeclaringType, varName);
1834
1835             // EdmMember constructor is responsible for basic validation
1836             Debug.Assert(memberMeta.Name != null, "EdmMember constructor allowed null name?");
1837             Debug.Assert(null != memberMeta.TypeUsage, "EdmMember constructor allowed null for TypeUsage?");
1838             Debug.Assert(null != memberMeta.DeclaringType, "EdmMember constructor allowed null for DeclaringType?");
1839             if(!CheckDataSpace(memberMeta.TypeUsage) || !CheckDataSpace(memberMeta.DeclaringType))
1840             {
1841                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EdmMemberIncorrectSpace, varName);
1842             }
1843         }
1844
1845         private static void CheckParameter(FunctionParameter paramMeta, string varName)
1846         {
1847             EntityUtil.CheckArgumentNull(paramMeta, varName);
1848             CheckReadOnly(paramMeta.DeclaringFunction, varName);
1849
1850             // FunctionParameter constructor is responsible for basic validation
1851             Debug.Assert(paramMeta.Name != null, "FunctionParameter constructor allowed null name?");
1852             
1853             // Verify that the parameter is from the same workspace as the DbCommandTree
1854             if (!CheckDataSpace(paramMeta.TypeUsage))
1855             {
1856                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionParameterIncorrectSpace, varName);
1857             }
1858         }
1859
1860         /// <summary>
1861         /// Verifies that the specified function metadata is valid - non-null and either created by this command tree (if a LambdaFunction) or from the same metadata collection and data space as the command tree (for ordinary function metadata)
1862         /// </summary>
1863         /// <param name="function">The function metadata to verify</param>
1864         private static void CheckFunction(EdmFunction function)
1865         {
1866             EntityUtil.CheckArgumentNull(function, "function");
1867             CheckReadOnly(function, "function");
1868
1869             Debug.Assert(function.Name != null, "EdmType constructor allowed null name?");
1870             
1871             if (!CheckDataSpace(function))
1872             {
1873                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionIncorrectSpace, "function");
1874             }
1875
1876             // Composable functions must have a return parameter.
1877             if (function.IsComposableAttribute && null == function.ReturnParameter)
1878             {
1879                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionReturnParameterNull, "function");
1880             }
1881
1882             // Verify that the function ReturnType - if present - is from the DbCommandTree's metadata collection and dataspace
1883             // A return parameter is not required for non-composable functions.
1884             if (function.ReturnParameter != null)
1885             {
1886                 if (!CheckDataSpace(function.ReturnParameter.TypeUsage))
1887                 {
1888                     throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionParameterIncorrectSpace, "function.ReturnParameter");
1889                 }
1890             }
1891
1892             // Verify that the function parameters collection is non-null and,
1893             // if non-empty, contains valid IParameterMetadata instances.
1894             IList<FunctionParameter> functionParams = function.Parameters;
1895             Debug.Assert(functionParams != null, "EdmFunction constructor did not initialize Parameters?");
1896             
1897             for (int idx = 0; idx < functionParams.Count; idx++)
1898             {
1899                 CheckParameter(functionParams[idx], StringUtil.FormatIndex("function.Parameters", idx));
1900             }
1901         }
1902
1903         /// <summary>
1904         /// Verifies that the specified EntitySet is valid with respect to the command tree
1905         /// </summary>
1906         /// <param name="entitySet">The EntitySet to verify</param>
1907         /// <param name="varName">The variable name to use if an exception should be thrown</param>
1908         private static void CheckEntitySet(EntitySetBase entitySet, string varName)
1909         {
1910             EntityUtil.CheckArgumentNull(entitySet, varName);
1911             CheckReadOnly(entitySet, varName);
1912
1913             // EntitySetBase constructor is responsible for basic validation of set name and element type
1914             Debug.Assert(!string.IsNullOrEmpty(entitySet.Name), "EntitySetBase constructor allowed null/empty set name?");
1915             
1916             //
1917             // Verify the Extent's Container
1918             //
1919             if (null == entitySet.EntityContainer)
1920             {
1921                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EntitySetEntityContainerNull, varName);
1922             }
1923
1924             if(!CheckDataSpace(entitySet.EntityContainer))
1925             {
1926                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EntitySetIncorrectSpace, varName);
1927             }
1928
1929             //
1930             // Verify the Extent's Entity Type
1931             //
1932             // EntitySetBase constructor is responsible for basic validation of set name and element type
1933             Debug.Assert(entitySet.ElementType != null, "EntitySetBase constructor allowed null container?");
1934                         
1935             if(!CheckDataSpace(entitySet.ElementType))
1936             {
1937                 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EntitySetIncorrectSpace, varName);
1938             }
1939         }
1940
1941         private static bool CheckDataSpace(TypeUsage type)
1942         {
1943             return CheckDataSpace(type.EdmType);
1944         }
1945
1946         private static bool CheckDataSpace(GlobalItem item)
1947         {
1948             // Since the set of primitive types and canonical functions are shared, we don't need to check for them.
1949             // Additionally, any non-canonical function in the C-Space must be a cached store function, which will
1950             // also not be present in the workspace.
1951             if (BuiltInTypeKind.PrimitiveType == item.BuiltInTypeKind ||
1952                 (BuiltInTypeKind.EdmFunction == item.BuiltInTypeKind && DataSpace.CSpace == item.DataSpace))
1953             {
1954                 return true;
1955             }
1956
1957             // Transient types should be checked according to their non-transient element types
1958             if (Helper.IsRowType(item))
1959             {
1960                 foreach (EdmProperty prop in ((RowType)item).Properties)
1961                 {
1962                     if (!CheckDataSpace(prop.TypeUsage))
1963                     {
1964                         return false;
1965                     }
1966                 }
1967
1968                 return true;
1969             }
1970             else if (Helper.IsCollectionType(item))
1971             {
1972                 return CheckDataSpace(((CollectionType)item).TypeUsage);
1973             }
1974             else if (Helper.IsRefType(item))
1975             {
1976                 return CheckDataSpace(((RefType)item).ElementType);
1977             }
1978             else
1979             {
1980                 return (item.DataSpace == DataSpace.SSpace || item.DataSpace == DataSpace.CSpace);
1981             }
1982         }
1983         
1984         private static TypeUsage CreateCollectionOfRowResultType(List<KeyValuePair<string, TypeUsage>> columns)
1985         {
1986             TypeUsage retUsage = TypeUsage.Create(
1987                     TypeHelpers.CreateCollectionType(
1988                         TypeUsage.Create(
1989                             TypeHelpers.CreateRowType(columns)
1990                         )
1991                     )
1992                 );
1993
1994             return retUsage;
1995         }
1996
1997         private static TypeUsage CreateCollectionResultType(EdmType type)
1998         {
1999             TypeUsage retUsage = TypeUsage.Create(
2000                     TypeHelpers.CreateCollectionType(
2001                         TypeUsage.Create(type)
2002                     )
2003                 );
2004
2005             return retUsage;
2006         }
2007
2008         private static TypeUsage CreateCollectionResultType(TypeUsage type)
2009         {
2010             TypeUsage retUsage = TypeUsage.Create(TypeHelpers.CreateCollectionType(type));
2011             return retUsage;
2012         }
2013
2014         private static TypeUsage CreateResultType(EdmType resultType)
2015         {
2016             return TypeUsage.Create(resultType);
2017         }
2018
2019         private static TypeUsage CreateResultType(RelationshipEndMember end)
2020         {
2021             TypeUsage retType = end.TypeUsage;
2022             if (!TypeSemantics.IsReferenceType(retType))
2023             {
2024                 //
2025                 // The only relation end that is currently allowed to have a non-Reference type is the Child end of
2026                 // a composition, in which case the end type must be an entity type. 
2027                 //
2028                 //Debug.Assert(end.Relation.IsComposition && !end.IsParent && (end.Type is EntityType), "Relation end can only have non-Reference type if it is a Composition child end");
2029
2030                 retType = TypeHelpers.CreateReferenceTypeUsage(TypeHelpers.GetEdmType<EntityType>(retType));
2031             }
2032
2033             //
2034             // If the upper bound is not 1 the result type is a collection of the given type
2035             //
2036             if (RelationshipMultiplicity.Many == end.RelationshipMultiplicity)
2037             {
2038                 retType = TypeHelpers.CreateCollectionTypeUsage(retType);
2039             }
2040
2041             return retType;
2042         }
2043
2044         private static TypeUsage CreateReferenceResultType(EntityTypeBase referencedEntityType)
2045         {
2046             return TypeUsage.Create(TypeHelpers.CreateReferenceType(referencedEntityType));
2047         }
2048
2049         /// <summary>
2050         /// Requires: non-null expression
2051         /// Determines whether the expression is a constant negative integer value. Always returns
2052         /// false for non-constant, non-integer expression instances.
2053         /// </summary>
2054         private static bool IsConstantNegativeInteger(DbExpression expression)
2055         {
2056             return (expression.ExpressionKind == DbExpressionKind.Constant &&
2057                     TypeSemantics.IsIntegerNumericType(expression.ResultType) &&
2058                     Convert.ToInt64(((DbConstantExpression)expression).Value, CultureInfo.InvariantCulture) < 0);
2059         }
2060
2061         private static bool TryGetPrimitiveTypeKind(Type clrType, out PrimitiveTypeKind primitiveTypeKind)
2062         {
2063             return ClrProviderManifest.Instance.TryGetPrimitiveTypeKind(clrType, out primitiveTypeKind);
2064         }
2065
2066         /// <summary>
2067         /// Checks whether the clr enum type matched the edm enum type.
2068         /// </summary>
2069         /// <param name="edmEnumType">Edm enum type.</param>
2070         /// <param name="clrEnumType">Clr enum type.</param>
2071         /// <returns>
2072         /// <c>true</c> if types match otherwise <c>false</c>.
2073         /// </returns>
2074         /// <remarks>
2075         /// The clr enum type matches the edm enum type if:
2076         /// - type names are the same
2077         /// - both types have the same underlying type (note that this prevents from over- and underflows)
2078         /// - both types have the same number of members
2079         /// - members have the same names
2080         /// - members have the same values
2081         /// </remarks>
2082         private static bool ClrEdmEnumTypesMatch(EnumType edmEnumType, Type clrEnumType)
2083         {
2084             Debug.Assert(edmEnumType != null, "edmEnumType != null");
2085             Debug.Assert(clrEnumType != null, "clrEnumType != null");
2086             Debug.Assert(clrEnumType.IsEnum, "non enum clr type.");
2087
2088             // check that type names are the same and both types have the same number of members
2089             if (clrEnumType.Name != edmEnumType.Name 
2090                 || clrEnumType.GetEnumNames().Length != edmEnumType.Members.Count)
2091             {
2092                 return false;
2093             }
2094
2095             // check that both types have the same underlying type (note that this also prevents from over- and underflows)
2096             PrimitiveTypeKind clrEnumUnderlyingTypeKind;
2097             if(!TryGetPrimitiveTypeKind(clrEnumType.GetEnumUnderlyingType(), out clrEnumUnderlyingTypeKind) 
2098                 || clrEnumUnderlyingTypeKind != edmEnumType.UnderlyingType.PrimitiveTypeKind)
2099             {
2100                 return false;
2101             }
2102
2103             // check that all the members have the same names and values
2104             foreach (var edmEnumTypeMember in edmEnumType.Members)
2105             {
2106                 Debug.Assert(
2107                     edmEnumTypeMember.Value.GetType() == clrEnumType.GetEnumUnderlyingType(),
2108                     "Enum underlying types matched so types of member values must match the enum underlying type as well");
2109
2110                 if (!clrEnumType.GetEnumNames().Contains(edmEnumTypeMember.Name) 
2111                     || !edmEnumTypeMember.Value.Equals(
2112                         Convert.ChangeType(Enum.Parse(clrEnumType, edmEnumTypeMember.Name), clrEnumType.GetEnumUnderlyingType(), CultureInfo.InvariantCulture)))
2113                 {
2114                     return false;
2115                 }
2116             }
2117             
2118             return true;
2119         }
2120     }
2121 }