Example #1
0
        /// <summary>
        /// Applies a <see cref="ExpressionFilter"/> to filter the output.
        /// </summary>
        public AggregateQuery <T> Filter(ExpressionFilter condition)
        {
            if (_top != null)
            {
                // Programmer mistake
                throw new InvalidOperationException($"Cannot filter the query after {nameof(Top)} has been invoked");
            }

            var clone = Clone();

            if (condition != null)
            {
                if (clone._filter == null)
                {
                    clone._filter = condition;
                }
                else
                {
                    clone._filter = ExpressionFilter.Conjunction(clone._filter, condition);
                }
            }

            return(clone);
        }
Example #2
0
        /// <summary>
        /// Applies a <see cref="ExpressionFilter"/> to filter the result.
        /// </summary>
        public FactQuery <T> Filter(ExpressionFilter condition)
        {
            if (_top != null || _skip != null)
            {
                // Programmer mistake
                throw new InvalidOperationException($"Cannot filter the query again after either {nameof(Skip)} or {nameof(Top)} have been invoked.");
            }

            var clone = Clone();

            if (condition != null)
            {
                if (clone._filter == null)
                {
                    clone._filter = condition;
                }
                else
                {
                    clone._filter = ExpressionFilter.Conjunction(clone._filter, condition);
                }
            }

            return(clone);
        }
Example #3
0
 /// <summary>
 /// A version of <see cref="Filter(ExpressionFilter)"/> that accepts a string.
 /// </summary>
 public FactQuery <T> Filter(string condition)
 {
     return(Filter(ExpressionFilter.Parse(condition)));
 }
Example #4
0
        /// <summary>
        /// Backbone for <see cref="CountAsync(int, QueryContext, CancellationToken)"/>,
        /// <see cref="ToListAsync(QueryContext, CancellationToken)"/> and
        /// <see cref="ToListAndCountAsync(int, QueryContext, CancellationToken)"/>.
        /// </summary>
        private async Task <EntityOutput <T> > ToListAndCountInnerAsync(bool includeResult, bool includeCount, int maxCount, QueryContext ctx, CancellationToken cancellation)
        {
            var queryArgs = await _factory(cancellation);

            var connString = queryArgs.ConnectionString;
            var sources    = queryArgs.Sources;
            var loader     = queryArgs.Loader;

            var userId    = ctx.UserId;
            var userToday = ctx.UserToday;

            var resultDesc = TypeDescriptor.Get <T>();

            _orderby ??= (IsEntityWithKey() ? ExpressionOrderBy.Parse("Id desc") :
                          throw new InvalidOperationException($"Query<{resultDesc.Type.Name}> was executed without an orderby clause"));

            // Prepare all the query parameters
            ExpressionSelect  selectExp  = _select;
            ExpressionExpand  expandExp  = _expand;
            ExpressionOrderBy orderbyExp = _orderby;
            ExpressionFilter  filterExp  = _filter;

            // To prevent SQL injection
            ValidatePathsAndProperties(selectExp, expandExp, filterExp, orderbyExp, resultDesc);

            // ------------------------ Step #1

            // Segment the paths of select and expand along the one-to-many relationships, each one-to-many relationship will
            // result in a new internal query for the child collection with the original query as its principal query
            var segments = new Dictionary <ArraySegment <string>, EntityQueryInternal>(new PathEqualityComparer());

            // Helper method for creating a an internal query, will be used later in both the select and the expand loops
            EntityQueryInternal MakeQueryInternal(ArraySegment <string> previousFullPath, ArraySegment <string> subPath, TypeDescriptor desc)
            {
                EntityQueryInternal   principalQuery = previousFullPath == null ? null : segments[previousFullPath];
                ArraySegment <string> pathToCollectionPropertyInPrincipal = previousFullPath == null ? null : subPath;

                if (principalQuery != null && desc.KeyType == KeyType.None)
                {
                    // Programmer mistake
                    throw new InvalidOperationException($"[Bug] Type {desc.Name} has no Id property, yet it is used as a navigation collection on another entity");
                }

                string foreignKeyToPrincipalQuery = null;
                bool   isAncestorExpand           = false;

                if (principalQuery != null)
                {
                    // This loop retrieves the entity descriptor that has the collection property
                    TypeDescriptor collectionPropertyEntity = principalQuery.ResultDescriptor;
                    int            i = 0;
                    for (; i < pathToCollectionPropertyInPrincipal.Count - 1; i++)
                    {
                        var step = pathToCollectionPropertyInPrincipal[i];
                        collectionPropertyEntity = collectionPropertyEntity.NavigationProperty(step).TypeDescriptor;
                    }

                    // Get the collection/Parent property
                    string propertyName = pathToCollectionPropertyInPrincipal[i];
                    var    property     = collectionPropertyEntity.Property(propertyName);

                    if (property is NavigationPropertyDescriptor navProperty && navProperty.IsParent)
                    {
                        foreignKeyToPrincipalQuery = "ParentId";
                        isAncestorExpand           = true;
                    }
                    else if (property is CollectionPropertyDescriptor collProperty)
                    {
                        // Must be a collection then
                        foreignKeyToPrincipalQuery = collProperty.ForeignKeyName;
                    }
                    else
                    {
                        throw new InvalidOperationException($"Bug: Segment along a property {property.Name} on type {collectionPropertyEntity.Name} That is neither a collection nor a parent");
                    }
                }

                if (isAncestorExpand)
                {
                    // the path to parent entity is the path above minus the "Parent"
                    var pathToParentEntity = new ArraySegment <string>(
                        array: pathToCollectionPropertyInPrincipal.Array,
                        offset: 0,
                        count: pathToCollectionPropertyInPrincipal.Count - 1);

                    // Adding this causes the principal query to always include ParentId in the select clause
                    principalQuery.PathsToParentEntitiesWithExpandedAncestors.Add(pathToParentEntity);
                }

                // This is the orderby of related queries, and the default orderby of the root query
                var defaultOrderBy = ExpressionOrderBy.Parse(
                    desc.HasProperty("Index") ? "Index" :
                    desc.HasProperty("SortKey") ? "SortKey" : "Id");

                // Prepare the flat query and return it
                var flatQuery = new EntityQueryInternal
                {
                    PrincipalQuery   = principalQuery,
                    IsAncestorExpand = isAncestorExpand,
                    PathToCollectionPropertyInPrincipal = pathToCollectionPropertyInPrincipal,
                    ForeignKeyToPrincipalQuery          = foreignKeyToPrincipalQuery,
                    ResultDescriptor = desc,
                    OrderBy          = defaultOrderBy
                };

                return(flatQuery);
            }
Example #5
0
 /// <summary>
 /// A version of <see cref="Filter(ExpressionFilter)"/> that accepts a string.
 /// </summary>
 public EntityQuery <T> Filter(string filter)
 {
     return(Filter(ExpressionFilter.Parse(filter)));
 }
Example #6
0
 public static ExpressionFilter Conjunction(ExpressionFilter filter1, ExpressionFilter filter2)
 {
     var conjunction = new QueryexBinaryOperator("and", filter1.Expression, filter2.Expression);
     return new ExpressionFilter(conjunction);
 }
Example #7
0
 public static ExpressionFilter Disjunction(ExpressionFilter filter1, ExpressionFilter filter2)
 {
     var disjunction = new QueryexBinaryOperator("or", filter1.Expression, filter2.Expression);
     return new ExpressionFilter(disjunction);
 }
Example #8
0
 public FilteredQueryFactory(IQueryFactory baseFactory, string filter)
 {
     _baseFactory = baseFactory;
     _filter      = ExpressionFilter.Parse(filter);
 }