/// <summary> /// Generates a dictionary with property name and property values specified in the skiptoken value. /// </summary> /// <param name="value">The skiptoken string value.</param> /// <param name="context">The <see cref="ODataQueryContext"/> which contains the <see cref="IEdmModel"/> and some type information</param> /// <returns>Dictionary with property name and property value in the skiptoken value.</returns> internal static IDictionary <string, object> PopulatePropertyValuePairs(string value, ODataQueryContext context) { Contract.Assert(context != null); IDictionary <string, object> propertyValuePairs = new Dictionary <string, object>(); IList <string> keyValuesPairs = ParseValue(value, CommaDelimiter); IEdmStructuredType type = context.ElementType as IEdmStructuredType; Debug.Assert(type != null); foreach (string pair in keyValuesPairs) { string[] pieces = pair.Split(new char[] { propertyDelimiter }, 2); if (pieces.Length > 1 && !String.IsNullOrWhiteSpace(pieces[0])) { object propValue = null; IEdmTypeReference propertyType = null; IEdmProperty property = type.FindProperty(pieces[0]); if (property != null) { propertyType = property.Type; } propValue = ODataUriUtils.ConvertFromUriLiteral(pieces[1], ODataVersion.V401, context.Model, propertyType); propertyValuePairs.Add(pieces[0], propValue); } else { throw new ODataException(Error.Format(SRResources.SkipTokenParseError, value)); } } return(propertyValuePairs); }
/// <summary> /// Core logic for applying the query option to the IQueryable. /// </summary> /// <param name="query">The original <see cref="IQueryable"/>.</param> /// <param name="querySettings">Query setting used for validating the query option.</param> /// <param name="orderByNodes">OrderBy information required to correctly apply the query option for default implementation.</param> /// <param name="context">The <see cref="ODataQueryContext"/> which contains the <see cref="IEdmModel"/> and some type information</param> /// <param name="skipTokenRawValue">The raw string value of the skiptoken query parameter.</param> /// <returns></returns> private static IQueryable ApplyToCore(IQueryable query, ODataQuerySettings querySettings, IList <OrderByNode> orderByNodes, ODataQueryContext context, string skipTokenRawValue) { Contract.Assert(query != null); Contract.Assert(context.ElementClrType != null); IDictionary <string, OrderByDirection> directionMap; if (orderByNodes != null) { directionMap = orderByNodes.OfType <OrderByPropertyNode>().ToDictionary(node => node.Property.Name, node => node.Direction); } else { directionMap = new Dictionary <string, OrderByDirection>(); } IDictionary <string, object> propertyValuePairs = PopulatePropertyValuePairs(skipTokenRawValue, context); if (propertyValuePairs.Count == 0) { throw Error.InvalidOperation("Unable to get property values from the skiptoken value."); } ExpressionBinderBase binder = context.GetFilterBinder(querySettings); bool parameterizeConstant = querySettings.EnableConstantParameterization; ParameterExpression param = Expression.Parameter(context.ElementClrType); Expression where = null; /* We will create a where lambda of the following form - * Where (Prop1>Value1) * OR (Prop1=Value1 AND Prop2>Value2) * OR (Prop1=Value1 AND Prop2=Value2 AND Prop3>Value3) * and so on... * Adding the first true to simplify implementation. */ Expression lastEquality = null; bool firstProperty = true; foreach (KeyValuePair <string, object> item in propertyValuePairs) { string key = item.Key; MemberExpression property = Expression.Property(param, key); object value = item.Value; Expression compare = null; ODataEnumValue enumValue = value as ODataEnumValue; if (enumValue != null) { value = enumValue.Value; } Expression constant = parameterizeConstant ? LinqParameterContainer.Parameterize(value.GetType(), value) : Expression.Constant(value); if (directionMap.ContainsKey(key) && directionMap[key] == OrderByDirection.Descending) { compare = binder.CreateBinaryExpression(BinaryOperatorKind.LessThan, property, constant, true); } else { compare = binder.CreateBinaryExpression(BinaryOperatorKind.GreaterThan, property, constant, true); } if (firstProperty) { lastEquality = binder.CreateBinaryExpression(BinaryOperatorKind.Equal, property, constant, true); where = compare; firstProperty = false; } else { Expression condition = Expression.AndAlso(lastEquality, compare); where = Expression.OrElse(where, condition); lastEquality = Expression.AndAlso(lastEquality, binder.CreateBinaryExpression(BinaryOperatorKind.Equal, property, constant, true)); } } Expression whereLambda = Expression.Lambda(where, param); return(ExpressionHelpers.Where(query, whereLambda, query.ElementType)); }