private FilterQueryContext GetQueryContexts(FilterQuery query, string parameterName) { var queryContext = new FilterQueryContext(query); var customQuery = _requestResourceDefinition?.GetCustomQueryFilter(query.Target); if (customQuery != null) { queryContext.IsCustom = true; queryContext.CustomQuery = customQuery; return(queryContext); } queryContext.Relationship = GetRelationship(parameterName, query.Relationship); var attribute = GetAttribute(parameterName, query.Attribute, queryContext.Relationship); if (queryContext.Relationship is HasManyAttribute) { throw new InvalidQueryStringParameterException(parameterName, "Filtering on one-to-many and many-to-many relationships is currently not supported.", $"Filtering on the relationship '{queryContext.Relationship.PublicRelationshipName}.{attribute.PublicAttributeName}' is currently not supported."); } if (!attribute.Capabilities.HasFlag(AttrCapabilities.AllowFilter)) { throw new InvalidQueryStringParameterException(parameterName, "Filtering on the requested attribute is not allowed.", $"Filtering on attribute '{attribute.PublicAttributeName}' is not allowed."); } queryContext.Attribute = attribute; return(queryContext); }
private FilterQueryContext GetQueryContexts(FilterQuery query, string parameterName) { var queryContext = new FilterQueryContext(query); var customQuery = _requestResourceDefinition?.GetCustomQueryFilter(query.Target); if (customQuery != null) { queryContext.IsCustom = true; queryContext.CustomQuery = customQuery; return(queryContext); } queryContext.Relationship = GetRelationship(parameterName, query.Relationship); var attribute = GetAttribute(parameterName, query.Attribute, queryContext.Relationship); if (!attribute.IsFilterable) { throw new InvalidQueryStringParameterException(parameterName, "Filtering on the requested attribute is not allowed.", $"Filtering on attribute '{attribute.PublicAttributeName}' is not allowed."); } queryContext.Attribute = attribute; return(queryContext); }
private FilterQueryContext GetQueryContexts(FilterQuery query) { var queryContext = new FilterQueryContext(query); if (_requestResourceDefinition != null) { var customQuery = _requestResourceDefinition.GetCustomQueryFilter(query.Target); if (customQuery != null) { queryContext.IsCustom = true; queryContext.CustomQuery = customQuery; return(queryContext); } } queryContext.Relationship = GetRelationship(query.Relationship); var attribute = GetAttribute(query.Attribute, queryContext.Relationship); if (attribute.IsFilterable == false) { throw new JsonApiException(400, $"Filter is not allowed for attribute '{attribute.PublicAttributeName}'."); } queryContext.Attribute = attribute; return(queryContext); }
/// <inheritdoc /> public virtual IQueryable <TResource> Filter(IQueryable <TResource> entities, FilterQueryContext filterQueryContext) { if (filterQueryContext.IsCustom) { var query = (Func <IQueryable <TResource>, FilterQuery, IQueryable <TResource> >)filterQueryContext.CustomQuery; return(query(entities, filterQueryContext.Query)); } return(entities.Filter(filterQueryContext)); }
public static IQueryable <TSource> Filter <TSource>(this IQueryable <TSource> source, FilterQueryContext filterQuery) { if (filterQuery == null) { return(source); } if (filterQuery.Operation == FilterOperation.@in || filterQuery.Operation == FilterOperation.nin) { return(CallGenericWhereContainsMethod(source, filterQuery)); } return(CallGenericWhereMethod(source, filterQuery)); }
/// <summary> /// This calls a generic where method.. more explaining to follow /// /// </summary> /// <typeparam name="TSource"></typeparam> /// <param name="source"></param> /// <param name="filter"></param> /// <returns></returns> private static IQueryable <TSource> CallGenericWhereMethod <TSource>(IQueryable <TSource> source, FilterQueryContext filter) { var op = filter.Operation; var concreteType = typeof(TSource); PropertyInfo relationProperty = null; PropertyInfo property = null; MemberExpression left; Expression right; // {model} var parameter = Expression.Parameter(concreteType, "model"); // Is relationship attribute if (filter.IsAttributeOfRelationship) { relationProperty = concreteType.GetProperty(filter.Relationship.InternalRelationshipName); if (relationProperty == null) { throw new ArgumentException($"'{filter.Relationship.InternalRelationshipName}' is not a valid relationship of '{concreteType}'"); } var relatedType = filter.Relationship.RightType; property = relatedType.GetProperty(filter.Attribute.PropertyInfo.Name); if (property == null) { throw new ArgumentException($"'{filter.Attribute.PropertyInfo.Name}' is not a valid attribute of '{filter.Relationship.InternalRelationshipName}'"); } var leftRelationship = Expression.PropertyOrField(parameter, filter.Relationship.InternalRelationshipName); // {model.Relationship} left = Expression.PropertyOrField(leftRelationship, property.Name); } // Is standalone attribute else { property = concreteType.GetProperty(filter.Attribute.PropertyInfo.Name); if (property == null) { throw new ArgumentException($"'{filter.Attribute.PropertyInfo.Name}' is not a valid property of '{concreteType}'"); } // {model.Id} left = Expression.PropertyOrField(parameter, property.Name); } try { if (op == FilterOperation.isnotnull || op == FilterOperation.isnull) { right = Expression.Constant(null); } else { // convert the incoming value to the target value type // "1" -> 1 var convertedValue = TypeHelper.ConvertType(filter.Value, property.PropertyType); right = CreateTupleAccessForConstantExpression(convertedValue, property.PropertyType); } var body = GetFilterExpressionLambda(left, right, filter.Operation); var lambda = Expression.Lambda <Func <TSource, bool> >(body, parameter); return(source.Where(lambda)); } catch (FormatException) { throw new JsonApiException(400, $"Could not cast {filter.Value} to {property.PropertyType.Name}"); } }
private static IQueryable <TSource> CallGenericWhereContainsMethod <TSource>(IQueryable <TSource> source, FilterQueryContext filter) { var concreteType = typeof(TSource); var property = concreteType.GetProperty(filter.Attribute.PropertyInfo.Name); try { var propertyValues = filter.Value.Split(QueryConstants.COMMA); ParameterExpression entity = Expression.Parameter(concreteType, "entity"); MemberExpression member; if (filter.IsAttributeOfRelationship) { var relation = Expression.PropertyOrField(entity, filter.Relationship.InternalRelationshipName); member = Expression.Property(relation, filter.Attribute.PropertyInfo.Name); } else { member = Expression.Property(entity, filter.Attribute.PropertyInfo.Name); } var method = ContainsMethod.MakeGenericMethod(member.Type); var obj = TypeHelper.ConvertListType(propertyValues, member.Type); if (filter.Operation == FilterOperation.@in) { // Where(i => arr.Contains(i.column)) var contains = Expression.Call(method, new Expression[] { Expression.Constant(obj), member }); var lambda = Expression.Lambda <Func <TSource, bool> >(contains, entity); return(source.Where(lambda)); } else { // Where(i => !arr.Contains(i.column)) var notContains = Expression.Not(Expression.Call(method, new Expression[] { Expression.Constant(obj), member })); var lambda = Expression.Lambda <Func <TSource, bool> >(notContains, entity); return(source.Where(lambda)); } } catch (FormatException) { throw new JsonApiException(400, $"Could not cast {filter.Value} to {property.PropertyType.Name}"); } }
private static IQueryable <TSource> CallGenericWhereContainsMethod <TSource>(IQueryable <TSource> source, FilterQueryContext filter) { var concreteType = typeof(TSource); var property = concreteType.GetProperty(filter.Attribute.PropertyInfo.Name); var propertyValues = filter.Value.Split(QueryConstants.COMMA); ParameterExpression entity = Expression.Parameter(concreteType, "entity"); MemberExpression member; if (filter.IsAttributeOfRelationship) { var relation = Expression.PropertyOrField(entity, filter.Relationship.InternalRelationshipName); member = Expression.Property(relation, filter.Attribute.PropertyInfo.Name); } else { member = Expression.Property(entity, filter.Attribute.PropertyInfo.Name); } var method = ContainsMethod.MakeGenericMethod(member.Type); var list = TypeHelper.CreateListFor(member.Type); foreach (var value in propertyValues) { object targetType; try { targetType = TypeHelper.ConvertType(value, member.Type); } catch (FormatException) { throw new InvalidQueryStringParameterException("filter", "Mismatch between query string parameter value and resource attribute type.", $"Failed to convert '{value}' in set '{filter.Value}' to '{property.PropertyType.Name}' for filtering on '{filter.Query.Attribute}' attribute."); } list.Add(targetType); } if (filter.Operation == FilterOperation.@in) { // Where(i => arr.Contains(i.column)) var contains = Expression.Call(method, new Expression[] { Expression.Constant(list), member }); var lambda = Expression.Lambda <Func <TSource, bool> >(contains, entity); return(source.Where(lambda)); } else { // Where(i => !arr.Contains(i.column)) var notContains = Expression.Not(Expression.Call(method, new Expression[] { Expression.Constant(list), member })); var lambda = Expression.Lambda <Func <TSource, bool> >(notContains, entity); return(source.Where(lambda)); } }
/// <inheritdoc /> public virtual IQueryable <TResource> Filter(IQueryable <TResource> entities, FilterQueryContext filterQueryContext) { _logger.LogTrace($"Entering {nameof(Filter)}({nameof(entities)}, {nameof(filterQueryContext)})."); if (filterQueryContext.IsCustom) { var query = (Func <IQueryable <TResource>, FilterQuery, IQueryable <TResource> >)filterQueryContext.CustomQuery; return(query(entities, filterQueryContext.Query)); } return(entities.Filter(filterQueryContext)); }
/// <summary> /// This calls a generic where method.. more explaining to follow /// /// </summary> /// <typeparam name="TSource"></typeparam> /// <param name="source"></param> /// <param name="filter"></param> /// <returns></returns> private static IQueryable <TSource> CallGenericWhereMethod <TSource>(IQueryable <TSource> source, FilterQueryContext filter) { var op = filter.Operation; var concreteType = typeof(TSource); PropertyInfo property; MemberExpression left; // {model} var parameter = Expression.Parameter(concreteType, "model"); // Is relationship attribute if (filter.IsAttributeOfRelationship) { var relationProperty = concreteType.GetProperty(filter.Relationship.PropertyInfo.Name); if (relationProperty == null) { throw new ArgumentException($"'{filter.Relationship.PropertyInfo.Name}' is not a valid relationship of '{concreteType}'"); } var relatedType = filter.Relationship.RightType; property = relatedType.GetProperty(filter.Attribute.PropertyInfo.Name); if (property == null) { throw new ArgumentException($"'{filter.Attribute.PropertyInfo.Name}' is not a valid attribute of '{filter.Relationship.PropertyInfo.Name}'"); } var leftRelationship = Expression.PropertyOrField(parameter, filter.Relationship.PropertyInfo.Name); // {model.Relationship} left = Expression.PropertyOrField(leftRelationship, property.Name); } // Is standalone attribute else { property = concreteType.GetProperty(filter.Attribute.PropertyInfo.Name); if (property == null) { throw new ArgumentException($"'{filter.Attribute.PropertyInfo.Name}' is not a valid property of '{concreteType}'"); } // {model.Id} left = Expression.PropertyOrField(parameter, property.Name); } Expression right; if (op == FilterOperation.isnotnull || op == FilterOperation.isnull) { right = Expression.Constant(null); } else { // convert the incoming value to the target value type // "1" -> 1 object convertedValue; try { convertedValue = TypeHelper.ConvertType(filter.Value, property.PropertyType); } catch (FormatException) { throw new InvalidQueryStringParameterException("filter", "Mismatch between query string parameter value and resource attribute type.", $"Failed to convert '{filter.Value}' to '{property.PropertyType.Name}' for filtering on '{filter.Query.Attribute}' attribute."); } right = CreateTupleAccessForConstantExpression(convertedValue, property.PropertyType); } var body = GetFilterExpressionLambda(left, right, filter.Operation); var lambda = Expression.Lambda <Func <TSource, bool> >(body, parameter); return(source.Where(lambda)); }