private Expression VerifyQueryDeserialization <T>(string clauseString, string expectedResult = null, Action <ODataQuerySettings> settingsCustomizer = null) where T : class { IEdmModel model = GetModel <T>(); ApplyClause clause = CreateApplyNode(clauseString, model, typeof(T)); IWebApiAssembliesResolver assembliesResolver = WebApiAssembliesResolverFactory.Create(); Func <ODataQuerySettings, ODataQuerySettings> customizeSettings = (settings) => { if (settingsCustomizer != null) { settingsCustomizer.Invoke(settings); } return(settings); }; var binder = new AggregationBinder( customizeSettings(new ODataQuerySettings { HandleNullPropagation = HandleNullPropagationOption.False }), assembliesResolver, typeof(T), model, clause.Transformations.First()); var query = Enumerable.Empty <T>().AsQueryable(); var queryResult = binder.Bind(query); var applyExpr = queryResult.Expression; VerifyExpression <T>(applyExpr, expectedResult); return(applyExpr); }
/// <summary> /// Apply the apply query to the given IQueryable. /// </summary> /// <remarks> /// The <see cref="ODataQuerySettings.HandleNullPropagation"/> property specifies /// how this method should handle null propagation. /// </remarks> /// <param name="query">The original <see cref="IQueryable"/>.</param> /// <param name="querySettings">The <see cref="ODataQuerySettings"/> that contains all the query application related settings.</param> /// <returns>The new <see cref="IQueryable"/> after the filter query has been applied to.</returns> public IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings) { if (query == null) { throw Error.ArgumentNull("query"); } if (querySettings == null) { throw Error.ArgumentNull("querySettings"); } if (Context.ElementClrType == null) { throw Error.NotSupported(SRResources.ApplyToOnUntypedQueryOption, "ApplyTo"); } ApplyClause applyClause = ApplyClause; Contract.Assert(applyClause != null); ODataQuerySettings updatedSettings = Context.UpdateQuerySettings(querySettings, query); // The IWebApiAssembliesResolver service is internal and can only be injected by WebApi. // This code path may be used in cases when the service container is not available // and the service container is available but may not contain an instance of IWebApiAssembliesResolver. IWebApiAssembliesResolver assembliesResolver = WebApiAssembliesResolver.Default; if (Context.RequestContainer != null) { IWebApiAssembliesResolver injectedResolver = Context.RequestContainer.GetService <IWebApiAssembliesResolver>(); if (injectedResolver != null) { assembliesResolver = injectedResolver; } } foreach (var transformation in applyClause.Transformations) { if (transformation.Kind == TransformationNodeKind.Aggregate || transformation.Kind == TransformationNodeKind.GroupBy) { var binder = new AggregationBinder(updatedSettings, assembliesResolver, ResultClrType, Context.Model, transformation); query = binder.Bind(query); this.ResultClrType = binder.ResultClrType; } else if (transformation.Kind == TransformationNodeKind.Filter) { var filterTransformation = transformation as FilterTransformationNode; Expression filter = FilterBinder.Bind(query, filterTransformation.FilterClause, ResultClrType, Context, querySettings); query = ExpressionHelpers.Where(query, filter, ResultClrType); } } return(query); }
public IQueryable Bind(IQueryable query, ApplyClause applyClause) { // groupby and aggregate transform input by collapsing everything not used in groupby/aggregate // as a result we have two distinct cases for expand implementation // 1. Expands followed by groupby/aggregate with entity set aggregations => filters in expand need to be applied (pushed down) to corresponding entityset aggregations // 2. Mix of expands and filters w/o any groupby/aggregation => falling back to $expand behavior and could just use SelectExpandBinder bool inputShapeChanged = false; foreach (var transformation in applyClause.Transformations) { if (transformation.Kind == TransformationNodeKind.Aggregate || transformation.Kind == TransformationNodeKind.GroupBy) { var binder = new AggregationBinder(_settings, _assembliesResolver, ResultClrType, _context.Model, transformation, _context, SelectExpandClause); query = binder.Bind(query); this.ResultClrType = binder.ResultClrType; inputShapeChanged = true; } else if (transformation.Kind == TransformationNodeKind.Compute) { var binder = new ComputeBinder(_settings, _assembliesResolver, ResultClrType, _context.Model, (ComputeTransformationNode)transformation); query = binder.Bind(query); this.ResultClrType = binder.ResultClrType; inputShapeChanged = true; } else if (transformation.Kind == TransformationNodeKind.Filter) { var filterTransformation = (FilterTransformationNode)transformation; Expression filter = FilterBinder.Bind(query, filterTransformation.FilterClause, ResultClrType, _context, _settings); query = ExpressionHelpers.Where(query, filter, ResultClrType); } else if (transformation.Kind == TransformationNodeKind.Expand) { var newClause = ((ExpandTransformationNode)transformation).ExpandClause; if (SelectExpandClause == null) { SelectExpandClause = newClause; } else { SelectExpandClause = new SelectExpandClause(SelectExpandClause.SelectedItems.Concat(newClause.SelectedItems), false); } } } if (SelectExpandClause != null && !inputShapeChanged) { var expandString = GetExpandsOnlyString(SelectExpandClause); var selectExpandQueryOption = new SelectExpandQueryOption(null, expandString, _context, SelectExpandClause); query = SelectExpandBinder.Bind(query, _settings, selectExpandQueryOption); } return(query); }
/// <summary> /// Apply the apply query to the given IQueryable. /// </summary> /// <remarks> /// The <see cref="ODataQuerySettings.HandleNullPropagation"/> property specifies /// how this method should handle null propagation. /// </remarks> /// <param name="query">The original <see cref="IQueryable"/>.</param> /// <param name="querySettings">The <see cref="ODataQuerySettings"/> that contains all the query application related settings.</param> /// <param name="assemblyProvider">The <see cref="IAssemblyProvider"/> to use.</param> /// <returns>The new <see cref="IQueryable"/> after the filter query has been applied to.</returns> public IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings, IAssemblyProvider assemblyProvider) { if (query == null) { throw Error.ArgumentNull("query"); } if (querySettings == null) { throw Error.ArgumentNull("querySettings"); } if (assemblyProvider == null) { throw Error.ArgumentNull("assemblyProvider"); } if (Context.ElementClrType == null) { throw Error.NotSupported(SRResources.ApplyToOnUntypedQueryOption, "ApplyTo"); } ApplyClause applyClause = ApplyClause; Contract.Assert(applyClause != null); // Ensure we have decided how to handle null propagation ODataQuerySettings updatedSettings = querySettings; if (querySettings.HandleNullPropagation == HandleNullPropagationOption.Default) { updatedSettings = new ODataQuerySettings(updatedSettings); updatedSettings.HandleNullPropagation = HandleNullPropagationOptionHelper.GetDefaultHandleNullPropagationOption(query); } foreach (var transformation in applyClause.Transformations) { if (transformation.Kind == TransformationNodeKind.Aggregate || transformation.Kind == TransformationNodeKind.GroupBy) { var binder = new AggregationBinder(updatedSettings, assemblyProvider, ResultClrType, Context.Model, transformation as TransformationNode, _serviceProvider); query = binder.Bind(query); this.ResultClrType = binder.ResultClrType; } else if (transformation.Kind == TransformationNodeKind.Filter) { var filterTransformation = transformation as FilterTransformationNode; Expression filter = FilterBinder.Bind(filterTransformation.FilterClause, ResultClrType, Context.Model, assemblyProvider, updatedSettings, _serviceProvider); query = ExpressionHelpers.Where(query, filter, ResultClrType); } } return(query); }
/// <summary> /// Apply the apply query to the given IQueryable. /// </summary> /// <remarks> /// The <see cref="ODataQuerySettings.HandleNullPropagation"/> property specifies /// how this method should handle null propagation. /// </remarks> /// <param name="query">The original <see cref="IQueryable"/>.</param> /// <param name="querySettings">The <see cref="ODataQuerySettings"/> that contains all the query application related settings.</param> /// <returns>The new <see cref="IQueryable"/> after the filter query has been applied to.</returns> public IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings) { if (query == null) { throw Error.ArgumentNull("query"); } if (querySettings == null) { throw Error.ArgumentNull("querySettings"); } if (Context.ElementClrType == null) { throw Error.NotSupported(SRResources.ApplyToOnUntypedQueryOption, "ApplyTo"); } ApplyClause applyClause = ApplyClause; Contract.Assert(applyClause != null); ODataQuerySettings updatedSettings = Context.UpdateQuerySettings(querySettings, query); foreach (var transformation in applyClause.Transformations) { if (transformation.Kind == TransformationNodeKind.Aggregate || transformation.Kind == TransformationNodeKind.GroupBy) { var binder = new AggregationBinder(updatedSettings, _assembliesResolver, ResultClrType, Context.Model, transformation); query = binder.Bind(query); this.ResultClrType = binder.ResultClrType; } else if (transformation.Kind == TransformationNodeKind.Filter) { var filterTransformation = transformation as FilterTransformationNode; Expression filter = FilterBinder.Bind(query, filterTransformation.FilterClause, ResultClrType, Context.RequestContainer); query = ExpressionHelpers.Where(query, filter, ResultClrType); } } return(query); }
/// <summary> /// Apply the apply query to the given IQueryable. /// </summary> /// <remarks> /// The <see cref="ODataQuerySettings.HandleNullPropagation"/> property specifies /// how this method should handle null propagation. /// </remarks> /// <param name="query">The original <see cref="IQueryable"/>.</param> /// <param name="querySettings">The <see cref="ODataQuerySettings"/> that contains all the query application related settings.</param> /// <returns>The new <see cref="IQueryable"/> after the filter query has been applied to.</returns> public IQueryable ApplyTo(IQueryable query, ODataQuerySettings querySettings) { if (query == null) { throw Error.ArgumentNull(nameof(query)); } if (querySettings == null) { throw Error.ArgumentNull(nameof(querySettings)); } if (Context.ElementClrType == null) { throw Error.NotSupported(SRResources.ApplyToOnUntypedQueryOption, "ApplyTo"); } // Linq to SQL not supported for $apply if (query.Provider.GetType().Namespace == HandleNullPropagationOptionHelper.Linq2SqlQueryProviderNamespace) { throw Error.NotSupported(SRResources.ApplyQueryOptionNotSupportedForLinq2SQL); } ApplyClause applyClause = ApplyClause; Contract.Assert(applyClause != null); // The IWebApiAssembliesResolver service is internal and can only be injected by WebApi. // This code path may be used in cases when the service container is not available // and the service container is available but may not contain an instance of IWebApiAssembliesResolver. IAssemblyResolver assembliesResolver = AssemblyResolverHelper.Default; if (Context.RequestContainer != null) { IAssemblyResolver injectedResolver = Context.RequestContainer.GetService <IAssemblyResolver>(); if (injectedResolver != null) { assembliesResolver = injectedResolver; } } foreach (var transformation in applyClause.Transformations) { if (transformation.Kind == TransformationNodeKind.Aggregate || transformation.Kind == TransformationNodeKind.GroupBy) { var binder = new AggregationBinder(querySettings, assembliesResolver, ResultClrType, Context.Model, transformation); query = binder.Bind(query); this.ResultClrType = binder.ResultClrType; } else if (transformation.Kind == TransformationNodeKind.Compute) { var binder = new ComputeBinder(querySettings, assembliesResolver, ResultClrType, Context.Model, (ComputeTransformationNode)transformation); query = binder.Bind(query); this.ResultClrType = binder.ResultClrType; } else if (transformation.Kind == TransformationNodeKind.Filter) { var filterTransformation = transformation as FilterTransformationNode; IFilterBinder binder = Context.GetFilterBinder(); QueryBinderContext binderContext = new QueryBinderContext(Context.Model, querySettings, ResultClrType); query = binder.ApplyBind(query, filterTransformation.FilterClause, binderContext); } } return(query); }