1 //---------------------------------------------------------------------
2 // <copyright file="ArgumentValidation.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Common.CommandTrees.ExpressionBuilder.Internal
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;
22 internal static class ArgumentValidation
24 private static TypeUsage _booleanType = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(PrimitiveTypeKind.Boolean);
26 // The Metadata ReadOnlyCollection class conflicts with System.Collections.ObjectModel.ReadOnlyCollection...
27 internal static System.Collections.ObjectModel.ReadOnlyCollection<TElement> NewReadOnlyCollection<TElement>(IList<TElement> list)
29 return new System.Collections.ObjectModel.ReadOnlyCollection<TElement>(list);
32 private static void RequirePolymorphicType(TypeUsage type, string typeArgumentName)
34 Debug.Assert(type != null, "Ensure type is non-null before calling RequirePolymorphicType");
36 if (!TypeSemantics.IsPolymorphicType(type))
38 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicTypeRequired(TypeHelpers.GetFullName(type)), "type");
42 private static void RequireCompatibleType(DbExpression expression, TypeUsage requiredResultType, string argumentName)
44 RequireCompatibleType(expression, requiredResultType, argumentName, -1);
47 private static void RequireCompatibleType(DbExpression expression, TypeUsage requiredResultType, string argumentName, int argumentIndex)
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");
52 if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(expression.ResultType, requiredResultType))
54 // Don't call FormatIndex unless an exception is actually being thrown
55 if (argumentIndex != -1)
57 argumentName = StringUtil.FormatIndex(argumentName, argumentIndex);
60 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_ExpressionLink_TypeMismatch(
61 TypeHelpers.GetFullName(expression.ResultType),
62 TypeHelpers.GetFullName(requiredResultType)
69 private static void RequireCompatibleType(DbExpression expression, PrimitiveTypeKind requiredResultType, string argumentName)
71 RequireCompatibleType(expression, requiredResultType, argumentName, -1);
74 private static void RequireCompatibleType(DbExpression expression, PrimitiveTypeKind requiredResultType, string argumentName, int index)
76 Debug.Assert(expression != null, "Ensure expression is non-null before checking for type compatibility");
78 PrimitiveTypeKind valueTypeKind;
79 bool valueIsPrimitive = TypeHelpers.TryGetPrimitiveTypeKind(expression.ResultType, out valueTypeKind);
80 if (!valueIsPrimitive ||
81 valueTypeKind != requiredResultType)
85 argumentName = StringUtil.FormatIndex(argumentName, index);
88 throw EntityUtil.Argument(
89 System.Data.Entity.Strings.Cqt_ExpressionLink_TypeMismatch(
91 Enum.GetName(typeof(PrimitiveTypeKind), valueTypeKind)
92 : TypeHelpers.GetFullName(expression.ResultType)),
93 Enum.GetName(typeof(PrimitiveTypeKind), requiredResultType)
100 private static void RequireCompatibleType(DbExpression from, RelationshipEndMember end, bool allowAllRelationshipsInSameTypeHierarchy)
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");
105 TypeUsage endType = end.TypeUsage;
106 if (!TypeSemantics.IsReferenceType(endType))
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.
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");
114 endType = TypeHelpers.CreateReferenceTypeUsage(TypeHelpers.GetEdmType<EntityType>(endType));
117 if (allowAllRelationshipsInSameTypeHierarchy)
119 if (TypeHelpers.GetCommonTypeUsage(endType, from.ResultType) == null)
121 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_RelNav_WrongSourceType(TypeHelpers.GetFullName(endType)), "from");
124 else if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(from.ResultType.EdmType, endType.EdmType))
126 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_RelNav_WrongSourceType(TypeHelpers.GetFullName(endType)), "from");
130 private static void RequireCollectionArgument<TExpressionType>(DbExpression argument)
132 Debug.Assert(argument != null, "Validate argument is non-null before calling CheckCollectionArgument");
134 if (!TypeSemantics.IsCollectionType(argument.ResultType))
136 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Unary_CollectionRequired(typeof(TExpressionType).Name), "argument");
140 private static TypeUsage RequireCollectionArguments<TExpressionType>(DbExpression left, DbExpression right)
142 Debug.Assert(left != null && right != null, "Ensure left and right are non-null before calling RequireCollectionArguments");
144 if (!TypeSemantics.IsCollectionType(left.ResultType) || !TypeSemantics.IsCollectionType(right.ResultType))
146 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binary_CollectionsRequired(typeof(TExpressionType).Name));
149 TypeUsage commonType = TypeHelpers.GetCommonTypeUsage(left.ResultType, right.ResultType);
150 if (null == commonType)
152 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binary_CollectionsRequired(typeof(TExpressionType).Name));
158 private static TypeUsage RequireComparableCollectionArguments<TExpressionType>(DbExpression left, DbExpression right)
160 TypeUsage resultType = RequireCollectionArguments<TExpressionType>(left, right);
162 if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(left.ResultType)))
164 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_InvalidTypeForSetOperation(TypeHelpers.GetElementTypeUsage(left.ResultType).Identity, typeof(TExpressionType).Name), "left");
167 if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(right.ResultType)))
169 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_InvalidTypeForSetOperation(TypeHelpers.GetElementTypeUsage(right.ResultType).Identity, typeof(TExpressionType).Name), "right");
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)
177 EnumerableValidator<TElementIn, TElementOut, TResult> ret = new EnumerableValidator<TElementIn, TElementOut, TResult>(argument, argumentName);
178 ret.ConvertElement = convertElement;
179 ret.CreateResult = createResult;
183 private static DbExpressionList CreateExpressionList(IEnumerable<DbExpression> arguments, string argumentName, Action<DbExpression, int> validationCallback)
185 return CreateExpressionList(arguments, argumentName, false, validationCallback);
188 private static DbExpressionList CreateExpressionList(IEnumerable<DbExpression> arguments, string argumentName, bool allowEmpty, Action<DbExpression, int> validationCallback)
190 var ev = CreateValidator(arguments, argumentName,
193 if (validationCallback != null)
195 validationCallback(exp, idx);
199 expList => new DbExpressionList(expList)
202 ev.AllowEmpty = allowEmpty;
204 return ev.Validate();
207 private static DbExpressionList CreateExpressionList(IEnumerable<DbExpression> arguments, string argumentName, int expectedElementCount, Action<DbExpression, int> validationCallback)
209 var ev = CreateValidator(arguments, argumentName,
212 if (validationCallback != null)
214 validationCallback(exp, idx);
218 (expList) => new DbExpressionList(expList)
221 ev.ExpectedElementCount = expectedElementCount;
222 ev.AllowEmpty = false;
224 return ev.Validate();
227 private static TypeUsage ValidateBinary(DbExpression left, DbExpression right)
229 EntityUtil.CheckArgumentNull(left, "left");
230 EntityUtil.CheckArgumentNull(right, "right");
232 return TypeHelpers.GetCommonTypeUsage(left.ResultType, right.ResultType);
235 private static void ValidateUnary(DbExpression argument)
237 EntityUtil.CheckArgumentNull(argument, "argument");
240 private static void ValidateTypeUnary(DbExpression argument, TypeUsage type, string typeArgumentName)
242 ValidateUnary(argument);
243 CheckType(type, typeArgumentName);
246 #region Bindings - Expression and Group
248 internal static TypeUsage ValidateBindAs(DbExpression input, string varName)
251 // Ensure no argument is null
253 EntityUtil.CheckArgumentNull(varName, "varName");
254 EntityUtil.CheckArgumentNull(input, "input");
257 // Ensure Variable name is non-empty
259 if (string.IsNullOrEmpty(varName))
261 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_VariableNameNotValid, "varName");
265 // Ensure the DbExpression has a collection result type
267 TypeUsage elementType = null;
268 if (!TypeHelpers.TryGetCollectionElementType(input.ResultType, out elementType))
270 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_CollectionRequired, "input");
273 Debug.Assert(elementType.IsReadOnly, "DbExpressionBinding Expression ResultType has editable element type");
278 internal static TypeUsage ValidateGroupBindAs(DbExpression input, string varName, string groupVarName)
281 // Ensure no argument is null
283 EntityUtil.CheckArgumentNull(varName, "varName");
284 EntityUtil.CheckArgumentNull(groupVarName, "groupVarName");
285 EntityUtil.CheckArgumentNull(input, "input");
288 // Ensure Variable and Group names are both non-empty
290 if (string.IsNullOrEmpty(varName))
292 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_VariableNameNotValid, "varName");
295 if (string.IsNullOrEmpty(groupVarName))
297 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBinding_GroupVariableNameNotValid, "groupVarName");
301 // Ensure the DbExpression has a collection result type
303 TypeUsage elementType = null;
304 if (!TypeHelpers.TryGetCollectionElementType(input.ResultType, out elementType))
306 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBinding_CollectionRequired, "input");
309 Debug.Assert((elementType.IsReadOnly), "DbGroupExpressionBinding Expression ResultType has editable element type");
316 #region Aggregates and Sort Keys
318 private static FunctionParameter[] GetExpectedParameters(EdmFunction function)
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();
324 internal static DbExpressionList ValidateFunctionAggregate(EdmFunction function, IEnumerable<DbExpression> args)
327 // Verify that the aggregate function is from the metadata collection and data space of the command tree.
329 ArgumentValidation.CheckFunction(function);
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)
335 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Aggregate_InvalidFunction, "function");
338 FunctionParameter[] expectedParams = GetExpectedParameters(function);
339 DbExpressionList funcArgs = CreateExpressionList(args, "argument", expectedParams.Length, (exp, idx) =>
341 TypeUsage paramType = expectedParams[idx].TypeUsage;
342 TypeUsage elementType = null;
343 if (TypeHelpers.TryGetCollectionElementType(paramType, out elementType))
345 paramType = elementType;
348 ArgumentValidation.RequireCompatibleType(exp, paramType, "argument");
355 internal static DbExpressionList ValidateGroupAggregate(DbExpression argument)
357 EntityUtil.CheckArgumentNull(argument, "argument");
358 return new DbExpressionList(new[] { argument });
361 internal static void ValidateSortClause(DbExpression key)
363 EntityUtil.CheckArgumentNull(key, "key");
365 if (!TypeHelpers.IsValidSortOpKeyType(key.ResultType))
367 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Sort_OrderComparable, "key");
371 internal static void ValidateSortClause(DbExpression key, string collation)
373 ValidateSortClause(key);
375 EntityUtil.CheckArgumentNull(collation, "collation");
376 if (StringUtil.IsNullOrEmptyOrWhiteSpace(collation))
378 throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Sort_EmptyCollationInvalid, "collation");
381 if (!TypeSemantics.IsPrimitiveType(key.ResultType, PrimitiveTypeKind.String))
383 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Sort_NonStringCollationInvalid, "collation");
391 internal static System.Collections.ObjectModel.ReadOnlyCollection<DbVariableReferenceExpression> ValidateLambda(IEnumerable<DbVariableReferenceExpression> variables, DbExpression body)
393 EntityUtil.CheckArgumentNull(body, "body");
395 var varVal = CreateValidator(variables, "variables",
400 throw EntityUtil.ArgumentNull(StringUtil.FormatIndex("variables", idx));
404 (varList) => new System.Collections.ObjectModel.ReadOnlyCollection<DbVariableReferenceExpression>(varList)
406 varVal.AllowEmpty = true;
407 varVal.GetName = (varDef, idx) => varDef.VariableName;
409 var result = varVal.Validate();
415 #region Binding-based methods: All, Any, Cross|OuterApply, Cross|FullOuter|Inner|LeftOuterJoin, Filter, GroupBy, Project, Skip, Sort
417 private static void ValidateBinding(DbExpressionBinding binding, string argumentName)
419 EntityUtil.CheckArgumentNull(binding, argumentName);
422 private static void ValidateGroupBinding(DbGroupExpressionBinding binding, string argumentName)
424 EntityUtil.CheckArgumentNull(binding, argumentName);
427 private static void ValidateBound(DbExpressionBinding input, DbExpression argument, string argumentName)
429 ValidateBinding(input, "input");
430 EntityUtil.CheckArgumentNull(argument, argumentName);
433 internal static TypeUsage ValidateQuantifier(DbExpressionBinding input, DbExpression predicate)
435 ValidateBound(input, predicate, "predicate");
436 RequireCompatibleType(predicate, PrimitiveTypeKind.Boolean, "predicate");
438 return predicate.ResultType;
441 internal static TypeUsage ValidateApply(DbExpressionBinding input, DbExpressionBinding apply)
443 ValidateBinding(input, "input");
444 ValidateBinding(apply, "apply");
447 // Duplicate Input and Apply binding names are not allowed
449 if (input.VariableName.Equals(apply.VariableName, StringComparison.Ordinal))
451 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Apply_DuplicateVariableNames);
455 // Initialize the result type
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));
461 return CreateCollectionOfRowResultType(recordCols);
464 internal static System.Collections.ObjectModel.ReadOnlyCollection<DbExpressionBinding> ValidateCrossJoin(IEnumerable<DbExpressionBinding> inputs, out TypeUsage resultType)
467 // Ensure that the list of input expression bindings is non-null.
469 EntityUtil.CheckArgumentNull(inputs, "inputs");
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.
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();
480 while (inputEnum.MoveNext())
482 DbExpressionBinding input = inputEnum.Current;
485 // Validate the DbExpressionBinding before accessing its properties
487 ValidateBinding(input, StringUtil.FormatIndex("inputs", iPos));
490 // Duplicate binding names are not allowed
493 if (bindingNames.TryGetValue(input.VariableName, out nameIndex))
495 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_CrossJoin_DuplicateVariableNames(nameIndex, iPos, input.VariableName));
498 inputList.Add(input);
499 bindingNames.Add(input.VariableName, iPos);
501 columns.Add(new KeyValuePair<string, TypeUsage>(input.VariableName, input.VariableType));
506 if (inputList.Count < 2)
508 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_CrossJoin_AtLeastTwoInputs, "inputs");
512 // Initialize the result type
514 resultType = CreateCollectionOfRowResultType(columns);
519 return inputList.AsReadOnly();
522 internal static TypeUsage ValidateJoin(DbExpressionBinding left, DbExpressionBinding right, DbExpression joinCondition)
527 ValidateBinding(left, "left");
528 ValidateBinding(left, "right");
529 EntityUtil.CheckArgumentNull(joinCondition, "joinCondition");
532 // Duplicate Left and Right binding names are not allowed
534 if (left.VariableName.Equals(right.VariableName, StringComparison.Ordinal))
536 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Join_DuplicateVariableNames);
540 // Validate the JoinCondition)
542 RequireCompatibleType(joinCondition, PrimitiveTypeKind.Boolean, "joinCondition");
545 // Initialize the result type
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));
551 return CreateCollectionOfRowResultType(columns);
554 internal static TypeUsage ValidateFilter(DbExpressionBinding input, DbExpression predicate)
556 ValidateBound(input, predicate, "predicate");
557 RequireCompatibleType(predicate, PrimitiveTypeKind.Boolean, "predicate");
558 return input.Expression.ResultType;
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)
564 // Validate the input set
566 ValidateGroupBinding(input, "input");
569 // Track the cumulative set of column names and types, as well as key column names
571 List<KeyValuePair<string, TypeUsage>> columns = new List<KeyValuePair<string, TypeUsage>>();
572 HashSet<string> keyNames = new HashSet<string>();
575 // Validate the grouping keys
577 var keyValidator = CreateValidator(keys, "keys",
580 ArgumentValidation.CheckNamed(keyInfo, "keys", index);
583 // The result Type of an expression used as a group key must be equality comparable
585 if (!TypeHelpers.IsValidGroupKeyType(keyInfo.Value.ResultType))
587 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_KeyNotEqualityComparable(keyInfo.Key));
590 keyNames.Add(keyInfo.Key);
591 columns.Add(new KeyValuePair<string, TypeUsage>(keyInfo.Key, keyInfo.Value.ResultType));
593 return keyInfo.Value;
595 expList => new DbExpressionList(expList)
597 keyValidator.AllowEmpty = true;
598 keyValidator.GetName = (keyInfo, idx) => keyInfo.Key;
599 validKeys = keyValidator.Validate();
601 bool hasGroupAggregate = false;
602 var aggValidator = CreateValidator(aggregates, "aggregates",
605 ArgumentValidation.CheckNamed(aggInfo, "aggregates", idx);
608 // Is there a grouping key with the same name?
610 if (keyNames.Contains(aggInfo.Key))
612 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_AggregateColumnExistsAsGroupColumn(aggInfo.Key));
616 // At most one group aggregate can be specified
618 if (aggInfo.Value is DbGroupAggregate)
620 if (hasGroupAggregate)
622 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_MoreThanOneGroupAggregate);
626 hasGroupAggregate = true;
630 columns.Add(new KeyValuePair<string, TypeUsage>(aggInfo.Key, aggInfo.Value.ResultType));
631 return aggInfo.Value;
633 aggList => NewReadOnlyCollection(aggList)
635 aggValidator.AllowEmpty = true;
636 aggValidator.GetName = (aggInfo, idx) => aggInfo.Key;
637 validAggregates = aggValidator.Validate();
640 // Either the Keys or Aggregates may be omitted, but not both
642 if (0 == validKeys.Count && 0 == validAggregates.Count)
644 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_AtLeastOneKeyOrAggregate);
648 // Create the result type. This is a collection of the record type produced by the group keys and aggregates.
650 return CreateCollectionOfRowResultType(columns);
653 internal static TypeUsage ValidateProject(DbExpressionBinding input, DbExpression projection)
655 ValidateBound(input, projection, "projection");
656 return CreateCollectionResultType(projection.ResultType);
660 /// Validates the input and sort key arguments to both DbSkipExpression and DbSortExpression.
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)
666 ValidateBinding(input, "input");
668 var ev = CreateValidator(sortOrder, "sortOrder",
670 keyList => NewReadOnlyCollection(keyList)
672 ev.AllowEmpty = false;
673 return ev.Validate();
676 internal static System.Collections.ObjectModel.ReadOnlyCollection<DbSortClause> ValidateSkip(DbExpressionBinding input, IEnumerable<DbSortClause> sortOrder, DbExpression count)
679 // Validate the input expression binding and sort keys
681 var sortKeys = ValidateSortArguments(input, sortOrder);
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.
687 EntityUtil.CheckArgumentNull(count, "count");
688 if (!TypeSemantics.IsIntegerNumericType(count.ResultType))
690 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Skip_IntegerRequired, "count");
694 // Currently the Count expression is also required to be either a DbConstantExpression or a DbParameterReferenceExpression.
696 if (count.ExpressionKind != DbExpressionKind.Constant &&
697 count.ExpressionKind != DbExpressionKind.ParameterReference)
699 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Skip_ConstantOrParameterRefRequired, "count");
703 // For constants, verify the count is non-negative.
705 if (ArgumentValidation.IsConstantNegativeInteger(count))
707 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Skip_NonNegativeCountRequired, "count");
713 internal static System.Collections.ObjectModel.ReadOnlyCollection<DbSortClause> ValidateSort(DbExpressionBinding input, IEnumerable<DbSortClause> sortOrder)
716 // Validate the input expression binding and sort keys
718 return ValidateSortArguments(input, sortOrder);
723 #region Leaf Expressions - Null, Constant, Parameter, Scan
725 internal static void ValidateNull(TypeUsage nullType)
727 CheckType(nullType, "nullType");
730 internal static TypeUsage ValidateConstant(object value)
732 EntityUtil.CheckArgumentNull(value, "value");
735 // Check that typeof(value) is actually a valid constant (i.e. primitive) type
737 PrimitiveTypeKind primitiveTypeKind;
738 if (!ArgumentValidation.TryGetPrimitiveTypeKind(value.GetType(), out primitiveTypeKind))
740 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Constant_InvalidType, "value");
743 return TypeHelpers.GetLiteralTypeUsage(primitiveTypeKind);
746 internal static void ValidateConstant(TypeUsage constantType, object value)
749 // Basic validation of constant value and constant type (non-null, read-only, etc)
751 EntityUtil.CheckArgumentNull(value, "value");
752 ArgumentValidation.CheckType(constantType, "constantType");
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.
759 EnumType edmEnumType;
760 if(TypeHelpers.TryGetEdmType<EnumType>(constantType, out edmEnumType))
762 var clrEnumUnderlyingType = edmEnumType.UnderlyingType.ClrEquivalentType;
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()))
767 throw EntityUtil.Argument(
768 System.Data.Entity.Strings.Cqt_Constant_ClrEnumTypeDoesNotMatchEdmEnumType(
769 value.GetType().Name,
771 clrEnumUnderlyingType.Name),
777 PrimitiveType primitiveType;
778 if (!TypeHelpers.TryGetEdmType<PrimitiveType>(constantType, out primitiveType))
780 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Constant_InvalidConstantType(constantType.ToString()), "constantType");
783 PrimitiveTypeKind valueKind;
784 if (!ArgumentValidation.TryGetPrimitiveTypeKind(value.GetType(), out valueKind) ||
785 primitiveType.PrimitiveTypeKind != valueKind)
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))
792 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Constant_InvalidValueForType(constantType.ToString()), "value");
798 internal static void ValidateParameter(TypeUsage type, string name)
800 ArgumentValidation.CheckType(type);
802 EntityUtil.CheckArgumentNull(name, "name");
803 if (!DbCommandTree.IsValidParameterName(name))
805 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_CommandTree_InvalidParameterName(name), "name");
809 internal static TypeUsage ValidateScan(EntitySetBase entitySet)
811 ArgumentValidation.CheckEntitySet(entitySet, "targetSet");
812 return ArgumentValidation.CreateCollectionResultType(entitySet.ElementType);
815 internal static void ValidateVariable(TypeUsage type, string name)
819 EntityUtil.CheckArgumentNull(name, "name");
820 if (string.IsNullOrEmpty(name))
822 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_VariableNameNotValid, "name");
828 #region Boolean Operators - And, Or, Not
830 internal static TypeUsage ValidateAnd(DbExpression left, DbExpression right)
832 TypeUsage resultType = ValidateBinary(left, right);
833 if (null == resultType || !TypeSemantics.IsPrimitiveType(resultType, PrimitiveTypeKind.Boolean))
835 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_And_BooleanArgumentsRequired);
841 internal static TypeUsage ValidateOr(DbExpression left, DbExpression right)
843 TypeUsage resultType = ValidateBinary(left, right);
844 if (null == resultType || !TypeSemantics.IsPrimitiveType(resultType, PrimitiveTypeKind.Boolean))
846 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Or_BooleanArgumentsRequired);
852 internal static TypeUsage ValidateNot(DbExpression argument)
854 EntityUtil.CheckArgumentNull(argument, "argument");
857 // Argument to Not must have Boolean result type
859 if (!TypeSemantics.IsPrimitiveType(argument.ResultType, PrimitiveTypeKind.Boolean))
861 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Not_BooleanArgumentRequired);
864 return argument.ResultType;
869 #region Arithmetic Operators
871 internal static DbExpressionList ValidateArithmetic(DbExpression argument, out TypeUsage resultType)
873 ValidateUnary(argument);
874 resultType = argument.ResultType;
875 if (!TypeSemantics.IsNumericType(resultType))
878 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Arithmetic_NumericCommonType);
880 //If argument to UnaryMinus is an unsigned type, promote return type to next higher, signed type.
881 if (TypeSemantics.IsUnsignedNumericType(argument.ResultType))
883 TypeUsage closestPromotableType = null;
884 if (TypeHelpers.TryGetClosestPromotableType(argument.ResultType, out closestPromotableType))
886 resultType = closestPromotableType;
890 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Arithmetic_InvalidUnsignedTypeForUnaryMinus(argument.ResultType.EdmType.FullName));
893 return new DbExpressionList(new[] { argument });
896 internal static DbExpressionList ValidateArithmetic(DbExpression left, DbExpression right, out TypeUsage resultType)
898 resultType = ValidateBinary(left, right);
899 if (null == resultType || !TypeSemantics.IsNumericType(resultType))
901 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Arithmetic_NumericCommonType);
904 return new DbExpressionList(new[] { left, right });
911 internal static TypeUsage ValidateComparison(DbExpressionKind kind, DbExpression left, DbExpression right)
913 EntityUtil.CheckArgumentNull(left, "left");
914 EntityUtil.CheckArgumentNull(right, "right");
917 // A comparison of the specified kind must exist between the left and right arguments
919 bool equality = true;
921 if (DbExpressionKind.GreaterThanOrEquals == kind ||
922 DbExpressionKind.LessThanOrEquals == kind)
924 equality = TypeSemantics.IsEqualComparableTo(left.ResultType, right.ResultType);
925 order = TypeSemantics.IsOrderComparableTo(left.ResultType, right.ResultType);
927 else if (DbExpressionKind.Equals == kind ||
928 DbExpressionKind.NotEquals == kind)
930 equality = TypeSemantics.IsEqualComparableTo(left.ResultType, right.ResultType);
934 order = TypeSemantics.IsOrderComparableTo(left.ResultType, right.ResultType);
937 if (!equality || !order)
939 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Comparison_ComparableRequired);
945 internal static TypeUsage ValidateIsNull(DbExpression argument)
947 return ValidateIsNull(argument, false);
950 internal static TypeUsage ValidateIsNull(DbExpression argument, bool allowRowType)
952 EntityUtil.CheckArgumentNull(argument, "argument");
955 // The argument cannot be of a collection type
957 if (TypeSemantics.IsCollectionType(argument.ResultType))
959 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_IsNull_CollectionNotAllowed);
963 // ensure argument type is valid for this operation
965 if (!TypeHelpers.IsValidIsNullOpType(argument.ResultType))
968 if (!allowRowType || !TypeSemantics.IsRowType(argument.ResultType))
970 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_IsNull_InvalidType);
977 internal static TypeUsage ValidateLike(DbExpression argument, DbExpression pattern)
979 EntityUtil.CheckArgumentNull(argument, "argument");
980 EntityUtil.CheckArgumentNull(pattern, "pattern");
982 RequireCompatibleType(argument, PrimitiveTypeKind.String, "argument");
983 RequireCompatibleType(pattern, PrimitiveTypeKind.String, "pattern");
988 internal static TypeUsage ValidateLike(DbExpression argument, DbExpression pattern, DbExpression escape)
990 TypeUsage resultType = ValidateLike(argument, pattern);
992 EntityUtil.CheckArgumentNull(escape, "escape");
993 RequireCompatibleType(escape, PrimitiveTypeKind.String, "escape");
1000 #region Type Operators - Cast, Treat, OfType, OfTypeOnly, IsOf, IsOfOnly
1002 internal static void ValidateCastTo(DbExpression argument, TypeUsage toType)
1004 ValidateTypeUnary(argument, toType, "toType");
1007 // Verify that the cast is allowed
1009 if (!TypeSemantics.IsCastAllowed(argument.ResultType, toType))
1011 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Cast_InvalidCast(TypeHelpers.GetFullName(argument.ResultType), TypeHelpers.GetFullName(toType)));
1015 internal static void ValidateTreatAs(DbExpression argument, TypeUsage asType)
1017 ValidateTypeUnary(argument, asType, "asType");
1020 // Verify the type to treat as. Treat-As (NullType) is not allowed.
1022 RequirePolymorphicType(asType, "asType");
1025 // Verify that the Treat operation is allowed
1027 if (!TypeSemantics.IsValidPolymorphicCast(argument.ResultType, asType))
1029 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicArgRequired(typeof(DbTreatExpression).Name));
1033 internal static TypeUsage ValidateOfType(DbExpression argument, TypeUsage type)
1035 ValidateTypeUnary(argument, type, "type");
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.
1041 RequirePolymorphicType(type, "type");
1044 // Ensure that the argument is actually of a collection type.
1046 RequireCollectionArgument<DbOfTypeExpression>(argument);
1049 // Verify that the OfType operation is allowed
1051 TypeUsage elementType = null;
1052 if (!TypeHelpers.TryGetCollectionElementType(argument.ResultType, out elementType) ||
1053 !TypeSemantics.IsValidPolymorphicCast(elementType, type))
1055 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicArgRequired(typeof(DbOfTypeExpression).Name));
1059 // The type of this DbExpression is a new collection type based on the requested element type.
1061 return CreateCollectionResultType(type);
1064 internal static TypeUsage ValidateIsOf(DbExpression argument, TypeUsage type)
1066 ValidateTypeUnary(argument, type, "type");
1069 // Ensure the ofType is non-null, associated with the correct metadata workspace/dataspace,
1070 // is not NullType, and is polymorphic
1072 RequirePolymorphicType(type, "type");
1075 // Verify that the IsOf operation is allowed
1077 if (!TypeSemantics.IsValidPolymorphicCast(argument.ResultType, type))
1079 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicArgRequired(typeof(DbIsOfExpression).Name));
1082 return _booleanType;
1087 #region Ref Operators - Deref, EntityRef, Ref, RefKey, RelationshipNavigation
1089 internal static TypeUsage ValidateDeref(DbExpression argument)
1091 ValidateUnary(argument);
1094 // Ensure that the operand is actually of a reference type.
1096 EntityType entityType;
1097 if (!TypeHelpers.TryGetRefEntityType(argument.ResultType, out entityType))
1099 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_DeRef_RefRequired, "argument");
1103 // Result Type is the element type of the reference type
1105 return CreateResultType(entityType);
1108 internal static TypeUsage ValidateGetEntityRef(DbExpression argument)
1110 ValidateUnary(argument);
1112 EntityType entityType = null;
1113 if (!TypeHelpers.TryGetEdmType<EntityType>(argument.ResultType, out entityType) || null == entityType)
1115 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GetEntityRef_EntityRequired, "argument");
1118 return CreateReferenceResultType(entityType);
1121 internal static TypeUsage ValidateCreateRef(EntitySet entitySet, IEnumerable<DbExpression> keyValues, out DbExpression keyConstructor)
1123 EntityUtil.CheckArgumentNull(entitySet, "entitySet");
1124 return ValidateCreateRef(entitySet, entitySet.ElementType, keyValues, out keyConstructor);
1127 internal static TypeUsage ValidateCreateRef(EntitySet entitySet, EntityType entityType, IEnumerable<DbExpression> keyValues, out DbExpression keyConstructor)
1129 CheckEntitySet(entitySet, "entitySet");
1130 CheckType(entityType, "entityType");
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.
1136 if (!TypeSemantics.IsValidPolymorphicCast(entitySet.ElementType, entityType))
1138 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Ref_PolymorphicArgRequired);
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",
1148 RequireCompatibleType(valueExp, keyMembers[idx].TypeUsage, "keyValues", idx);
1149 return new KeyValuePair<string, DbExpression>(keyMembers[idx].Name, valueExp);
1151 (columnList) => columnList
1153 keyValueValidator.ExpectedElementCount = keyMembers.Count;
1154 var keyColumns = keyValueValidator.Validate();
1156 keyConstructor = DbExpressionBuilder.NewRow(keyColumns);
1157 return CreateReferenceResultType(entityType);
1160 internal static TypeUsage ValidateRefFromKey(EntitySet entitySet, DbExpression keyValues)
1162 EntityUtil.CheckArgumentNull(entitySet, "entitySet");
1163 return ValidateRefFromKey(entitySet, keyValues, entitySet.ElementType);
1166 internal static TypeUsage ValidateRefFromKey(EntitySet entitySet, DbExpression keyValues, EntityType entityType)
1168 CheckEntitySet(entitySet, "entitySet");
1169 EntityUtil.CheckArgumentNull(keyValues, "keyValues");
1170 CheckType(entityType);
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.
1176 if (!TypeSemantics.IsValidPolymorphicCast(entitySet.ElementType, entityType))
1178 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Ref_PolymorphicArgRequired);
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
1190 // Set the expected type to be the record type created based on the Key members
1192 TypeUsage keyType = CreateResultType(TypeHelpers.CreateKeyRowType(entitySet.ElementType));
1193 RequireCompatibleType(keyValues, keyType, "keyValues");
1195 return CreateReferenceResultType(entityType);
1198 internal static TypeUsage ValidateGetRefKey(DbExpression argument)
1200 ValidateUnary(argument);
1202 RefType refType = null;
1203 if (!TypeHelpers.TryGetEdmType<RefType>(argument.ResultType, out refType) || null == refType)
1205 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GetRefKey_RefRequired, "argument");
1208 // RefType is responsible for basic validation of ElementType
1209 Debug.Assert(refType.ElementType != null, "RefType constructor allowed null ElementType?");
1211 return CreateResultType(TypeHelpers.CreateKeyRowType(refType.ElementType));
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)
1217 EntityUtil.CheckArgumentNull(navigateFrom, "navigateFrom");
1220 // Ensure that the relation type is non-null and from the same metadata workspace as the command tree
1225 // Verify that the from and to relation end names are not null
1227 EntityUtil.CheckArgumentNull(fromEndName, "fromEndName");
1228 EntityUtil.CheckArgumentNull(toEndName, "toEndName");
1231 // Retrieve the relation end properties with the specified 'from' and 'to' names
1233 if (!type.RelationshipEndMembers.TryGetValue(fromEndName, false /*ignoreCase*/, out fromEnd))
1235 throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Factory_NoSuchRelationEnd, fromEndName);
1238 if (!type.RelationshipEndMembers.TryGetValue(toEndName, false /*ignoreCase*/, out toEnd))
1240 throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Factory_NoSuchRelationEnd, toEndName);
1244 // Validate the retrieved relation end against the navigation source
1246 RequireCompatibleType(navigateFrom, fromEnd, allowAllRelationshipsInSameTypeHierarchy: false);
1248 return CreateResultType(toEnd);
1251 internal static TypeUsage ValidateNavigate(DbExpression navigateFrom, RelationshipEndMember fromEnd, RelationshipEndMember toEnd, out RelationshipType relType, bool allowAllRelationshipsInSameTypeHierarchy)
1253 EntityUtil.CheckArgumentNull(navigateFrom, "navigateFrom");
1256 // Validate the relationship ends before use
1258 CheckMember(fromEnd, "fromEnd");
1259 CheckMember(toEnd, "toEnd");
1261 relType = fromEnd.DeclaringType as RelationshipType;
1264 // Ensure that the relation type is non-null and read-only
1269 // Validate that the 'to' relationship end is defined by the same relationship type as the 'from' end
1271 if (!relType.Equals(toEnd.DeclaringType))
1273 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Factory_IncompatibleRelationEnds, "toEnd");
1276 RequireCompatibleType(navigateFrom, fromEnd, allowAllRelationshipsInSameTypeHierarchy);
1278 return CreateResultType(toEnd);
1283 #region Unary and Binary Set Operators - Distinct, Element, IsEmpty, Except, Intersect, UnionAll, Limit
1285 internal static TypeUsage ValidateDistinct(DbExpression argument)
1287 ValidateUnary(argument);
1290 // Ensure that the Argument is of a collection type
1292 RequireCollectionArgument<DbDistinctExpression>(argument);
1295 // Ensure that the Distinct operation is valid for the input
1297 CollectionType inputType = TypeHelpers.GetEdmType<CollectionType>(argument.ResultType);
1298 if (!TypeHelpers.IsValidDistinctOpType(inputType.TypeUsage))
1300 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Distinct_InvalidCollection, "argument");
1303 return argument.ResultType;
1306 internal static TypeUsage ValidateElement(DbExpression argument)
1308 ValidateUnary(argument);
1311 // Ensure that the operand is actually of a collection type.
1313 RequireCollectionArgument<DbElementExpression>(argument);
1316 // Result Type is the element type of the collection type
1318 return TypeHelpers.GetEdmType<CollectionType>(argument.ResultType).TypeUsage;
1321 internal static TypeUsage ValidateIsEmpty(DbExpression argument)
1323 ValidateUnary(argument);
1326 // Ensure that the Argument is of a collection type
1328 RequireCollectionArgument<DbIsEmptyExpression>(argument);
1330 return _booleanType;
1333 internal static TypeUsage ValidateExcept(DbExpression left, DbExpression right)
1335 ValidateBinary(left, right);
1338 // Ensures the left and right operands are each of a comparable collection type
1340 RequireComparableCollectionArguments<DbExceptExpression>(left, right);
1342 return left.ResultType;
1345 internal static TypeUsage ValidateIntersect(DbExpression left, DbExpression right)
1347 ValidateBinary(left, right);
1350 // Ensures the left and right operands are each of a comparable collection type
1352 return RequireComparableCollectionArguments<DbIntersectExpression>(left, right);
1355 internal static TypeUsage ValidateUnionAll(DbExpression left, DbExpression right)
1357 ValidateBinary(left, right);
1360 // Ensure that the left and right operands are each of a collection type and that a common type exists for those types.
1362 return RequireCollectionArguments<DbUnionAllExpression>(left, right);
1365 internal static TypeUsage ValidateLimit(DbExpression argument, DbExpression limit)
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.
1371 EntityUtil.CheckArgumentNull(argument, "argument");
1372 RequireCollectionArgument<DbLimitExpression>(argument);
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.
1378 EntityUtil.CheckArgumentNull(limit, "count");
1379 if (!TypeSemantics.IsIntegerNumericType(limit.ResultType))
1381 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Limit_IntegerRequired, "limit");
1385 // Currently the Limit expression is also required to be either a DbConstantExpression or a DbParameterReferenceExpression.
1387 if (limit.ExpressionKind != DbExpressionKind.Constant &&
1388 limit.ExpressionKind != DbExpressionKind.ParameterReference)
1390 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Limit_ConstantOrParameterRefRequired, "limit");
1394 // For constants, verify the limit is non-negative.
1396 if (ArgumentValidation.IsConstantNegativeInteger(limit))
1398 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Limit_NonNegativeLimitRequired, "limit");
1401 return argument.ResultType;
1405 #region General Operators - Case, Function, NewInstance, Property
1407 internal static TypeUsage ValidateCase(IEnumerable<DbExpression> whenExpressions, IEnumerable<DbExpression> thenExpressions, DbExpression elseExpression, out DbExpressionList validWhens, out DbExpressionList validThens)
1409 EntityUtil.CheckArgumentNull(whenExpressions, "whenExpressions");
1410 EntityUtil.CheckArgumentNull(thenExpressions, "thenExpressions");
1411 EntityUtil.CheckArgumentNull(elseExpression, "elseExpression");
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.
1418 validWhens = CreateExpressionList(whenExpressions, "whenExpressions", (exp, idx) =>
1420 RequireCompatibleType(exp, PrimitiveTypeKind.Boolean, "whenExpressions", idx);
1423 Debug.Assert(validWhens.Count > 0, "CreateExpressionList(arguments, argumentName, validationCallback) allowed empty Whens?");
1425 TypeUsage commonResultType = null;
1426 validThens = CreateExpressionList(thenExpressions, "thenExpressions", (exp, idx) =>
1428 if (null == commonResultType)
1430 commonResultType = exp.ResultType;
1434 commonResultType = TypeHelpers.GetCommonTypeUsage(exp.ResultType, commonResultType);
1435 if (null == commonResultType)
1437 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Case_InvalidResultType);
1442 Debug.Assert(validWhens.Count > 0, "CreateExpressionList(arguments, argumentName, validationCallback) allowed empty Thens?");
1444 commonResultType = TypeHelpers.GetCommonTypeUsage(elseExpression.ResultType, commonResultType);
1445 if (null == commonResultType)
1447 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Case_InvalidResultType);
1451 // The number of 'When's must equal the number of 'Then's.
1453 if (validWhens.Count != validThens.Count)
1455 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Case_WhensMustEqualThens);
1459 // The result type of DbCaseExpression is the common result type
1461 return commonResultType;
1464 internal static TypeUsage ValidateFunction(EdmFunction function, IEnumerable<DbExpression> arguments, out DbExpressionList validArgs)
1467 // Ensure that the function metadata is non-null and from the same metadata workspace and dataspace as the command tree.
1468 CheckFunction(function);
1471 // Non-composable functions or non-UDF functions including command text are not permitted in expressions -- they can only be
1472 // executed independently
1474 if (!function.IsComposableAttribute)
1476 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Function_NonComposableInExpression, "function");
1478 if (!String.IsNullOrEmpty(function.CommandTextAttribute) && !function.HasUserDefinedBody)
1480 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Function_CommandTextInExpression, "function");
1484 // Functions that return void are not allowed
1486 if (null == function.ReturnParameter)
1488 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Function_VoidResultInvalid, "function");
1492 // Validate the arguments
1494 FunctionParameter[] expectedParams = GetExpectedParameters(function);
1495 validArgs = CreateExpressionList(arguments, "arguments", expectedParams.Length, (exp, idx) =>
1497 ArgumentValidation.RequireCompatibleType(exp, expectedParams[idx].TypeUsage, "arguments", idx);
1501 return function.ReturnParameter.TypeUsage;
1504 internal static TypeUsage ValidateInvoke(DbLambda lambda, IEnumerable<DbExpression> arguments, out DbExpressionList validArguments)
1506 EntityUtil.CheckArgumentNull(lambda, "lambda");
1507 EntityUtil.CheckArgumentNull(arguments, "arguments");
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) =>
1513 RequireCompatibleType(exp, lambda.Variables[idx].ResultType, "arguments", idx);
1516 expList => new DbExpressionList(expList)
1518 argValidator.ExpectedElementCount = lambda.Variables.Count;
1519 validArguments = argValidator.Validate();
1521 // The result type of the lambda expression is the result type of the lambda body
1522 return lambda.Body.ResultType;
1525 internal static TypeUsage ValidateNewCollection(IEnumerable<DbExpression> elements, out DbExpressionList validElements)
1527 TypeUsage commonElementType = null;
1528 validElements = CreateExpressionList(elements, "elements", (exp, idx) =>
1530 if (commonElementType == null)
1532 commonElementType = exp.ResultType;
1536 commonElementType = TypeSemantics.GetCommonType(commonElementType, exp.ResultType);
1539 if (null == commonElementType)
1541 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Factory_NewCollectionInvalidCommonType, "collectionElements");
1546 Debug.Assert(validElements.Count > 0, "CreateExpressionList(arguments, argumentName, validationCallback) allowed empty elements list?");
1548 return CreateCollectionResultType(commonElementType);
1551 internal static TypeUsage ValidateNewEmptyCollection(TypeUsage collectionType, out DbExpressionList validElements)
1553 CheckType(collectionType, "collectionType");
1554 if (!TypeSemantics.IsCollectionType(collectionType))
1556 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_CollectionTypeRequired, "collectionType");
1565 validElements = new DbExpressionList(new DbExpression[] { });
1566 return collectionType;
1569 internal static TypeUsage ValidateNewRow(IEnumerable<KeyValuePair<string, DbExpression>> columnValues, out DbExpressionList validElements)
1571 List<KeyValuePair<string, TypeUsage>> columnTypes = new List<KeyValuePair<string, TypeUsage>>();
1572 var columnValidator = CreateValidator(columnValues, "columnValues", (columnValue, idx) =>
1574 CheckNamed(columnValue, "columnValues", idx);
1575 columnTypes.Add(new KeyValuePair<string, TypeUsage>(columnValue.Key, columnValue.Value.ResultType));
1576 return columnValue.Value;
1578 expList => new DbExpressionList(expList)
1580 columnValidator.GetName = ((columnValue, idx) => columnValue.Key);
1581 validElements = columnValidator.Validate();
1582 return CreateResultType(TypeHelpers.CreateRowType(columnTypes));
1585 internal static TypeUsage ValidateNew(TypeUsage instanceType, IEnumerable<DbExpression> arguments, out DbExpressionList validArguments)
1588 // Ensure that the type is non-null, valid and not NullType
1590 CheckType(instanceType, "instanceType");
1592 CollectionType collectionType = null;
1593 if (TypeHelpers.TryGetEdmType<CollectionType>(instanceType, out collectionType) &&
1594 collectionType != null)
1596 // Collection arguments may have zero count for empty collection construction
1597 TypeUsage elementType = collectionType.TypeUsage;
1598 validArguments = CreateExpressionList(arguments, "arguments", true, (exp, idx) =>
1600 RequireCompatibleType(exp, elementType, "arguments", idx);
1605 List<TypeUsage> expectedTypes = GetStructuralMemberTypes(instanceType);
1607 validArguments = CreateExpressionList(arguments, "arguments", expectedTypes.Count, (exp, idx) =>
1609 RequireCompatibleType(exp, expectedTypes[pos++], "arguments", idx);
1613 return instanceType;
1616 private static List<TypeUsage> GetStructuralMemberTypes(TypeUsage instanceType)
1618 StructuralType structType = instanceType.EdmType as StructuralType;
1619 if (null == structType)
1621 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_StructuralTypeRequired, "instanceType");
1624 if (structType.Abstract)
1626 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_CannotInstantiateAbstractType(TypeHelpers.GetFullName(instanceType)), "instanceType");
1629 var members = TypeHelpers.GetAllStructuralMembers(structType);
1630 if (members == null || members.Count < 1)
1632 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_CannotInstantiateMemberlessType(TypeHelpers.GetFullName(instanceType)), "instanceType");
1635 List<TypeUsage> memberTypes = new List<TypeUsage>(members.Count);
1636 for (int idx = 0; idx < members.Count; idx++)
1638 memberTypes.Add(Helper.GetModelTypeUsage(members[idx]));
1643 internal static TypeUsage ValidateNewEntityWithRelationships(EntityType entityType, IEnumerable<DbExpression> attributeValues, IList<DbRelatedEntityRef> relationships, out DbExpressionList validArguments, out System.Collections.ObjectModel.ReadOnlyCollection<DbRelatedEntityRef> validRelatedRefs)
1645 EntityUtil.CheckArgumentNull(entityType, "entityType");
1646 EntityUtil.CheckArgumentNull(attributeValues, "attributeValues");
1647 EntityUtil.CheckArgumentNull(relationships, "relationships");
1649 TypeUsage resultType = CreateResultType(entityType);
1650 resultType = ArgumentValidation.ValidateNew(resultType, attributeValues, out validArguments);
1652 if (relationships.Count > 0)
1654 List<DbRelatedEntityRef> relatedRefs = new List<DbRelatedEntityRef>(relationships.Count);
1655 for (int idx = 0; idx < relationships.Count; idx++)
1657 DbRelatedEntityRef relatedRef = relationships[idx];
1658 EntityUtil.CheckArgumentNull(relatedRef, StringUtil.FormatIndex("relationships", idx));
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;
1663 if (!entityType.EdmEquals(expectedSourceType) &&
1664 !entityType.IsSubtypeOf(expectedSourceType))
1666 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_IncompatibleRelatedEntity_SourceTypeNotValid, StringUtil.FormatIndex("relationships", idx));
1669 relatedRefs.Add(relatedRef);
1671 validRelatedRefs = relatedRefs.AsReadOnly();
1675 validRelatedRefs = new System.Collections.ObjectModel.ReadOnlyCollection<DbRelatedEntityRef>(new DbRelatedEntityRef[] { });
1681 internal static TypeUsage ValidateProperty(DbExpression instance, EdmMember property, string propertyArgumentName)
1684 // Validate the member
1686 CheckMember(property, propertyArgumentName);
1689 // Validate the instance
1691 if (null == instance)
1693 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Property_InstanceRequiredForInstance, "instance");
1696 TypeUsage expectedInstanceType = TypeUsage.Create(property.DeclaringType);
1697 RequireCompatibleType(instance, expectedInstanceType, "instance");
1699 Debug.Assert(null != Helper.GetModelTypeUsage(property), "EdmMember metadata has a TypeUsage of null");
1701 return Helper.GetModelTypeUsage(property);
1704 internal static TypeUsage ValidateProperty(DbExpression instance, string propertyName, bool ignoreCase, out EdmMember foundMember)
1706 EntityUtil.CheckArgumentNull(instance, "instance");
1707 EntityUtil.CheckArgumentNull(propertyName, "propertyName");
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.
1714 StructuralType structType;
1715 if (TypeHelpers.TryGetEdmType<StructuralType>(instance.ResultType, out structType))
1718 // Does the type declare a member with the given name?
1720 if (structType.Members.TryGetValue(propertyName, ignoreCase, out foundMember) &&
1721 foundMember != null)
1724 // If the member is a RelationshipEndMember, call the corresponding overload.
1726 if (Helper.IsRelationshipEndMember(foundMember) ||
1727 Helper.IsEdmProperty(foundMember) ||
1728 Helper.IsNavigationProperty(foundMember))
1730 return Helper.GetModelTypeUsage(foundMember);
1735 throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Factory_NoSuchProperty(propertyName, TypeHelpers.GetFullName(instance.ResultType)), "propertyName");
1740 private static void CheckNamed<T>(KeyValuePair<string, T> element, string argumentName, int index)
1742 if (string.IsNullOrEmpty(element.Key))
1746 argumentName = StringUtil.FormatIndex(argumentName, index);
1748 throw EntityUtil.ArgumentNull(string.Format(CultureInfo.InvariantCulture, "{0}.Key", argumentName));
1751 if (null == element.Value)
1755 argumentName = StringUtil.FormatIndex(argumentName, index);
1757 throw EntityUtil.ArgumentNull(string.Format(CultureInfo.InvariantCulture, "{0}.Value", argumentName));
1761 private static void CheckReadOnly(GlobalItem item, string varName)
1763 EntityUtil.CheckArgumentNull(item, varName);
1764 if (!(item.IsReadOnly))
1766 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_MetadataNotReadOnly, varName);
1770 private static void CheckReadOnly(TypeUsage item, string varName)
1772 EntityUtil.CheckArgumentNull(item, varName);
1773 if (!(item.IsReadOnly))
1775 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_MetadataNotReadOnly, varName);
1779 private static void CheckReadOnly(EntitySetBase item, string varName)
1781 EntityUtil.CheckArgumentNull(item, varName);
1782 if (!(item.IsReadOnly))
1784 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_MetadataNotReadOnly, varName);
1788 private static void CheckType(EdmType type)
1790 CheckType(type, "type");
1793 private static void CheckType(EdmType type, string argumentName)
1795 EntityUtil.CheckArgumentNull(type, argumentName);
1796 CheckReadOnly(type, argumentName);
1800 /// Ensures that the specified type is non-null, associated with the correct metadata workspace/dataspace, and is not NullType.
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)
1808 CheckType(type, "type");
1811 private static void CheckType(TypeUsage type, string varName)
1813 EntityUtil.CheckArgumentNull(type, varName);
1814 CheckReadOnly(type, varName);
1816 // TypeUsage constructor is responsible for basic validation of EdmType
1817 Debug.Assert(type.EdmType != null, "TypeUsage constructor allowed null EdmType?");
1819 if (!CheckDataSpace(type))
1821 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_TypeUsageIncorrectSpace, "type");
1826 /// Verifies that the specified member is valid - non-null, from the same metadata workspace and data space as the command tree, etc
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)
1832 EntityUtil.CheckArgumentNull(memberMeta, varName);
1833 CheckReadOnly(memberMeta.DeclaringType, varName);
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))
1841 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EdmMemberIncorrectSpace, varName);
1845 private static void CheckParameter(FunctionParameter paramMeta, string varName)
1847 EntityUtil.CheckArgumentNull(paramMeta, varName);
1848 CheckReadOnly(paramMeta.DeclaringFunction, varName);
1850 // FunctionParameter constructor is responsible for basic validation
1851 Debug.Assert(paramMeta.Name != null, "FunctionParameter constructor allowed null name?");
1853 // Verify that the parameter is from the same workspace as the DbCommandTree
1854 if (!CheckDataSpace(paramMeta.TypeUsage))
1856 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionParameterIncorrectSpace, varName);
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)
1863 /// <param name="function">The function metadata to verify</param>
1864 private static void CheckFunction(EdmFunction function)
1866 EntityUtil.CheckArgumentNull(function, "function");
1867 CheckReadOnly(function, "function");
1869 Debug.Assert(function.Name != null, "EdmType constructor allowed null name?");
1871 if (!CheckDataSpace(function))
1873 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionIncorrectSpace, "function");
1876 // Composable functions must have a return parameter.
1877 if (function.IsComposableAttribute && null == function.ReturnParameter)
1879 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionReturnParameterNull, "function");
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)
1886 if (!CheckDataSpace(function.ReturnParameter.TypeUsage))
1888 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionParameterIncorrectSpace, "function.ReturnParameter");
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?");
1897 for (int idx = 0; idx < functionParams.Count; idx++)
1899 CheckParameter(functionParams[idx], StringUtil.FormatIndex("function.Parameters", idx));
1904 /// Verifies that the specified EntitySet is valid with respect to the command tree
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)
1910 EntityUtil.CheckArgumentNull(entitySet, varName);
1911 CheckReadOnly(entitySet, varName);
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?");
1917 // Verify the Extent's Container
1919 if (null == entitySet.EntityContainer)
1921 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EntitySetEntityContainerNull, varName);
1924 if(!CheckDataSpace(entitySet.EntityContainer))
1926 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EntitySetIncorrectSpace, varName);
1930 // Verify the Extent's Entity Type
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?");
1935 if(!CheckDataSpace(entitySet.ElementType))
1937 throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EntitySetIncorrectSpace, varName);
1941 private static bool CheckDataSpace(TypeUsage type)
1943 return CheckDataSpace(type.EdmType);
1946 private static bool CheckDataSpace(GlobalItem item)
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))
1957 // Transient types should be checked according to their non-transient element types
1958 if (Helper.IsRowType(item))
1960 foreach (EdmProperty prop in ((RowType)item).Properties)
1962 if (!CheckDataSpace(prop.TypeUsage))
1970 else if (Helper.IsCollectionType(item))
1972 return CheckDataSpace(((CollectionType)item).TypeUsage);
1974 else if (Helper.IsRefType(item))
1976 return CheckDataSpace(((RefType)item).ElementType);
1980 return (item.DataSpace == DataSpace.SSpace || item.DataSpace == DataSpace.CSpace);
1984 private static TypeUsage CreateCollectionOfRowResultType(List<KeyValuePair<string, TypeUsage>> columns)
1986 TypeUsage retUsage = TypeUsage.Create(
1987 TypeHelpers.CreateCollectionType(
1989 TypeHelpers.CreateRowType(columns)
1997 private static TypeUsage CreateCollectionResultType(EdmType type)
1999 TypeUsage retUsage = TypeUsage.Create(
2000 TypeHelpers.CreateCollectionType(
2001 TypeUsage.Create(type)
2008 private static TypeUsage CreateCollectionResultType(TypeUsage type)
2010 TypeUsage retUsage = TypeUsage.Create(TypeHelpers.CreateCollectionType(type));
2014 private static TypeUsage CreateResultType(EdmType resultType)
2016 return TypeUsage.Create(resultType);
2019 private static TypeUsage CreateResultType(RelationshipEndMember end)
2021 TypeUsage retType = end.TypeUsage;
2022 if (!TypeSemantics.IsReferenceType(retType))
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.
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");
2030 retType = TypeHelpers.CreateReferenceTypeUsage(TypeHelpers.GetEdmType<EntityType>(retType));
2034 // If the upper bound is not 1 the result type is a collection of the given type
2036 if (RelationshipMultiplicity.Many == end.RelationshipMultiplicity)
2038 retType = TypeHelpers.CreateCollectionTypeUsage(retType);
2044 private static TypeUsage CreateReferenceResultType(EntityTypeBase referencedEntityType)
2046 return TypeUsage.Create(TypeHelpers.CreateReferenceType(referencedEntityType));
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.
2054 private static bool IsConstantNegativeInteger(DbExpression expression)
2056 return (expression.ExpressionKind == DbExpressionKind.Constant &&
2057 TypeSemantics.IsIntegerNumericType(expression.ResultType) &&
2058 Convert.ToInt64(((DbConstantExpression)expression).Value, CultureInfo.InvariantCulture) < 0);
2061 private static bool TryGetPrimitiveTypeKind(Type clrType, out PrimitiveTypeKind primitiveTypeKind)
2063 return ClrProviderManifest.Instance.TryGetPrimitiveTypeKind(clrType, out primitiveTypeKind);
2067 /// Checks whether the clr enum type matched the edm enum type.
2069 /// <param name="edmEnumType">Edm enum type.</param>
2070 /// <param name="clrEnumType">Clr enum type.</param>
2072 /// <c>true</c> if types match otherwise <c>false</c>.
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
2082 private static bool ClrEdmEnumTypesMatch(EnumType edmEnumType, Type clrEnumType)
2084 Debug.Assert(edmEnumType != null, "edmEnumType != null");
2085 Debug.Assert(clrEnumType != null, "clrEnumType != null");
2086 Debug.Assert(clrEnumType.IsEnum, "non enum clr type.");
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)
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)
2103 // check that all the members have the same names and values
2104 foreach (var edmEnumTypeMember in edmEnumType.Members)
2107 edmEnumTypeMember.Value.GetType() == clrEnumType.GetEnumUnderlyingType(),
2108 "Enum underlying types matched so types of member values must match the enum underlying type as well");
2110 if (!clrEnumType.GetEnumNames().Contains(edmEnumTypeMember.Name)
2111 || !edmEnumTypeMember.Value.Equals(
2112 Convert.ChangeType(Enum.Parse(clrEnumType, edmEnumTypeMember.Name), clrEnumType.GetEnumUnderlyingType(), CultureInfo.InvariantCulture)))