/// <summary> /// Analyzes a lambda expression to check whether it can be satisfied with /// $select and client-side materialization. /// </summary> /// <param name="le">Lambda expression.</param> /// <param name="re">Resource expression in scope.</param> /// <param name="matchMembers">Whether member accesses are matched as top-level projections.</param> /// <param name="context">Context of expression to analyze.</param> /// <returns>true if the lambda is a client-side projection; false otherwise.</returns> internal static bool Analyze(LambdaExpression le, ResourceExpression re, bool matchMembers, DataServiceContext context) { Debug.Assert(le != null, "le != null"); if (le.Body.NodeType == ExpressionType.Constant) { if (ClientTypeUtil.TypeOrElementTypeIsEntity(le.Body.Type)) { throw new NotSupportedException(Strings.ALinq_CannotCreateConstantEntity); } re.Projection = new ProjectionQueryOptionExpression(le.Body.Type, le, new List <string>()); return(true); } if (le.Body.NodeType == ExpressionType.MemberInit || le.Body.NodeType == ExpressionType.New) { AnalyzeResourceExpression(le, re, context); return(true); } if (matchMembers) { // Members can be projected standalone or type-casted. Expression withoutConverts = SkipConverts(le.Body); if (withoutConverts.NodeType == ExpressionType.MemberAccess) { AnalyzeResourceExpression(le, re, context); return(true); } } return(false); }
internal override Expression VisitMemberAccess(MemberExpression m) { PropertyInfo info; Expression expression; Type type = m.Expression.Type; this.leafExpressionIsMemberAccess = true; if (PrimitiveType.IsKnownNullableType(type)) { this.leafExpressionIsMemberAccess = false; return(base.VisitMemberAccess(m)); } if (ProjectionAnalyzer.IsCollectionProducingExpression(m.Expression)) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString())); } if (!ResourceBinder.PatternRules.MatchNonPrivateReadableProperty(m, out info, out expression)) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjection(this.type, m.ToString())); } Expression expression2 = base.VisitMemberAccess(m); if (ClientTypeUtil.TypeOrElementTypeIsEntity(type)) { Type type2; ResourceBinder.StripTo <Expression>(m.Expression, out type2); this.box.AppendPropertyToPath(info, type2, this.context); this.leafExpressionIsMemberAccess = false; } return(expression2); }
/// <summary> /// Materializes the primitive data values in the given list of <paramref name="values"/>. /// </summary> /// <param name="actualType">Actual type for properties being materialized.</param> /// <param name="values">List of values to materialize.</param> /// <param name="undeclaredPropertyBehavior"> /// Whether properties missing from the client types should be supported or throw exception. /// </param> /// <remarks> /// Values are materialized in-place with each <see cref="ODataProperty"/> /// instance. /// </remarks> internal void MaterializeDataValues(ClientTypeAnnotation actualType, IEnumerable <ODataProperty> values, UndeclaredPropertyBehavior undeclaredPropertyBehavior) { Debug.Assert(actualType != null, "actualType != null"); Debug.Assert(values != null, "values != null"); foreach (var odataProperty in values) { if (odataProperty.Value is ODataStreamReferenceValue) { continue; } string propertyName = odataProperty.Name; var property = actualType.GetProperty(propertyName, undeclaredPropertyBehavior); // may throw if (property == null) { continue; } // we should throw if the property type on the client does not match with the property type in the server // This is a breaking change from V1/V2 where we allowed materialization of entities into non-entities and vice versa if (ClientTypeUtil.TypeOrElementTypeIsEntity(property.PropertyType)) { throw DSClient.Error.InvalidOperation(DSClient.Strings.AtomMaterializer_InvalidEntityType(property.EntityCollectionItemType ?? property.PropertyType)); } if (property.IsKnownType) { // Note: MaterializeDataValue materializes only properties of primitive types. Materialization specific // to complex types and collections is done later. this.MaterializePrimitiveDataValue(property.NullablePropertyType, odataProperty); } } }
internal override NewExpression VisitNew(NewExpression nex) { if (ResourceBinder.PatternRules.MatchNewDataServiceCollectionOfT(nex)) { if (ClientTypeUtil.TypeOrElementTypeIsEntity(nex.Type)) { foreach (Expression expression in nex.Arguments) { if (expression.NodeType != ExpressionType.Constant) { base.Visit(expression); } } return(nex); } } else if (ResourceBinder.PatternRules.MatchNewCollectionOfT(nex) && !ClientTypeUtil.TypeOrElementTypeIsEntity(nex.Type)) { foreach (Expression expression2 in nex.Arguments) { if (expression2.NodeType != ExpressionType.Constant) { base.Visit(expression2); } } return(nex); } throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, nex.ToString())); }
/// <summary> /// Appends the given property and source TypeAs to the projection and expand paths. /// </summary> /// <param name="pi">Navigation property</param> /// <param name="convertedSourceType">The TypeAs type if the source of the member access expression is a TypeAs operation. Null otherwise.</param> /// <param name="context">Data service context instance.</param> internal void AppendPropertyToPath(PropertyInfo pi, Type convertedSourceType, DataServiceContext context) { Debug.Assert(pi != null, "pi != null"); StringBuilder sb; bool propertyTypeisEntityType = ClientTypeUtil.TypeOrElementTypeIsEntity(pi.PropertyType); string convertedSourceTypeName = (convertedSourceType == null) ? null : UriHelper.GetEntityTypeNameForUriAndValidateMaxProtocolVersion(convertedSourceType, context, ref this.uriVersion); if (propertyTypeisEntityType) { // an entity, so need to append to expand path also if (convertedSourceType != null) { AppendToExpandPath(convertedSourceTypeName); } AppendToExpandPath(pi.Name); } sb = null; if (convertedSourceType != null) { AppendToProjectionPath(convertedSourceTypeName, false); } sb = AppendToProjectionPath(pi.Name, false); if (propertyTypeisEntityType) { AddEntireEntityMarker(sb); } }
/// <summary> /// CODE: x /// ORIGINAL: Convert(x, t) where t is assignable from typeof(x) /// ORIGINAL: x as t, where t is assignable from typeof(x) /// ORIGINAL: and typeof(x) or t are not known primitives unless typeof(x) == t /// ORIGINAL: and x is not a collection of entity types /// NORMALIZED: x /// </summary> internal override Expression VisitUnary(UnaryExpression u) { UnaryExpression visited = (UnaryExpression)base.VisitUnary(u); Expression result = visited; // Note that typically we would record a potential rewrite // after extracting the conversion, but we avoid doing this // because it breaks undoing the rewrite by making the non-local // change circular, ie: // unary [operand = a] // becomes // a <- unary [operand = a] // So the undoing visits a, then the original unary, then the // operand and again the unary, the operand, etc. this.RecordRewrite(u, result); // Convert(x, t) or x as t, where t is assignable from typeof(x) if ((visited.NodeType == ExpressionType.Convert || visited.NodeType == ExpressionType.TypeAs) && visited.Type.IsAssignableFrom(visited.Operand.Type)) { // typeof(x) or t are not known primitives unless typeof(x) == t if (!PrimitiveType.IsKnownNullableType(visited.Operand.Type) && !PrimitiveType.IsKnownNullableType(visited.Type) || visited.Operand.Type == visited.Type) { // x is not a collection of entity types if (!(ClientTypeUtil.TypeOrElementTypeIsEntity(visited.Operand.Type) && ProjectionAnalyzer.IsCollectionProducingExpression(visited.Operand))) { result = visited.Operand; } } } return(result); }
internal override Expression VisitLambda(LambdaExpression lambda) { if (!this.topLevelProjectionFound || ((lambda.Parameters.Count == 1) && ClientTypeUtil.TypeOrElementTypeIsEntity(lambda.Parameters[0].Type))) { this.topLevelProjectionFound = true; ParameterExpression expectedType = Expression.Parameter(typeof(Type), "type" + this.identifierId); ParameterExpression entry = Expression.Parameter(typeof(object), "entry" + this.identifierId); this.identifierId++; this.pathBuilder.EnterLambdaScope(lambda, entry, expectedType); ProjectionPath startPath = new ProjectionPath(lambda.Parameters[0], expectedType, entry); ProjectionPathSegment item = new ProjectionPathSegment(startPath, null, null); startPath.Add(item); ExpressionAnnotation annotation = new ExpressionAnnotation { Segment = item }; this.annotations[lambda.Parameters[0]] = annotation; Expression expression4 = this.Visit(lambda.Body); if (expression4.Type.IsValueType) { expression4 = Expression.Convert(expression4, typeof(object)); } Expression expression = Expression.Lambda <Func <object, object, Type, object> >(expression4, new ParameterExpression[] { this.materializerExpression, entry, expectedType }); this.pathBuilder.LeaveLambdaScope(); return(expression); } return(base.VisitLambda(lambda)); }
internal override Expression VisitConditional(ConditionalExpression conditional) { Expression expressionBeforeNormalization = this.GetExpressionBeforeNormalization(conditional); if (expressionBeforeNormalization != conditional) { return(this.Visit(expressionBeforeNormalization)); } ResourceBinder.PatternRules.MatchNullCheckResult nullCheck = ResourceBinder.PatternRules.MatchNullCheck(this.pathBuilder.LambdaParameterInScope, conditional); if (!nullCheck.Match || !ClientTypeUtil.TypeOrElementTypeIsEntity(ResourceBinder.StripConvertToAssignable(nullCheck.TestToNullExpression).Type)) { Expression test = null; if (nullCheck.Match) { Expression left = this.Visit(nullCheck.TestToNullExpression); if (left.NodeType == ExpressionType.Convert) { left = ((UnaryExpression)left).Operand; } test = Expression.MakeBinary(ExpressionType.Equal, left, Expression.Constant(null)); } if (test == null) { test = this.Visit(conditional.Test); } Expression ifTrue = this.Visit(conditional.IfTrue); Expression ifFalse = this.Visit(conditional.IfFalse); if (((test != conditional.Test) || (ifTrue != conditional.IfTrue)) || (ifFalse != conditional.IfFalse)) { return(Expression.Condition(test, ifTrue, ifFalse, ifTrue.Type.IsAssignableFrom(ifFalse.Type) ? ifTrue.Type : ifFalse.Type)); } } return(this.RebindConditionalNullCheck(conditional, nullCheck)); }
private Expression RebindMethodCallForMemberSelect(MethodCallExpression call) { Expression key = null; ExpressionAnnotation annotation; Expression expression2 = this.Visit(call.Arguments[0]); this.annotations.TryGetValue(expression2, out annotation); if (annotation != null) { LambdaExpression expression3 = call.Arguments[1] as LambdaExpression; ParameterExpression expression4 = expression3.Parameters.Last <ParameterExpression>(); Expression expression5 = this.Visit(call.Arguments[1]); if (ClientTypeUtil.TypeOrElementTypeIsEntity(expression4.Type)) { Type type = call.Method.ReturnType.GetGenericArguments()[0]; key = CallMaterializer("ProjectionSelect", new Expression[] { this.materializerExpression, this.pathBuilder.ParameterEntryInScope, this.pathBuilder.ExpectedParamTypeInScope, Expression.Constant(type, typeof(Type)), Expression.Constant(annotation.Segment.StartPath, typeof(object)), expression5 }); this.annotations.Add(key, annotation); key = CallMaterializerWithType("EnumerateAsElementType", new Type[] { type }, new Expression[] { key }); this.annotations.Add(key, annotation); } else { key = Expression.Call(call.Method, expression2, expression5); this.annotations.Add(key, annotation); } } if (key == null) { key = base.VisitMethodCall(call); } return(key); }
internal void AppendPropertyToPath(PropertyInfo pi, Type convertedSourceType, DataServiceContext context) { bool flag = ClientTypeUtil.TypeOrElementTypeIsEntity(pi.PropertyType); string name = (convertedSourceType == null) ? null : System.Data.Services.Client.UriHelper.GetEntityTypeNameForUriAndValidateMaxProtocolVersion(convertedSourceType, context, ref this.uriVersion); if (flag) { if (convertedSourceType != null) { this.AppendToExpandPath(name); } this.AppendToExpandPath(pi.Name); } StringBuilder sb = null; if (convertedSourceType != null) { this.AppendToProjectionPath(name, false); } sb = this.AppendToProjectionPath(pi.Name, false); if (flag) { AddEntireEntityMarker(sb); } }
internal override Expression VisitMemberAccess(MemberExpression m) { Debug.Assert(m != null, "m != null"); this.leafExpressionIsMemberAccess = true; // Only allowed to project entities if (!ClientTypeUtil.TypeOrElementTypeIsEntity(m.Expression.Type) || IsCollectionProducingExpression(m.Expression)) { throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, m.ToString())); } PropertyInfo pi; Expression boundTarget; if (ResourceBinder.PatternRules.MatchNonPrivateReadableProperty(m, out pi, out boundTarget)) { Expression e = base.VisitMemberAccess(m); Type convertedType; ResourceBinder.StripTo <Expression>(m.Expression, out convertedType); this.builder.AppendPropertyToPath(pi, convertedType, this.context); this.leafExpressionIsMemberAccess = false; return(e); } throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, m.ToString())); }
/// <summary> /// Determine if the specified type is an DataServiceCollection. /// </summary> /// <remarks> /// If there a generic class in the inheritance hierarchy of the type, that has a single /// entity type paramenter T, and is assignable to DataServiceCollection(Of T), then /// the type is an DataServiceCollection. /// </remarks> /// <param name="collectionType">An object type specifier.</param> /// <param name="model">The client model.</param> /// <returns>true if the type is an DataServiceCollection; otherwise false.</returns> internal static bool IsDataServiceCollection(Type collectionType, ClientEdmModel model) { Debug.Assert(collectionType != null, "Argument 'collectionType' cannot be null."); metadataCacheLock.EnterReadLock(); try { object resultAsObject; if (knownObservableCollectionTypes.TryGetValue(collectionType, out resultAsObject)) { return(resultAsObject == TrueObject); } } finally { metadataCacheLock.ExitReadLock(); } Type type = collectionType; bool result = false; while (type != null) { if (type.IsGenericType()) { // Is there a generic class in the inheritance hierarchy, that has a single // entity type paramenter T, and is assignable to DataServiceCollection<T> Type[] parms = type.GetGenericArguments(); if (parms != null && parms.Length == 1 && ClientTypeUtil.TypeOrElementTypeIsEntity(parms[0])) { // if ObservableCollection is not available dataServiceCollection will be null Type dataServiceCollection = WebUtil.GetDataServiceCollectionOfT(parms); if (dataServiceCollection != null && dataServiceCollection.IsAssignableFrom(type)) { result = true; break; } } } type = type.GetBaseType(); } metadataCacheLock.EnterWriteLock(); try { if (!knownObservableCollectionTypes.ContainsKey(collectionType)) { knownObservableCollectionTypes[collectionType] = result ? TrueObject : FalseObject; } } finally { metadataCacheLock.ExitWriteLock(); } return(result); }
internal override Expression VisitTypeIs(TypeBinaryExpression b) { if (ClientTypeUtil.TypeOrElementTypeIsEntity(b.Expression.Type) || ProjectionAnalyzer.IsCollectionProducingExpression(b.Expression)) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjection(this.type, b.ToString())); } return(base.VisitTypeIs(b)); }
internal override Expression VisitConstant(ConstantExpression c) { if (ClientTypeUtil.TypeOrElementTypeIsEntity(c.Type)) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjection(this.type, c.ToString())); } return(base.VisitConstant(c)); }
internal override NewExpression VisitNew(NewExpression nex) { if (ClientTypeUtil.TypeOrElementTypeIsEntity(nex.Type) && !ResourceBinder.PatternRules.MatchNewDataServiceCollectionOfT(nex)) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjection(this.type, nex.ToString())); } return(base.VisitNew(nex)); }
internal override Expression VisitMemberInit(MemberInitExpression init) { if (!ClientTypeUtil.TypeOrElementTypeIsEntity(init.Type)) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, init.ToString())); } ProjectionAnalyzer.Analyze(init, this.box, this.context); return(init); }
public void IFTypeProperty_HasKeyAttribute_TypeIsEntity() { //Arrange Type person = typeof(Person); //Act bool actualResult = ClientTypeUtil.TypeOrElementTypeIsEntity(person); //Assert Assert.True(actualResult); }
public void IFTypeProperty_HasNoKeyAttribute_TypeIsNotEntity() { //Arrange Type student = typeof(Student); //Act bool actualResult = ClientTypeUtil.TypeOrElementTypeIsEntity(student); //Assert Assert.False(actualResult); }
public void IFTypeProperty_HasKeyAttributeAndOneProperty_TypeIsEntityAndDoesNotThrowException() { //Arrange Type car = typeof(Car); //Act bool actualResult = ClientTypeUtil.TypeOrElementTypeIsEntity(car); //Assert Assert.True(actualResult); }
internal override Expression VisitBinary(BinaryExpression b) { if (ClientTypeUtil.TypeOrElementTypeIsEntity(b.Left.Type) || ClientTypeUtil.TypeOrElementTypeIsEntity(b.Right.Type) || IsCollectionProducingExpression(b.Left) || IsCollectionProducingExpression(b.Right)) { throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, b.ToString())); } return(base.VisitBinary(b)); }
internal override Expression VisitInvocation(InvocationExpression iv) { if (ClientTypeUtil.TypeOrElementTypeIsEntity(iv.Expression.Type) || IsCollectionProducingExpression(iv.Expression) || iv.Arguments.Any(a => ClientTypeUtil.TypeOrElementTypeIsEntity(a.Type) || IsCollectionProducingExpression(a))) { throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, iv.ToString())); } return(base.VisitInvocation(iv)); }
internal override Expression VisitMemberInit(MemberInitExpression init) { if (!ClientTypeUtil.TypeOrElementTypeIsEntity(init.Type)) { // MemberInit to a complex type is not supported on entity types. throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjectionToEntity(this.type, init.ToString())); } ProjectionAnalyzer.Analyze(init, this.builder, this.context); return(init); }
private static void Analyze(MemberInitExpression mie, PathBox pb, DataServiceContext context) { if (ClientTypeUtil.TypeOrElementTypeIsEntity(mie.Type)) { EntityProjectionAnalyzer.Analyze(mie, pb, context); } else { NonEntityProjectionAnalyzer.Analyze(mie, pb, context); } }
internal override NewExpression VisitNew(NewExpression nex) { // Allow creation of DataServiceCollection<T> objects in projections, stop others that project entities if (ClientTypeUtil.TypeOrElementTypeIsEntity(nex.Type) && !ResourceBinder.PatternRules.MatchNewDataServiceCollectionOfT(nex)) { throw new NotSupportedException(Strings.ALinq_ExpressionNotSupportedInProjection(this.type, nex.ToString())); } return(base.VisitNew(nex)); }
internal override Expression VisitParameter(ParameterExpression p) { if (ClientTypeUtil.TypeOrElementTypeIsEntity(p.Type)) { if (p != this.box.ParamExpressionInScope) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjection(this.type, p.ToString())); } this.box.StartNewPath(); } return(p); }
internal void EnterLambdaScope(LambdaExpression lambda, Expression entry, Expression expectedType) { ParameterExpression item = lambda.Parameters[0]; Type type = lambda.Body.Type; bool flag = ClientTypeUtil.TypeOrElementTypeIsEntity(type); this.entityInScope.Push(flag); this.parameterExpressions.Push(item); this.parameterExpressionTypes.Push(expectedType); this.parameterEntries.Push(entry); this.parameterProjectionTypes.Push(type); }
internal static string GetEntityTypeNameForUriAndValidateMaxProtocolVersion(Type type, DataServiceContext context, ref Version uriVersion) { if (context.MaxProtocolVersionAsVersion < Util.DataServiceVersion3) { throw new NotSupportedException(Strings.ALinq_TypeAsNotSupportedForMaxDataServiceVersionLessThan3); } if (!ClientTypeUtil.TypeOrElementTypeIsEntity(type)) { throw new NotSupportedException(Strings.ALinq_TypeAsArgumentNotEntityType(type.FullName)); } WebUtil.RaiseVersion(ref uriVersion, Util.DataServiceVersion3); return(context.ResolveNameFromType(type) ?? type.FullName); }
/// <summary> /// Determine if the specified type is an entity type. /// </summary> /// <param name="type">An object type specifier.</param> /// <param name="model">The client model.</param> /// <returns>true if the type is an entity type; otherwise false.</returns> internal static bool IsEntityType(Type type, ClientEdmModel model) { Debug.Assert(type != null, "Argument 'type' cannot be null."); metadataCacheLock.EnterReadLock(); try { if (knownNonEntityTypes.Contains(type)) { return(false); } } finally { metadataCacheLock.ExitReadLock(); } bool isEntityType; try { if (BindingEntityInfo.IsDataServiceCollection(type, model)) { return(false); } isEntityType = ClientTypeUtil.TypeOrElementTypeIsEntity(type); } catch (InvalidOperationException) { isEntityType = false; } if (!isEntityType) { metadataCacheLock.EnterWriteLock(); try { if (!knownNonEntityTypes.Contains(type)) { knownNonEntityTypes.Add(type); } } finally { metadataCacheLock.ExitWriteLock(); } } return(isEntityType); }
internal override Expression VisitConditional(ConditionalExpression c) { ResourceBinder.PatternRules.MatchNullCheckResult result = ResourceBinder.PatternRules.MatchNullCheck(this.box.ParamExpressionInScope, c); if (result.Match) { this.Visit(result.AssignExpression); return(c); } if (((ClientTypeUtil.TypeOrElementTypeIsEntity(c.Test.Type) || ClientTypeUtil.TypeOrElementTypeIsEntity(c.IfTrue.Type)) || (ClientTypeUtil.TypeOrElementTypeIsEntity(c.IfFalse.Type) || ProjectionAnalyzer.IsCollectionProducingExpression(c.Test))) || (ProjectionAnalyzer.IsCollectionProducingExpression(c.IfTrue) || ProjectionAnalyzer.IsCollectionProducingExpression(c.IfFalse))) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjection(this.type, c.ToString())); } return(base.VisitConditional(c)); }
internal override Expression VisitInvocation(InvocationExpression iv) { if ((ClientTypeUtil.TypeOrElementTypeIsEntity(iv.Expression.Type) || ProjectionAnalyzer.IsCollectionProducingExpression(iv.Expression)) || iv.Arguments.Any <Expression>(delegate(Expression a) { if (!ClientTypeUtil.TypeOrElementTypeIsEntity(a.Type)) { return(ProjectionAnalyzer.IsCollectionProducingExpression(a)); } return(true); })) { throw new NotSupportedException(System.Data.Services.Client.Strings.ALinq_ExpressionNotSupportedInProjection(this.type, iv.ToString())); } return(base.VisitInvocation(iv)); }