Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataEntity / System / Data / Common / CommandTrees / ExpressionBuilder / Internal / ArgumentValidation.cs / 1305376 / ArgumentValidation.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Data.Metadata.Edm; // for TypeHelpers using System.Data.Common.CommandTrees; using System.Data.Common; using System.Data.Common.CommandTrees.Internal; using System.Linq; using System.Data.Common.Utils; namespace System.Data.Common.CommandTrees.ExpressionBuilder.Internal { internal static class ArgumentValidation { private static TypeUsage _booleanType = EdmProviderManifest.Instance.GetCanonicalModelTypeUsage(PrimitiveTypeKind.Boolean); // The Metadata ReadOnlyCollection class conflicts with System.Collections.ObjectModel.ReadOnlyCollection... internal static System.Collections.ObjectModel.ReadOnlyCollectionNewReadOnlyCollection (IList list) { return new System.Collections.ObjectModel.ReadOnlyCollection (list); } private static void RequirePolymorphicType(TypeUsage type, string typeArgumentName) { Debug.Assert(type != null, "Ensure type is non-null before calling RequirePolymorphicType"); if (!TypeSemantics.IsPolymorphicType(type)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicTypeRequired(TypeHelpers.GetFullName(type)), "type"); } } private static void RequireCompatibleType(DbExpression expression, TypeUsage requiredResultType, string argumentName) { RequireCompatibleType(expression, requiredResultType, argumentName, -1); } private static void RequireCompatibleType(DbExpression expression, TypeUsage requiredResultType, string argumentName, int argumentIndex) { Debug.Assert(expression != null, "Ensure expression is non-null before checking for type compatibility"); Debug.Assert(requiredResultType != null, "Ensure type is non-null before checking for type compatibility"); if (!TypeSemantics.IsStructurallyEqualOrPromotableTo(expression.ResultType, requiredResultType)) { // Don't call FormatIndex unless an exception is actually being thrown if (argumentIndex != -1) { argumentName = StringUtil.FormatIndex(argumentName, argumentIndex); } throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_ExpressionLink_TypeMismatch( TypeHelpers.GetFullName(expression.ResultType), TypeHelpers.GetFullName(requiredResultType) ), argumentName ); } } private static void RequireCompatibleType(DbExpression expression, PrimitiveTypeKind requiredResultType, string argumentName) { RequireCompatibleType(expression, requiredResultType, argumentName, -1); } private static void RequireCompatibleType(DbExpression expression, PrimitiveTypeKind requiredResultType, string argumentName, int index) { Debug.Assert(expression != null, "Ensure expression is non-null before checking for type compatibility"); PrimitiveTypeKind valueTypeKind; bool valueIsPrimitive = TypeHelpers.TryGetPrimitiveTypeKind(expression.ResultType, out valueTypeKind); if (!valueIsPrimitive || valueTypeKind != requiredResultType) { if (index != -1) { argumentName = StringUtil.FormatIndex(argumentName, index); } throw EntityUtil.Argument( System.Data.Entity.Strings.Cqt_ExpressionLink_TypeMismatch( (valueIsPrimitive ? Enum.GetName(typeof(PrimitiveTypeKind), valueTypeKind) : TypeHelpers.GetFullName(expression.ResultType)), Enum.GetName(typeof(PrimitiveTypeKind), requiredResultType) ), argumentName ); } } private static void RequireCompatibleType(DbExpression from, RelationshipEndMember end) { Debug.Assert(from != null, "Ensure navigation source expression is non-null before calling RequireCompatibleType"); Debug.Assert(end != null, "Ensure navigation start end is non-null before calling RequireCompatibleType"); TypeUsage endType = end.TypeUsage; if (!TypeSemantics.IsReferenceType(endType)) { // // The only relation end that is currently allowed to have a non-Reference type is the Child end of // a composition, in which case the end type must be an entity type. // // 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"); endType = TypeHelpers.CreateReferenceTypeUsage(TypeHelpers.GetEdmType (endType)); } TypeUsage retType = TypeHelpers.GetCommonTypeUsage(endType, from.ResultType); if (null == retType) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_RelNav_WrongSourceType(TypeHelpers.GetFullName(endType)), "from"); } } private static void RequireCollectionArgument (DbExpression argument) { Debug.Assert(argument != null, "Validate argument is non-null before calling CheckCollectionArgument"); if (!TypeSemantics.IsCollectionType(argument.ResultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Unary_CollectionRequired(typeof(TExpressionType).Name), "argument"); } } private static TypeUsage RequireCollectionArguments (DbExpression left, DbExpression right) { Debug.Assert(left != null && right != null, "Ensure left and right are non-null before calling RequireCollectionArguments"); if (!TypeSemantics.IsCollectionType(left.ResultType) || !TypeSemantics.IsCollectionType(right.ResultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binary_CollectionsRequired(typeof(TExpressionType).Name)); } TypeUsage commonType = TypeHelpers.GetCommonTypeUsage(left.ResultType, right.ResultType); if (null == commonType) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binary_CollectionsRequired(typeof(TExpressionType).Name)); } return commonType; } private static TypeUsage RequireComparableCollectionArguments (DbExpression left, DbExpression right) { TypeUsage resultType = RequireCollectionArguments (left, right); if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(left.ResultType))) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_InvalidTypeForSetOperation(TypeHelpers.GetElementTypeUsage(left.ResultType).Identity, typeof(TExpressionType).Name), "left"); } if (!TypeHelpers.IsSetComparableOpType(TypeHelpers.GetElementTypeUsage(right.ResultType))) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_InvalidTypeForSetOperation(TypeHelpers.GetElementTypeUsage(right.ResultType).Identity, typeof(TExpressionType).Name), "right"); } return resultType; } private static EnumerableValidator CreateValidator (IEnumerable argument, string argumentName, Func convertElement, Func , TResult> createResult) { EnumerableValidator
ret = new EnumerableValidator (argument, argumentName); ret.ConvertElement = convertElement; ret.CreateResult = createResult; return ret; } private static DbExpressionList CreateExpressionList(IEnumerable arguments, string argumentName, Action validationCallback) { return CreateExpressionList(arguments, argumentName, false, validationCallback); } private static DbExpressionList CreateExpressionList(IEnumerable arguments, string argumentName, bool allowEmpty, Action validationCallback) { var ev = CreateValidator(arguments, argumentName, (exp, idx) => { if (validationCallback != null) { validationCallback(exp, idx); } return exp; }, expList => new DbExpressionList(expList) ); ev.AllowEmpty = allowEmpty; return ev.Validate(); } private static DbExpressionList CreateExpressionList(IEnumerable arguments, string argumentName, int expectedElementCount, Action validationCallback) { var ev = CreateValidator(arguments, argumentName, (exp, idx) => { if (validationCallback != null) { validationCallback(exp, idx); } return exp; }, (expList) => new DbExpressionList(expList) ); ev.ExpectedElementCount = expectedElementCount; ev.AllowEmpty = false; return ev.Validate(); } private static TypeUsage ValidateBinary(DbExpression left, DbExpression right) { EntityUtil.CheckArgumentNull(left, "left"); EntityUtil.CheckArgumentNull(right, "right"); return TypeHelpers.GetCommonTypeUsage(left.ResultType, right.ResultType); } private static void ValidateUnary(DbExpression argument) { EntityUtil.CheckArgumentNull(argument, "argument"); } private static void ValidateTypeUnary(DbExpression argument, TypeUsage type, string typeArgumentName) { ValidateUnary(argument); CheckType(type, typeArgumentName); } #region Bindings - Expression and Group internal static TypeUsage ValidateBindAs(DbExpression input, string varName) { // // Ensure no argument is null // EntityUtil.CheckArgumentNull(varName, "varName"); EntityUtil.CheckArgumentNull(input, "input"); // // Ensure Variable name is non-empty // if (string.IsNullOrEmpty(varName)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_VariableNameNotValid, "varName"); } // // Ensure the DbExpression has a collection result type // TypeUsage elementType = null; if (!TypeHelpers.TryGetCollectionElementType(input.ResultType, out elementType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_CollectionRequired, "input"); } Debug.Assert(elementType.IsReadOnly, "DbExpressionBinding Expression ResultType has editable element type"); return elementType; } internal static TypeUsage ValidateGroupBindAs(DbExpression input, string varName, string groupVarName) { // // Ensure no argument is null // EntityUtil.CheckArgumentNull(varName, "varName"); EntityUtil.CheckArgumentNull(groupVarName, "groupVarName"); EntityUtil.CheckArgumentNull(input, "input"); // // Ensure Variable and Group names are both non-empty // if (string.IsNullOrEmpty(varName)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_VariableNameNotValid, "varName"); } if (string.IsNullOrEmpty(groupVarName)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBinding_GroupVariableNameNotValid, "groupVarName"); } // // Ensure the DbExpression has a collection result type // TypeUsage elementType = null; if (!TypeHelpers.TryGetCollectionElementType(input.ResultType, out elementType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBinding_CollectionRequired, "input"); } Debug.Assert((elementType.IsReadOnly), "DbGroupExpressionBinding Expression ResultType has editable element type"); return elementType; } #endregion #region Aggregates and Sort Keys private static FunctionParameter[] GetExpectedParameters(EdmFunction function) { Debug.Assert(function != null, "Ensure function is non-null before calling GetExpectedParameters"); return function.Parameters.Where(p => p.Mode == ParameterMode.In || p.Mode == ParameterMode.InOut).ToArray(); } internal static DbExpressionList ValidateFunctionAggregate(EdmFunction function, IEnumerable args) { // // Verify that the aggregate function is from the metadata collection and data space of the command tree. // ArgumentValidation.CheckFunction(function); // Verify that the function is actually a valid aggregate function. // For now, only a single argument is allowed. if (!TypeSemantics.IsAggregateFunction(function) || null == function.ReturnParameter || TypeSemantics.IsNullOrNullType(function.ReturnParameter.TypeUsage)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Aggregate_InvalidFunction, "function"); } FunctionParameter[] expectedParams = GetExpectedParameters(function); DbExpressionList funcArgs = CreateExpressionList(args, "argument", expectedParams.Length, (exp, idx) => { TypeUsage paramType = expectedParams[idx].TypeUsage; TypeUsage elementType = null; if (TypeHelpers.TryGetCollectionElementType(paramType, out elementType)) { paramType = elementType; } ArgumentValidation.RequireCompatibleType(exp, paramType, "argument"); } ); return funcArgs; } internal static DbExpressionList ValidateGroupAggregate(DbExpression argument) { EntityUtil.CheckArgumentNull(argument, "argument"); return new DbExpressionList(new[] { argument }); } internal static void ValidateSortClause(DbExpression key) { EntityUtil.CheckArgumentNull(key, "key"); if (!TypeHelpers.IsValidSortOpKeyType(key.ResultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Sort_OrderComparable, "key"); } } internal static void ValidateSortClause(DbExpression key, string collation) { ValidateSortClause(key); EntityUtil.CheckArgumentNull(collation, "collation"); if (StringUtil.IsNullOrEmptyOrWhiteSpace(collation)) { throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Sort_EmptyCollationInvalid, "collation"); } if (!TypeSemantics.IsPrimitiveType(key.ResultType, PrimitiveTypeKind.String)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Sort_NonStringCollationInvalid, "collation"); } } #endregion #region DbLambda internal static System.Collections.ObjectModel.ReadOnlyCollection ValidateLambda(IEnumerable variables, DbExpression body) { EntityUtil.CheckArgumentNull(body, "body"); var varVal = CreateValidator(variables, "variables", (varExp, idx) => { if (null == varExp) { throw EntityUtil.ArgumentNull(StringUtil.FormatIndex("variables", idx)); } return varExp; }, (varList) => new System.Collections.ObjectModel.ReadOnlyCollection (varList) ); varVal.AllowEmpty = true; varVal.GetName = (varDef, idx) => varDef.VariableName; var result = varVal.Validate(); return result; } #endregion #region Binding-based methods: All, Any, Cross|OuterApply, Cross|FullOuter|Inner|LeftOuterJoin, Filter, GroupBy, Project, Skip, Sort private static void ValidateBinding(DbExpressionBinding binding, string argumentName) { EntityUtil.CheckArgumentNull(binding, argumentName); } private static void ValidateGroupBinding(DbGroupExpressionBinding binding, string argumentName) { EntityUtil.CheckArgumentNull(binding, argumentName); } private static void ValidateBound(DbExpressionBinding input, DbExpression argument, string argumentName) { ValidateBinding(input, "input"); EntityUtil.CheckArgumentNull(argument, argumentName); } internal static TypeUsage ValidateQuantifier(DbExpressionBinding input, DbExpression predicate) { ValidateBound(input, predicate, "predicate"); RequireCompatibleType(predicate, PrimitiveTypeKind.Boolean, "predicate"); return predicate.ResultType; } internal static TypeUsage ValidateApply(DbExpressionBinding input, DbExpressionBinding apply) { ValidateBinding(input, "input"); ValidateBinding(apply, "apply"); // // Duplicate Input and Apply binding names are not allowed // if (input.VariableName.Equals(apply.VariableName, StringComparison.Ordinal)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Apply_DuplicateVariableNames); } // // Initialize the result type // List > recordCols = new List >(); recordCols.Add(new KeyValuePair (input.VariableName, input.VariableType)); recordCols.Add(new KeyValuePair (apply.VariableName, apply.VariableType)); return CreateCollectionOfRowResultType(recordCols); } internal static System.Collections.ObjectModel.ReadOnlyCollection ValidateCrossJoin(IEnumerable inputs, out TypeUsage resultType) { // // Ensure that the list of input expression bindings is non-null. // EntityUtil.CheckArgumentNull(inputs, "inputs"); // // Validate the input expression bindings and build the column types for the record type // component of the collection of record type result type of the join. // List inputList = new List (); List > columns = new List >(); Dictionary bindingNames = new Dictionary (); IEnumerator inputEnum = inputs.GetEnumerator(); int iPos = 0; while (inputEnum.MoveNext()) { DbExpressionBinding input = inputEnum.Current; // // Validate the DbExpressionBinding before accessing its properties // ValidateBinding(input, StringUtil.FormatIndex("inputs", iPos)); // // Duplicate binding names are not allowed // int nameIndex = -1; if (bindingNames.TryGetValue(input.VariableName, out nameIndex)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_CrossJoin_DuplicateVariableNames(nameIndex, iPos, input.VariableName)); } inputList.Add(input); bindingNames.Add(input.VariableName, iPos); columns.Add(new KeyValuePair (input.VariableName, input.VariableType)); iPos++; } if (inputList.Count < 2) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_CrossJoin_AtLeastTwoInputs, "inputs"); } // // Initialize the result type // resultType = CreateCollectionOfRowResultType(columns); // // Initialize state // return inputList.AsReadOnly(); } internal static TypeUsage ValidateJoin(DbExpressionBinding left, DbExpressionBinding right, DbExpression joinCondition) { // // Validate // ValidateBinding(left, "left"); ValidateBinding(left, "right"); EntityUtil.CheckArgumentNull(joinCondition, "joinCondition"); // // Duplicate Left and Right binding names are not allowed // if (left.VariableName.Equals(right.VariableName, StringComparison.Ordinal)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Join_DuplicateVariableNames); } // // Validate the JoinCondition) // RequireCompatibleType(joinCondition, PrimitiveTypeKind.Boolean, "joinCondition"); // // Initialize the result type // List > columns = new List >(2); columns.Add(new KeyValuePair (left.VariableName, left.VariableType)); columns.Add(new KeyValuePair (right.VariableName, right.VariableType)); return CreateCollectionOfRowResultType(columns); } internal static TypeUsage ValidateFilter(DbExpressionBinding input, DbExpression predicate) { ValidateBound(input, predicate, "predicate"); RequireCompatibleType(predicate, PrimitiveTypeKind.Boolean, "predicate"); return input.Expression.ResultType; } internal static TypeUsage ValidateGroupBy(DbGroupExpressionBinding input, IEnumerable > keys, IEnumerable > aggregates, out DbExpressionList validKeys, out System.Collections.ObjectModel.ReadOnlyCollection validAggregates) { // // Validate the input set // ValidateGroupBinding(input, "input"); // // Track the cumulative set of column names and types, as well as key column names // List > columns = new List >(); HashSet keyNames = new HashSet (); // // Validate the grouping keys // var keyValidator = CreateValidator(keys, "keys", (keyInfo, index) => { ArgumentValidation.CheckNamed(keyInfo, "keys", index); // // The result Type of an expression used as a group key must be equality comparable // if (!TypeHelpers.IsValidGroupKeyType(keyInfo.Value.ResultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_KeyNotEqualityComparable(keyInfo.Key)); } keyNames.Add(keyInfo.Key); columns.Add(new KeyValuePair (keyInfo.Key, keyInfo.Value.ResultType)); return keyInfo.Value; }, expList => new DbExpressionList(expList) ); keyValidator.AllowEmpty = true; keyValidator.GetName = (keyInfo, idx) => keyInfo.Key; validKeys = keyValidator.Validate(); bool hasGroupAggregate = false; var aggValidator = CreateValidator(aggregates, "aggregates", (aggInfo, idx) => { ArgumentValidation.CheckNamed(aggInfo, "aggregates", idx); // // Is there a grouping key with the same name? // if (keyNames.Contains(aggInfo.Key)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_AggregateColumnExistsAsGroupColumn(aggInfo.Key)); } // // At most one group aggregate can be specified // if (aggInfo.Value is DbGroupAggregate) { if (hasGroupAggregate) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_MoreThanOneGroupAggregate); } else { hasGroupAggregate = true; } } columns.Add(new KeyValuePair (aggInfo.Key, aggInfo.Value.ResultType)); return aggInfo.Value; }, aggList => NewReadOnlyCollection(aggList) ); aggValidator.AllowEmpty = true; aggValidator.GetName = (aggInfo, idx) => aggInfo.Key; validAggregates = aggValidator.Validate(); // // Either the Keys or Aggregates may be omitted, but not both // if (0 == validKeys.Count && 0 == validAggregates.Count) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GroupBy_AtLeastOneKeyOrAggregate); } // // Create the result type. This is a collection of the record type produced by the group keys and aggregates. // return CreateCollectionOfRowResultType(columns); } internal static TypeUsage ValidateProject(DbExpressionBinding input, DbExpression projection) { ValidateBound(input, projection, "projection"); return CreateCollectionResultType(projection.ResultType); } /// /// Validates the input and sort key arguments to both DbSkipExpression and DbSortExpression. /// /// A DbExpressionBinding that provides the collection to be ordered /// A list of SortClauses that specifies the sort order to apply to the input collection private static System.Collections.ObjectModel.ReadOnlyCollectionValidateSortArguments(DbExpressionBinding input, IEnumerable sortOrder) { ValidateBinding(input, "input"); var ev = CreateValidator(sortOrder, "sortOrder", (key, idx) => key, keyList => NewReadOnlyCollection(keyList) ); ev.AllowEmpty = false; return ev.Validate(); } internal static System.Collections.ObjectModel.ReadOnlyCollection ValidateSkip(DbExpressionBinding input, IEnumerable sortOrder, DbExpression count) { // // Validate the input expression binding and sort keys // var sortKeys = ValidateSortArguments(input, sortOrder); // // Initialize the Count ExpressionLink. In addition to being non-null and from the same command tree, // the Count expression must also have an integer result type. // EntityUtil.CheckArgumentNull(count, "count"); if (!TypeSemantics.IsIntegerNumericType(count.ResultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Skip_IntegerRequired, "count"); } // // Currently the Count expression is also required to be either a DbConstantExpression or a DbParameterReferenceExpression. // if (count.ExpressionKind != DbExpressionKind.Constant && count.ExpressionKind != DbExpressionKind.ParameterReference) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Skip_ConstantOrParameterRefRequired, "count"); } // // For constants, verify the count is non-negative. // if (ArgumentValidation.IsConstantNegativeInteger(count)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Skip_NonNegativeCountRequired, "count"); } return sortKeys; } internal static System.Collections.ObjectModel.ReadOnlyCollection ValidateSort(DbExpressionBinding input, IEnumerable sortOrder) { // // Validate the input expression binding and sort keys // return ValidateSortArguments(input, sortOrder); } #endregion #region Leaf Expressions - Null, Constant, Parameter, Scan internal static void ValidateNull(TypeUsage nullType) { CheckType(nullType, "nullType"); } internal static TypeUsage ValidateConstant(object value) { EntityUtil.CheckArgumentNull(value, "value"); // // Check that typeof(value) is actually a valid constant (i.e. primitive) type // PrimitiveTypeKind primitiveTypeKind; if (!ArgumentValidation.TryGetPrimitiveTypeKind(value.GetType(), out primitiveTypeKind)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Constant_InvalidType, "value"); } return TypeHelpers.GetLiteralTypeUsage(primitiveTypeKind); } internal static void ValidateConstant(TypeUsage constantType, object value) { // // Basic validation of constant value and constant type (non-null, read-only, etc) // EntityUtil.CheckArgumentNull(value, "value"); ArgumentValidation.CheckType(constantType, "constantType"); // // Verify that constantType is a primitive type and that value is an instance of that primitive type // Note that the value is not validated against applicable facets (such as MaxLength for a string value), // this is left to the server. // PrimitiveType primitiveType; if (!TypeHelpers.TryGetEdmType (constantType, out primitiveType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Constant_InvalidConstantType(constantType.ToString()), "constantType"); } PrimitiveTypeKind valueKind; if (!ArgumentValidation.TryGetPrimitiveTypeKind(value.GetType(), out valueKind) || primitiveType.PrimitiveTypeKind != valueKind) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Constant_InvalidValueForType(constantType.ToString()), "value"); } } internal static void ValidateParameter(TypeUsage type, string name) { ArgumentValidation.CheckType(type); EntityUtil.CheckArgumentNull(name, "name"); if (!DbCommandTree.IsValidParameterName(name)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_CommandTree_InvalidParameterName(name), "name"); } } internal static TypeUsage ValidateScan(EntitySetBase entitySet) { ArgumentValidation.CheckEntitySet(entitySet, "targetSet"); return ArgumentValidation.CreateCollectionResultType(entitySet.ElementType); } internal static void ValidateVariable(TypeUsage type, string name) { CheckType(type); EntityUtil.CheckArgumentNull(name, "name"); if (string.IsNullOrEmpty(name)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Binding_VariableNameNotValid, "name"); } } #endregion #region Boolean Operators - And, Or, Not internal static TypeUsage ValidateAnd(DbExpression left, DbExpression right) { TypeUsage resultType = ValidateBinary(left, right); if (null == resultType || !TypeSemantics.IsPrimitiveType(resultType, PrimitiveTypeKind.Boolean)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_And_BooleanArgumentsRequired); } return resultType; } internal static TypeUsage ValidateOr(DbExpression left, DbExpression right) { TypeUsage resultType = ValidateBinary(left, right); if (null == resultType || !TypeSemantics.IsPrimitiveType(resultType, PrimitiveTypeKind.Boolean)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Or_BooleanArgumentsRequired); } return resultType; } internal static TypeUsage ValidateNot(DbExpression argument) { EntityUtil.CheckArgumentNull(argument, "argument"); // // Argument to Not must have Boolean result type // if (!TypeSemantics.IsPrimitiveType(argument.ResultType, PrimitiveTypeKind.Boolean)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Not_BooleanArgumentRequired); } return argument.ResultType; } #endregion #region Arithmetic Operators internal static DbExpressionList ValidateArithmetic(DbExpression argument, out TypeUsage resultType) { ValidateUnary(argument); resultType = argument.ResultType; if (!TypeSemantics.IsNumericType(resultType)) { // throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Arithmetic_NumericCommonType); } //If argument to UnaryMinus is an unsigned type, promote return type to next higher, signed type. if (TypeSemantics.IsUnsignedNumericType(argument.ResultType)) { TypeUsage closestPromotableType = null; if (TypeHelpers.TryGetClosestPromotableType(argument.ResultType, out closestPromotableType)) { resultType = closestPromotableType; } else { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Arithmetic_InvalidUnsignedTypeForUnaryMinus(argument.ResultType.EdmType.FullName)); } } return new DbExpressionList(new[] { argument }); } internal static DbExpressionList ValidateArithmetic(DbExpression left, DbExpression right, out TypeUsage resultType) { resultType = ValidateBinary(left, right); if (TypeSemantics.IsNullOrNullType(resultType) || !TypeSemantics.IsNumericType(resultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Arithmetic_NumericCommonType); } return new DbExpressionList(new[] { left, right }); } #endregion #region Comparison internal static TypeUsage ValidateComparison(DbExpressionKind kind, DbExpression left, DbExpression right) { EntityUtil.CheckArgumentNull(left, "left"); EntityUtil.CheckArgumentNull(right, "right"); // // A comparison of the specified kind must exist between the left and right arguments // bool equality = true; bool order = true; if (DbExpressionKind.GreaterThanOrEquals == kind || DbExpressionKind.LessThanOrEquals == kind) { equality = TypeSemantics.IsEqualComparableTo(left.ResultType, right.ResultType); order = TypeSemantics.IsOrderComparableTo(left.ResultType, right.ResultType); } else if (DbExpressionKind.Equals == kind || DbExpressionKind.NotEquals == kind) { equality = TypeSemantics.IsEqualComparableTo(left.ResultType, right.ResultType); } else { order = TypeSemantics.IsOrderComparableTo(left.ResultType, right.ResultType); } if (!equality || !order) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Comparison_ComparableRequired); } return _booleanType; } internal static TypeUsage ValidateIsNull(DbExpression argument) { return ValidateIsNull(argument, false); } internal static TypeUsage ValidateIsNull(DbExpression argument, bool allowRowType) { EntityUtil.CheckArgumentNull(argument, "argument"); // // The argument cannot be of a collection type // if (TypeSemantics.IsCollectionType(argument.ResultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_IsNull_CollectionNotAllowed); } // // ensure argument type is valid for this operation // if (!TypeHelpers.IsValidIsNullOpType(argument.ResultType)) { // if (!allowRowType || !TypeSemantics.IsRowType(argument.ResultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_IsNull_InvalidType); } } return _booleanType; } internal static TypeUsage ValidateLike(DbExpression argument, DbExpression pattern) { EntityUtil.CheckArgumentNull(argument, "argument"); EntityUtil.CheckArgumentNull(pattern, "pattern"); RequireCompatibleType(argument, PrimitiveTypeKind.String, "argument"); RequireCompatibleType(pattern, PrimitiveTypeKind.String, "pattern"); return _booleanType; } internal static TypeUsage ValidateLike(DbExpression argument, DbExpression pattern, DbExpression escape) { TypeUsage resultType = ValidateLike(argument, pattern); EntityUtil.CheckArgumentNull(escape, "escape"); RequireCompatibleType(escape, PrimitiveTypeKind.String, "escape"); return resultType; } #endregion #region Type Operators - Cast, Treat, OfType, OfTypeOnly, IsOf, IsOfOnly internal static void ValidateCastTo(DbExpression argument, TypeUsage toType) { ValidateTypeUnary(argument, toType, "toType"); // // Verify that the cast is allowed // if (!TypeSemantics.IsCastAllowed(argument.ResultType, toType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Cast_InvalidCast(TypeHelpers.GetFullName(argument.ResultType), TypeHelpers.GetFullName(toType))); } } internal static void ValidateTreatAs(DbExpression argument, TypeUsage asType) { ValidateTypeUnary(argument, asType, "asType"); // // Verify the type to treat as. Treat-As (NullType) is not allowed. // RequirePolymorphicType(asType, "asType"); // // Verify that the Treat operation is allowed // if (!TypeSemantics.IsValidPolymorphicCast(argument.ResultType, asType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicArgRequired(typeof(DbTreatExpression).Name)); } } internal static TypeUsage ValidateOfType(DbExpression argument, TypeUsage type) { ValidateTypeUnary(argument, type, "type"); // // Ensure that the type is non-null and valid - from the same metadata collection and dataspace and the command tree. // The type is also not allowed to be NullType. // RequirePolymorphicType(type, "type"); // // Ensure that the argument is actually of a collection type. // RequireCollectionArgument (argument); // // Verify that the OfType operation is allowed // TypeUsage elementType = null; if (!TypeHelpers.TryGetCollectionElementType(argument.ResultType, out elementType) || !TypeSemantics.IsValidPolymorphicCast(elementType, type)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicArgRequired(typeof(DbOfTypeExpression).Name)); } // // The type of this DbExpression is a new collection type based on the requested element type. // return CreateCollectionResultType(type); } internal static TypeUsage ValidateIsOf(DbExpression argument, TypeUsage type) { ValidateTypeUnary(argument, type, "type"); // // Ensure the ofType is non-null, associated with the correct metadata workspace/dataspace, // is not NullType, and is polymorphic // RequirePolymorphicType(type, "type"); // // Verify that the IsOf operation is allowed // if (!TypeSemantics.IsValidPolymorphicCast(argument.ResultType, type)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_PolymorphicArgRequired(typeof(DbIsOfExpression).Name)); } return _booleanType; } #endregion #region Ref Operators - Deref, EntityRef, Ref, RefKey, RelationshipNavigation internal static TypeUsage ValidateDeref(DbExpression argument) { ValidateUnary(argument); // // Ensure that the operand is actually of a reference type. // EntityType entityType; if (!TypeHelpers.TryGetRefEntityType(argument.ResultType, out entityType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_DeRef_RefRequired, "argument"); } // // Result Type is the element type of the reference type // return CreateResultType(entityType); } internal static TypeUsage ValidateGetEntityRef(DbExpression argument) { ValidateUnary(argument); EntityType entityType = null; if (!TypeHelpers.TryGetEdmType (argument.ResultType, out entityType) || null == entityType) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GetEntityRef_EntityRequired, "argument"); } return CreateReferenceResultType(entityType); } internal static TypeUsage ValidateCreateRef(EntitySet entitySet, IEnumerable keyValues, out DbExpression keyConstructor) { EntityUtil.CheckArgumentNull(entitySet, "entitySet"); return ValidateCreateRef(entitySet, entitySet.ElementType, keyValues, out keyConstructor); } internal static TypeUsage ValidateCreateRef(EntitySet entitySet, EntityType entityType, IEnumerable keyValues, out DbExpression keyConstructor) { CheckEntitySet(entitySet, "entitySet"); CheckType(entityType, "entityType"); // // Verify that the specified return type of the Ref operation is actually in // the same hierarchy as the Entity type of the specified Entity set. // if (!TypeSemantics.IsValidPolymorphicCast(entitySet.ElementType, entityType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Ref_PolymorphicArgRequired); } // Validate the key values. The count of values must match the count of key members, // and each key value must have a result type that is compatible with the type of // the corresponding key member. IList keyMembers = entityType.KeyMembers; var keyValueValidator = CreateValidator(keyValues, "keyValues", (valueExp, idx) => { RequireCompatibleType(valueExp, keyMembers[idx].TypeUsage, "keyValues", idx); return new KeyValuePair (keyMembers[idx].Name, valueExp); }, (columnList) => columnList ); keyValueValidator.ExpectedElementCount = keyMembers.Count; var keyColumns = keyValueValidator.Validate(); keyConstructor = DbExpressionBuilder.NewRow(keyColumns); return CreateReferenceResultType(entityType); } internal static TypeUsage ValidateRefFromKey(EntitySet entitySet, DbExpression keyValues) { EntityUtil.CheckArgumentNull(entitySet, "entitySet"); return ValidateRefFromKey(entitySet, keyValues, entitySet.ElementType); } internal static TypeUsage ValidateRefFromKey(EntitySet entitySet, DbExpression keyValues, EntityType entityType) { CheckEntitySet(entitySet, "entitySet"); EntityUtil.CheckArgumentNull(keyValues, "keyValues"); CheckType(entityType); // // Verify that the specified return type of the Ref operation is actually in // the same hierarchy as the Entity type of the specified Entity set. // if (!TypeSemantics.IsValidPolymorphicCast(entitySet.ElementType, entityType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Ref_PolymorphicArgRequired); } // // The Argument DbExpression must construct a set of values of the same types as the Key members of the Entity // The names of the columns in the record type constructed by the Argument are not important, only that the // number of columns is the same as the number of Key members and that for each Key member the corresponding // column (based on order) is of a promotable type. // To enforce this, the argument's result type is compared to a record type based on the names and types of // the Key members. Since the promotability check used in RequireCompatibleType will ignore the names of the // expected type's columns, RequireCompatibleType will therefore enforce the required level of type correctness // // Set the expected type to be the record type created based on the Key members // TypeUsage keyType = CreateResultType(TypeHelpers.CreateKeyRowType(entitySet.ElementType)); RequireCompatibleType(keyValues, keyType, "keyValues"); return CreateReferenceResultType(entityType); } internal static TypeUsage ValidateGetRefKey(DbExpression argument) { ValidateUnary(argument); RefType refType = null; if (!TypeHelpers.TryGetEdmType (argument.ResultType, out refType) || null == refType) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_GetRefKey_RefRequired, "argument"); } // RefType is responsible for basic validation of ElementType Debug.Assert(refType.ElementType != null, "RefType constructor allowed null ElementType?"); return CreateResultType(TypeHelpers.CreateKeyRowType(refType.ElementType)); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal static TypeUsage ValidateNavigate(DbExpression navigateFrom, RelationshipType type, string fromEndName, string toEndName, out RelationshipEndMember fromEnd, out RelationshipEndMember toEnd) { EntityUtil.CheckArgumentNull(navigateFrom, "navigateFrom"); // // Ensure that the relation type is non-null and from the same metadata workspace as the command tree // CheckType(type); // // Verify that the from and to relation end names are not null // EntityUtil.CheckArgumentNull(fromEndName, "fromEndName"); EntityUtil.CheckArgumentNull(toEndName, "toEndName"); // // Retrieve the relation end properties with the specified 'from' and 'to' names // if (!type.RelationshipEndMembers.TryGetValue(fromEndName, false /*ignoreCase*/, out fromEnd)) { throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Factory_NoSuchRelationEnd, fromEndName); } if (!type.RelationshipEndMembers.TryGetValue(toEndName, false /*ignoreCase*/, out toEnd)) { throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Factory_NoSuchRelationEnd, toEndName); } // // Validate the retrieved relation end against the navigation source // RequireCompatibleType(navigateFrom, fromEnd); return CreateResultType(toEnd); } internal static TypeUsage ValidateNavigate(DbExpression navigateFrom, RelationshipEndMember fromEnd, RelationshipEndMember toEnd, out RelationshipType relType) { EntityUtil.CheckArgumentNull(navigateFrom, "navigateFrom"); // // Validate the relationship ends before use // CheckMember(fromEnd, "fromEnd"); CheckMember(toEnd, "toEnd"); relType = fromEnd.DeclaringType as RelationshipType; // // Ensure that the relation type is non-null and read-only // CheckType(relType); // // Validate that the 'to' relationship end is defined by the same relationship type as the 'from' end // if (!relType.Equals(toEnd.DeclaringType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Factory_IncompatibleRelationEnds, "toEnd"); } RequireCompatibleType(navigateFrom, fromEnd); return CreateResultType(toEnd); } #endregion #region Unary and Binary Set Operators - Distinct, Element, IsEmpty, Except, Intersect, UnionAll, Limit internal static TypeUsage ValidateDistinct(DbExpression argument) { ValidateUnary(argument); // // Ensure that the Argument is of a collection type // RequireCollectionArgument (argument); // // Ensure that the Distinct operation is valid for the input // CollectionType inputType = TypeHelpers.GetEdmType (argument.ResultType); if (!TypeHelpers.IsValidDistinctOpType(inputType.TypeUsage)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Distinct_InvalidCollection, "argument"); } return argument.ResultType; } internal static TypeUsage ValidateElement(DbExpression argument) { ValidateUnary(argument); // // Ensure that the operand is actually of a collection type. // RequireCollectionArgument (argument); // // Result Type is the element type of the collection type // return TypeHelpers.GetEdmType (argument.ResultType).TypeUsage; } internal static TypeUsage ValidateIsEmpty(DbExpression argument) { ValidateUnary(argument); // // Ensure that the Argument is of a collection type // RequireCollectionArgument (argument); return _booleanType; } internal static TypeUsage ValidateExcept(DbExpression left, DbExpression right) { ValidateBinary(left, right); // // Ensures the left and right operands are each of a comparable collection type // RequireComparableCollectionArguments (left, right); // // Left Except Right produces a result with the same type as the Left operand // A Left operand with Type NullType is not allowed. // if (TypeSemantics.IsNullType(left.ResultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Except_LeftNullTypeInvalid, "left"); } return left.ResultType; } internal static TypeUsage ValidateIntersect(DbExpression left, DbExpression right) { ValidateBinary(left, right); // // Ensures the left and right operands are each of a comparable collection type // return RequireComparableCollectionArguments (left, right); } internal static TypeUsage ValidateUnionAll(DbExpression left, DbExpression right) { ValidateBinary(left, right); // // Ensure that the left and right operands are each of a collection type and that a common type exists for those types. // return RequireCollectionArguments (left, right); } internal static TypeUsage ValidateLimit(DbExpression argument, DbExpression limit) { // // Initialize the Argument ExpressionLink. In addition to being non-null and from the same command tree, // the Argument expression must have a collection result type. // EntityUtil.CheckArgumentNull(argument, "argument"); RequireCollectionArgument (argument); // // Initialize the Limit ExpressionLink. In addition to being non-null and from the same command tree, // the Limit expression must also have an integer result type. // EntityUtil.CheckArgumentNull(limit, "count"); if (!TypeSemantics.IsIntegerNumericType(limit.ResultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Limit_IntegerRequired, "limit"); } // // Currently the Limit expression is also required to be either a DbConstantExpression or a DbParameterReferenceExpression. // if (limit.ExpressionKind != DbExpressionKind.Constant && limit.ExpressionKind != DbExpressionKind.ParameterReference) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Limit_ConstantOrParameterRefRequired, "limit"); } // // For constants, verify the limit is non-negative. // if (ArgumentValidation.IsConstantNegativeInteger(limit)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Limit_NonNegativeLimitRequired, "limit"); } return argument.ResultType; } #endregion #region General Operators - Case, Function, NewInstance, Property internal static TypeUsage ValidateCase(IEnumerable whenExpressions, IEnumerable thenExpressions, DbExpression elseExpression, out DbExpressionList validWhens, out DbExpressionList validThens) { EntityUtil.CheckArgumentNull(whenExpressions, "whenExpressions"); EntityUtil.CheckArgumentNull(thenExpressions, "thenExpressions"); EntityUtil.CheckArgumentNull(elseExpression, "elseExpression"); // // All 'When's must produce a Boolean result, and a common (non-null) result type must exist // for all 'Thens' and 'Else'. At least one When/Then clause is required and the number of // 'When's must equal the number of 'Then's. // validWhens = CreateExpressionList(whenExpressions, "whenExpressions", (exp, idx) => { RequireCompatibleType(exp, PrimitiveTypeKind.Boolean, "whenExpressions", idx); } ); Debug.Assert(validWhens.Count > 0, "CreateExpressionList(arguments, argumentName, validationCallback) allowed empty Whens?"); TypeUsage commonResultType = null; validThens = CreateExpressionList(thenExpressions, "thenExpressions", (exp, idx) => { if (null == commonResultType) { commonResultType = exp.ResultType; } else { commonResultType = TypeHelpers.GetCommonTypeUsage(exp.ResultType, commonResultType); if (TypeSemantics.IsNullOrNullType(commonResultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Case_InvalidResultType); } } } ); Debug.Assert(validWhens.Count > 0, "CreateExpressionList(arguments, argumentName, validationCallback) allowed empty Thens?"); commonResultType = TypeHelpers.GetCommonTypeUsage(elseExpression.ResultType, commonResultType); if (TypeSemantics.IsNullOrNullType(commonResultType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Case_InvalidResultType); } // // The number of 'When's must equal the number of 'Then's. // if (validWhens.Count != validThens.Count) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Case_WhensMustEqualThens); } // // The result type of DbCaseExpression is the common result type // return commonResultType; } internal static TypeUsage ValidateFunction(EdmFunction function, IEnumerable arguments, out DbExpressionList validArgs) { // // Ensure that the function metadata is non-null and from the same metadata workspace and dataspace as the command tree. CheckFunction(function); // // Non-composable functions or non-UDF functions including command text are not permitted in expressions -- they can only be // executed independently // if (!function.IsComposableAttribute) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Function_NonComposableInExpression, "function"); } if (!String.IsNullOrEmpty(function.CommandTextAttribute) && !function.HasUserDefinedBody) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Function_CommandTextInExpression, "function"); } // // Functions that return void are not allowed // if (null == function.ReturnParameter || TypeSemantics.IsNullOrNullType(function.ReturnParameter.TypeUsage)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Function_VoidResultInvalid, "function"); } // // Validate the arguments // FunctionParameter[] expectedParams = GetExpectedParameters(function); validArgs = CreateExpressionList(arguments, "arguments", expectedParams.Length, (exp, idx) => { ArgumentValidation.RequireCompatibleType(exp, expectedParams[idx].TypeUsage, "arguments", idx); } ); return function.ReturnParameter.TypeUsage; } internal static TypeUsage ValidateInvoke(DbLambda lambda, IEnumerable arguments, out DbExpressionList validArguments) { EntityUtil.CheckArgumentNull(lambda, "lambda"); EntityUtil.CheckArgumentNull(arguments, "arguments"); // Each argument must be type-compatible with the corresponding lambda variable for which it supplies the value validArguments = null; var argValidator = CreateValidator(arguments, "arguments", (exp, idx) => { RequireCompatibleType(exp, lambda.Variables[idx].ResultType, "arguments", idx); return exp; }, expList => new DbExpressionList(expList) ); argValidator.ExpectedElementCount = lambda.Variables.Count; validArguments = argValidator.Validate(); // The result type of the lambda expression is the result type of the lambda body return lambda.Body.ResultType; } internal static TypeUsage ValidateNewCollection(IEnumerable elements, out DbExpressionList validElements) { TypeUsage commonElementType = null; validElements = CreateExpressionList(elements, "elements", (exp, idx) => { if (commonElementType == null) { commonElementType = exp.ResultType; } else { commonElementType = TypeSemantics.GetCommonType(commonElementType, exp.ResultType); } if (TypeSemantics.IsNullOrNullType(commonElementType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Factory_NewCollectionInvalidCommonType, "collectionElements"); } } ); Debug.Assert(validElements.Count > 0, "CreateExpressionList(arguments, argumentName, validationCallback) allowed empty elements list?"); return CreateCollectionResultType(commonElementType); } internal static TypeUsage ValidateNewEmptyCollection(TypeUsage collectionType, out DbExpressionList validElements) { CheckType(collectionType, "collectionType"); if (!TypeSemantics.IsCollectionType(collectionType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_CollectionTypeRequired, "collectionType"); } // validElements = new DbExpressionList(new DbExpression[] { }); return collectionType; } internal static TypeUsage ValidateNewRow(IEnumerable > columnValues, out DbExpressionList validElements) { List > columnTypes = new List >(); var columnValidator = CreateValidator(columnValues, "columnValues", (columnValue, idx) => { CheckNamed(columnValue, "columnValues", idx); columnTypes.Add(new KeyValuePair (columnValue.Key, columnValue.Value.ResultType)); return columnValue.Value; }, expList => new DbExpressionList(expList) ); columnValidator.GetName = ((columnValue, idx) => columnValue.Key); validElements = columnValidator.Validate(); return CreateResultType(TypeHelpers.CreateRowType(columnTypes)); } internal static TypeUsage ValidateNew(TypeUsage instanceType, IEnumerable arguments, out DbExpressionList validArguments) { // // Ensure that the type is non-null, valid and not NullType // CheckType(instanceType, "instanceType"); CollectionType collectionType = null; if (TypeHelpers.TryGetEdmType (instanceType, out collectionType) && collectionType != null) { // Collection arguments may have zero count for empty collection construction TypeUsage elementType = collectionType.TypeUsage; validArguments = CreateExpressionList(arguments, "arguments", true, (exp, idx) => { RequireCompatibleType(exp, elementType, "arguments", idx); }); } else { List expectedTypes = GetStructuralMemberTypes(instanceType); int pos = 0; validArguments = CreateExpressionList(arguments, "arguments", expectedTypes.Count, (exp, idx) => { RequireCompatibleType(exp, expectedTypes[pos++], "arguments", idx); }); } return instanceType; } private static List GetStructuralMemberTypes(TypeUsage instanceType) { StructuralType structType = instanceType.EdmType as StructuralType; if (null == structType) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_StructuralTypeRequired, "instanceType"); } if (structType.Abstract) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_CannotInstantiateAbstractType(TypeHelpers.GetFullName(instanceType)), "instanceType"); } var members = TypeHelpers.GetAllStructuralMembers(structType); if (members == null || members.Count < 1) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_CannotInstantiateMemberlessType(TypeHelpers.GetFullName(instanceType)), "instanceType"); } List memberTypes = new List (members.Count); for (int idx = 0; idx < members.Count; idx++) { memberTypes.Add(Helper.GetModelTypeUsage(members[idx])); } return memberTypes; } internal static TypeUsage ValidateNewEntityWithRelationships(EntityType entityType, IEnumerable attributeValues, IList relationships, out DbExpressionList validArguments, out System.Collections.ObjectModel.ReadOnlyCollection validRelatedRefs) { EntityUtil.CheckArgumentNull(entityType, "entityType"); EntityUtil.CheckArgumentNull(attributeValues, "attributeValues"); EntityUtil.CheckArgumentNull(relationships, "relationships"); TypeUsage resultType = CreateResultType(entityType); resultType = ArgumentValidation.ValidateNew(resultType, attributeValues, out validArguments); if (relationships.Count > 0) { List relatedRefs = new List (relationships.Count); for (int idx = 0; idx < relationships.Count; idx++) { DbRelatedEntityRef relatedRef = relationships[idx]; EntityUtil.CheckArgumentNull(relatedRef, StringUtil.FormatIndex("relationships", idx)); // The source end type must be the same type or a supertype of the Entity instance type EntityTypeBase expectedSourceType = TypeHelpers.GetEdmType (relatedRef.SourceEnd.TypeUsage).ElementType; // if (!entityType.EdmEquals(expectedSourceType) && !entityType.IsSubtypeOf(expectedSourceType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_NewInstance_IncompatibleRelatedEntity_SourceTypeNotValid, StringUtil.FormatIndex("relationships", idx)); } relatedRefs.Add(relatedRef); } validRelatedRefs = relatedRefs.AsReadOnly(); } else { validRelatedRefs = new System.Collections.ObjectModel.ReadOnlyCollection (new DbRelatedEntityRef[] { }); } return resultType; } internal static TypeUsage ValidateProperty(DbExpression instance, EdmMember property, string propertyArgumentName) { // // Validate the member // CheckMember(property, propertyArgumentName); // // Validate the instance // if (null == instance) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Property_InstanceRequiredForInstance, "instance"); } TypeUsage expectedInstanceType = TypeUsage.Create(property.DeclaringType); RequireCompatibleType(instance, expectedInstanceType, "instance"); Debug.Assert(!TypeSemantics.IsNullOrNullType(Helper.GetModelTypeUsage(property)), "EdmMember metadata has a TypeUsage of null or NullType"); return Helper.GetModelTypeUsage(property); } internal static TypeUsage ValidateProperty(DbExpression instance, string propertyName, bool ignoreCase, out EdmMember foundMember) { EntityUtil.CheckArgumentNull(instance, "instance"); EntityUtil.CheckArgumentNull(propertyName, "propertyName"); // // EdmProperty, NavigationProperty and RelationshipEndMember are the only valid members for DbPropertyExpression. // Since these all derive from EdmMember they are declared by subtypes of StructuralType, // so a non-StructuralType instance is invalid. // StructuralType structType; if (TypeHelpers.TryGetEdmType (instance.ResultType, out structType)) { // // Does the type declare a member with the given name? // if (structType.Members.TryGetValue(propertyName, ignoreCase, out foundMember) && foundMember != null) { // // If the member is a RelationshipEndMember, call the corresponding overload. // if (Helper.IsRelationshipEndMember(foundMember) || Helper.IsEdmProperty(foundMember) || Helper.IsNavigationProperty(foundMember)) { return Helper.GetModelTypeUsage(foundMember); } } } throw EntityUtil.ArgumentOutOfRange(System.Data.Entity.Strings.Cqt_Factory_NoSuchProperty(propertyName, TypeHelpers.GetFullName(instance.ResultType)), "propertyName"); } #endregion private static void CheckNamed (KeyValuePair element, string argumentName, int index) { if (string.IsNullOrEmpty(element.Key)) { if (index != -1) { argumentName = StringUtil.FormatIndex(argumentName, index); } throw EntityUtil.ArgumentNull(string.Format(CultureInfo.InvariantCulture, "{0}.Key", argumentName)); } if (null == element.Value) { if (index != -1) { argumentName = StringUtil.FormatIndex(argumentName, index); } throw EntityUtil.ArgumentNull(string.Format(CultureInfo.InvariantCulture, "{0}.Value", argumentName)); } } private static void CheckReadOnly(GlobalItem item, string varName) { EntityUtil.CheckArgumentNull(item, varName); if (!(item.IsReadOnly)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_MetadataNotReadOnly, varName); } } private static void CheckReadOnly(TypeUsage item, string varName) { EntityUtil.CheckArgumentNull(item, varName); if (!(item.IsReadOnly)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_MetadataNotReadOnly, varName); } } private static void CheckReadOnly(EntitySetBase item, string varName) { EntityUtil.CheckArgumentNull(item, varName); if (!(item.IsReadOnly)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_MetadataNotReadOnly, varName); } } private static void CheckType(EdmType type) { CheckType(type, "type"); } private static void CheckType(EdmType type, string argumentName) { EntityUtil.CheckArgumentNull(type, argumentName); CheckReadOnly(type, argumentName); } /// /// Ensures that the specified type is non-null, associated with the correct metadata workspace/dataspace, and is not NullType. /// /// The type usage instance to verify. ///If the specified type metadata is null ///If the specified type metadata belongs to a metadata workspace other than the workspace of the command tree ///If the specified type metadata belongs to a dataspace other than the dataspace of the command tree private static void CheckType(TypeUsage type) { CheckType(type, "type"); } private static void CheckType(TypeUsage type, string varName) { EntityUtil.CheckArgumentNull(type, varName); CheckReadOnly(type, varName); if (TypeSemantics.IsNullType(type)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_General_NullTypeInvalid, varName); } // TypeUsage constructor is responsible for basic validation of EdmType Debug.Assert(type.EdmType != null, "TypeUsage constructor allowed null EdmType?"); if (!CheckDataSpace(type)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_TypeUsageIncorrectSpace, "type"); } } ////// Verifies that the specified member is valid - non-null, from the same metadata workspace and data space as the command tree, etc /// /// The member to verify /// The name of the variable to which this member instance is being assigned private static void CheckMember(EdmMember memberMeta, string varName) { EntityUtil.CheckArgumentNull(memberMeta, varName); CheckReadOnly(memberMeta.DeclaringType, varName); // EdmMember constructor is responsible for basic validation Debug.Assert(memberMeta.Name != null, "EdmMember constructor allowed null name?"); Debug.Assert(!TypeSemantics.IsNullOrNullType(memberMeta.TypeUsage), "EdmMember constructor allowed null/NullType for TypeUsage?"); Debug.Assert(!TypeSemantics.IsNullOrNullType(memberMeta.DeclaringType), "EdmMember constructor allowed null/NullType for DeclaringType?"); if(!CheckDataSpace(memberMeta.TypeUsage) || !CheckDataSpace(memberMeta.DeclaringType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EdmMemberIncorrectSpace, varName); } } private static void CheckParameter(FunctionParameter paramMeta, string varName) { EntityUtil.CheckArgumentNull(paramMeta, varName); CheckReadOnly(paramMeta.DeclaringFunction, varName); // FunctionParameter constructor is responsible for basic validation Debug.Assert(paramMeta.Name != null, "FunctionParameter constructor allowed null name?"); Debug.Assert(!TypeSemantics.IsNullOrNullType(paramMeta.TypeUsage), "FunctionParameter constructor allowed null/NullType for TypeUsage?"); // Verify that the parameter is from the same workspace as the DbCommandTree if (!CheckDataSpace(paramMeta.TypeUsage)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionParameterIncorrectSpace, varName); } } ////// 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) /// /// The function metadata to verify private static void CheckFunction(EdmFunction function) { EntityUtil.CheckArgumentNull(function, "function"); CheckReadOnly(function, "function"); Debug.Assert(function.Name != null, "EdmType constructor allowed null name?"); if (!CheckDataSpace(function)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionIncorrectSpace, "function"); } // Composable functions must have a return parameter. if (function.IsComposableAttribute && null == function.ReturnParameter) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionReturnParameterNull, "function"); } // Verify that the function ReturnType - if present - is from the DbCommandTree's metadata collection and dataspace // A return parameter is not required for non-composable functions. if (function.ReturnParameter != null) { Debug.Assert(!TypeSemantics.IsNullOrNullType(function.ReturnParameter.TypeUsage), "FunctionParameter constructor allowed null/NullType for TypeUsage?"); if (!CheckDataSpace(function.ReturnParameter.TypeUsage)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_FunctionParameterIncorrectSpace, "function.ReturnParameter"); } } // Verify that the function parameters collection is non-null and, // if non-empty, contains valid IParameterMetadata instances. IListfunctionParams = function.Parameters; Debug.Assert(functionParams != null, "EdmFunction constructor did not initialize Parameters?"); for (int idx = 0; idx < functionParams.Count; idx++) { CheckParameter(functionParams[idx], StringUtil.FormatIndex("function.Parameters", idx)); } } /// /// Verifies that the specified EntitySet is valid with respect to the command tree /// /// The EntitySet to verify /// The variable name to use if an exception should be thrown private static void CheckEntitySet(EntitySetBase entitySet, string varName) { EntityUtil.CheckArgumentNull(entitySet, varName); CheckReadOnly(entitySet, varName); // EntitySetBase constructor is responsible for basic validation of set name and element type Debug.Assert(!string.IsNullOrEmpty(entitySet.Name), "EntitySetBase constructor allowed null/empty set name?"); // // Verify the Extent's Container // if (null == entitySet.EntityContainer) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EntitySetEntityContainerNull, varName); } if(!CheckDataSpace(entitySet.EntityContainer)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EntitySetIncorrectSpace, varName); } // // Verify the Extent's Entity Type // // EntitySetBase constructor is responsible for basic validation of set name and element type Debug.Assert(entitySet.ElementType != null, "EntitySetBase constructor allowed null container?"); if(!CheckDataSpace(entitySet.ElementType)) { throw EntityUtil.Argument(System.Data.Entity.Strings.Cqt_Metadata_EntitySetIncorrectSpace, varName); } } private static bool CheckDataSpace(TypeUsage type) { return CheckDataSpace(type.EdmType); } private static bool CheckDataSpace(GlobalItem item) { // Since the set of primitive types and canonical functions are shared, we don't need to check for them. // Additionally, any non-canonical function in the C-Space must be a cached store function, which will // also not be present in the workspace. if (BuiltInTypeKind.PrimitiveType == item.BuiltInTypeKind || (BuiltInTypeKind.EdmFunction == item.BuiltInTypeKind && DataSpace.CSpace == item.DataSpace)) { return true; } // Transient types should be checked according to their non-transient components if (Helper.IsRowType(item)) { foreach (EdmProperty prop in ((RowType)item).Properties) { if (!CheckDataSpace(prop.TypeUsage)) { return false; } } return true; } else if (Helper.IsCollectionType(item)) { return CheckDataSpace(((CollectionType)item).TypeUsage); } else if (Helper.IsRefType(item)) { return CheckDataSpace(((RefType)item).ElementType); } else { return (item.DataSpace == DataSpace.SSpace || item.DataSpace == DataSpace.CSpace); } } private static TypeUsage CreateCollectionOfRowResultType(List> columns) { TypeUsage retUsage = TypeUsage.Create( TypeHelpers.CreateCollectionType( TypeUsage.Create( TypeHelpers.CreateRowType(columns) ) ) ); return retUsage; } private static TypeUsage CreateCollectionResultType(EdmType type) { TypeUsage retUsage = TypeUsage.Create( TypeHelpers.CreateCollectionType( TypeUsage.Create(type) ) ); return retUsage; } private static TypeUsage CreateCollectionResultType(TypeUsage type) { TypeUsage retUsage = TypeUsage.Create(TypeHelpers.CreateCollectionType(type)); return retUsage; } private static TypeUsage CreateResultType(EdmType resultType) { return TypeUsage.Create(resultType); } private static TypeUsage CreateResultType(RelationshipEndMember end) { TypeUsage retType = end.TypeUsage; if (!TypeSemantics.IsReferenceType(retType)) { // // The only relation end that is currently allowed to have a non-Reference type is the Child end of // a composition, in which case the end type must be an entity type. // //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"); retType = TypeHelpers.CreateReferenceTypeUsage(TypeHelpers.GetEdmType (retType)); } // // If the upper bound is not 1 the result type is a collection of the given type // if (RelationshipMultiplicity.Many == end.RelationshipMultiplicity) { retType = TypeHelpers.CreateCollectionTypeUsage(retType); } return retType; } private static TypeUsage CreateReferenceResultType(EntityTypeBase referencedEntityType) { return TypeUsage.Create(TypeHelpers.CreateReferenceType(referencedEntityType)); } /// /// Requires: non-null expression /// Determines whether the expression is a constant negative integer value. Always returns /// false for non-constant, non-integer expression instances. /// private static bool IsConstantNegativeInteger(DbExpression expression) { return (expression.ExpressionKind == DbExpressionKind.Constant && TypeSemantics.IsIntegerNumericType(expression.ResultType) && Convert.ToInt64(((DbConstantExpression)expression).Value, CultureInfo.InvariantCulture) < 0); } private static bool TryGetPrimitiveTypeKind(Type clrType, out PrimitiveTypeKind primitiveTypeKind) { primitiveTypeKind = default(PrimitiveTypeKind); PrimitiveType primitiveType = null; if (ClrProviderManifest.Instance.TryGetPrimitiveType(clrType, out primitiveType)) { primitiveTypeKind = primitiveType.PrimitiveTypeKind; return true; } return false; } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- FieldToken.cs
- QuaternionConverter.cs
- CommandTreeTypeHelper.cs
- TextContainerChangedEventArgs.cs
- ViewUtilities.cs
- ImportCatalogPart.cs
- PrintController.cs
- PowerModeChangedEventArgs.cs
- Configuration.cs
- PathFigure.cs
- ObjectStateFormatter.cs
- QilName.cs
- TableItemPattern.cs
- ViewManager.cs
- InstanceDataCollection.cs
- RepeaterItemCollection.cs
- Helpers.cs
- DefaultHttpHandler.cs
- CapabilitiesState.cs
- UpdateTracker.cs
- OptimisticConcurrencyException.cs
- AsymmetricKeyExchangeDeformatter.cs
- ActivityDesigner.cs
- XmlWriterSettings.cs
- SerializationSectionGroup.cs
- SourceElementsCollection.cs
- ScriptResourceInfo.cs
- Operators.cs
- TextLine.cs
- TdsParserStaticMethods.cs
- AlignmentXValidation.cs
- SystemIcmpV6Statistics.cs
- XomlCompilerResults.cs
- StyleCollection.cs
- SemanticAnalyzer.cs
- JsonFormatWriterGenerator.cs
- MemoryStream.cs
- ThemeInfoAttribute.cs
- CompensationToken.cs
- PlatformCulture.cs
- DotExpr.cs
- Pointer.cs
- Connector.xaml.cs
- SharedTcpTransportManager.cs
- ListChunk.cs
- TypeExtensionSerializer.cs
- NetworkStream.cs
- ThreadStateException.cs
- MenuItemStyleCollectionEditor.cs
- XmlUtilWriter.cs
- ObjectDataSourceFilteringEventArgs.cs
- ClockGroup.cs
- DateTimeConverter.cs
- GrabHandleGlyph.cs
- ConfigurationManagerInternalFactory.cs
- UnmanagedMemoryStream.cs
- _ConnectOverlappedAsyncResult.cs
- XPathMessageFilterElement.cs
- DocumentGrid.cs
- AutomationIdentifierGuids.cs
- securitycriticaldata.cs
- GridItemCollection.cs
- XsltCompileContext.cs
- AnnotationObservableCollection.cs
- SiteMapDataSourceView.cs
- StylusPointDescription.cs
- SafeProcessHandle.cs
- PerformanceCounterLib.cs
- InstanceHandleReference.cs
- DiagnosticsConfigurationHandler.cs
- NotifyIcon.cs
- XmlSerializationReader.cs
- XmlTypeMapping.cs
- ColorEditor.cs
- PropertyManager.cs
- SiteMapNodeItem.cs
- EdmError.cs
- GridPattern.cs
- WebResponse.cs
- OleDbError.cs
- ResponseBodyWriter.cs
- Baml2006SchemaContext.cs
- SHA512.cs
- SqlTransaction.cs
- CacheAxisQuery.cs
- base64Transforms.cs
- ColorConverter.cs
- StructuredProperty.cs
- Camera.cs
- XXXInfos.cs
- CanExpandCollapseAllConverter.cs
- FlowDocumentFormatter.cs
- LocalClientSecuritySettings.cs
- XmlDataSourceView.cs
- DeploymentSectionCache.cs
- ResourceDictionary.cs
- DataContractSerializerOperationFormatter.cs
- DtcInterfaces.cs
- XPathSelfQuery.cs
- TranslateTransform3D.cs