/// <summary>Enumerates create enumerable in this collection.</summary>
        /// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
        /// <returns>
        ///     An enumerator that allows foreach to be used to process create enumerable in this collection.
        /// </returns>
        public IEnumerable <T> CreateEnumerable()
        {
            QueryIncludeOptimizedIncludeSubPath.RemoveLazyChild(this);

            // MODIFY query if necessary
#if EF5 || EF6
            var objectContext = OriginalQueryable.GetObjectQuery().Context;
            var keyMembers    = ((dynamic)objectContext).CreateObjectSet <T>().EntitySet.ElementType.KeyMembers;
            var keyNames      = ((IEnumerable <EdmMember>)keyMembers).Select(x => x.Name).ToArray();
#elif EFCORE
            var context = currentQuery.OriginalQueryable.GetDbContext();

            var keyNames = context.Model.FindEntityType(typeof(TResult).DisplayName(true))
                           .GetKeys().ToList()[0]
                           .Properties.Select(x => x.Name).ToArray();
#endif
            var newQuery = OriginalQueryable.AddToRootOrAppendOrderBy(keyNames).Select(x => x);

            foreach (var child in Childs)
            {
                child.CreateIncludeQuery(newQuery);
            }

            // RESOLVE current and all future child queries
            var list = QueryIncludeOptimizedManager.AllowQueryBatch ? newQuery.Future().ToList() : newQuery.ToList();

            // FIX collection null
            QueryIncludeOptimizedNullCollection.NullCollectionToEmpty(list, Childs);

            return(list);
        }
        /// <summary>Enumerates create enumerable in this collection.</summary>
        /// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
        /// <returns>
        ///     An enumerator that allows foreach to be used to process create enumerable in this collection.
        /// </returns>
        public IEnumerable <T> CreateEnumerable()
        {
            //var objectQuery = OriginalQueryable.GetObjectQuery();


            //if (objectQuery.MergeOption == MergeOption.NoTracking)
            //{
            //    objectQuery.MergeOption = MergeOption.AppendOnly;

            //    var newContext = QueryIncludeOptimizedManager.DbContextFactory(objectQuery.Context.GetDbContext()).GetObjectContext();

            //    // CHANGE the context under the objectQuery
            //    {
            //        var internalQueryProperty = OriginalQueryable.GetType().GetProperty("InternalQuery", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            //        var internalQuery = internalQueryProperty.GetValue(OriginalQueryable);
            //        //var internalQueryProperty = typeof(DbQuery).GetProperty("InternalQuery", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            //        //var internalQuery = internalQueryProperty.GetValue(OriginalQueryable, null);

            //        var stateField = typeof(ObjectQuery).GetField("_state", BindingFlags.NonPublic | BindingFlags.Instance);
            //        var state = stateField.GetValue(objectQuery);

            //        var assembly = typeof(ObjectQuery).Assembly;
            //        var objectQueryState = assembly.GetType("System.Data.Entity.Core.Objects.Internal.ObjectQueryState");
            //        var contextField = objectQueryState.GetField("_context", BindingFlags.NonPublic | BindingFlags.Instance);
            //        contextField.SetValue(state, newContext);

            //        var expressionField = state.GetType().GetField("_expression", BindingFlags.NonPublic | BindingFlags.Instance);
            //        var expression = (Expression)expressionField.GetValue(state);

            //        var visitor = new QueryIncludeOptimizedExpressionReduceVisitor2();
            //        expression = visitor.Visit(expression);
            //    }
            //}


            QueryIncludeOptimizedIncludeSubPath.RemoveLazyChild(this);

            // MODIFY query if necessary
#if EF5 || EF6
            var objectContext = OriginalQueryable.GetObjectQuery().Context;

            var keyMembers = objectContext.GetEntitySet <T>().ElementType.KeyMembers;
            var keyNames   = keyMembers.Select(x => x.Name).ToArray();
#elif EFCORE
            var context = currentQuery.OriginalQueryable.GetDbContext();

            var keyNames = context.Model.FindEntityType(typeof(TResult).DisplayName(true))
                           .GetKeys().ToList()[0]
                           .Properties.Select(x => x.Name).ToArray();
#endif
            var newQuery = OriginalQueryable.AddToRootOrAppendOrderBy(keyNames).Select(x => x);

            List <T> list;

            if (QueryIncludeOptimizedManager.AllowQueryBatch)
            {
                var future = newQuery.Future();

                foreach (var child in Childs)
                {
                    child.CreateIncludeQuery(newQuery);
                }

                list = future.ToList();
            }
            else
            {
                list = newQuery.ToList();

                foreach (var child in Childs)
                {
                    child.CreateIncludeQuery(newQuery);
                }
            }

#if EF6
            // FIX lazy loading
            QueryIncludeOptimizedLazyLoading.SetLazyLoaded(list, Childs);
#endif

            // FIX collection null
            QueryIncludeOptimizedNullCollection.NullCollectionToEmpty(list, Childs);

            return(list);
        }
        /// <summary>Executes the given expression.</summary>
        /// <exception cref="Exception">Thrown when an exception error condition occurs.</exception>
        /// <typeparam name="TResult">Type of the result.</typeparam>
        /// <param name="expression">The expression to execute.</param>
        /// <returns>The object returned by the execution of the expression.</returns>
        public TResult Execute <TResult>(Expression expression)
        {
            var methodCall = expression as MethodCallExpression;

            if (methodCall == null)
            {
                throw new Exception(ExceptionMessage.GeneralException);
            }

            if (methodCall.Method.Name == "All" ||
                methodCall.Method.Name == "Any" ||
                methodCall.Method.Name == "Average" ||
                methodCall.Method.Name == "Contains" ||
                methodCall.Method.Name == "Count" ||
                methodCall.Method.Name == "LongCount" ||
                methodCall.Method.Name == "Max" ||
                methodCall.Method.Name == "Min" ||
                methodCall.Method.Name == "SequenceEqual" ||
                methodCall.Method.Name == "Sum")
            {
                return(OriginalProvider.Execute <TResult>(expression));
            }

            QueryIncludeOptimizedIncludeSubPath.RemoveLazyChild(CurrentQueryable);

            var currentQuery  = CurrentQueryable;
            var currentMethod = methodCall.Method.GetGenericMethodDefinition();

            // CHECK if the internal expression can be supported
            var isExpressionSupported = false;

            var firstExpression = methodCall.Arguments.FirstOrDefault(x => x.Type.IsSubclassOf(typeof(Expression)));

            if (firstExpression != null && methodCall.Arguments.Count == 2)
            {
                var quoteExpression  = ((UnaryExpression)firstExpression).Operand;
                var lambdaExpression = quoteExpression as LambdaExpression;
                if (lambdaExpression != null)
                {
                    if (lambdaExpression.Type == typeof(Func <,>).MakeGenericType(currentQuery.ElementType, typeof(bool)))
                    {
                        var method        = typeof(Queryable).GetMethods().First(x => x.Name == "Where" && x.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericArguments().Length == 2);
                        var methodGeneric = method.MakeGenericMethod(currentQuery.ElementType);
                        currentQuery          = (QueryIncludeOptimizedParentQueryable <T>)methodGeneric.Invoke(null, new object[] { currentQuery, lambdaExpression });
                        currentMethod         = typeof(Queryable).GetMethods().FirstOrDefault(x => x.Name == currentMethod.Name && x.GetParameters().Length == 1);
                        isExpressionSupported = currentMethod != null;
                    }
                }
            }

            if (firstExpression != null && !isExpressionSupported)
            {
                throw new Exception(ExceptionMessage.QueryIncludeOptimized_ArgumentExpression);
            }

            // CREATE the new query by selecting included entities in an anonymous type
            var newQuery = currentQuery;

            // REPLACE the first argument with the new query expression
            var arguments = methodCall.Arguments.ToList();

            arguments[0] = newQuery.Expression;

            // REMOVE the last argument if a "Predicate" method was previously used
            if (firstExpression != null)
            {
                arguments.RemoveAt(1);
            }

            // RESOLE parent queries using .FutureValue();
#if EFCORE_3X
            var immediateQuery = new EntityQueryable <TResult>((IAsyncQueryProvider)OriginalProvider, expression);
#elif EF5 || EF6
            var objectQuery = CurrentQueryable.OriginalQueryable.GetObjectQuery();

            // GET provider
            //  because IQueryable.Provider call : ObjectQueryProvider and now that work for EF5
            var objectQueryProviderField = typeof(ObjectQuery).GetProperty("System.Linq.IQueryable.Provider", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
            var provider = (IQueryProvider)objectQueryProviderField.GetValue(objectQuery, null);

            // CREATE query from the expression
            var createQueryMethod = provider.GetType().GetMethod("CreateQuery", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(Expression) }, null);
            createQueryMethod = createQueryMethod.MakeGenericMethod(typeof(TResult));
            var immediateQuery = (ObjectQuery <T>)createQueryMethod.Invoke(provider, new object[] { expression });
#endif

            object value = null;

            if (QueryIncludeOptimizedManager.AllowQueryBatch)
            {
                var futureValue = immediateQuery.FutureValue();

                // RESOLVE child queries using .Future()
                {
                    // MODIFY query if necessary
#if EF5 || EF6
                    var objectContext = CurrentQueryable.OriginalQueryable.GetObjectQuery().Context;
                    var keyMembers    = objectContext.GetEntitySet <T>().ElementType.KeyMembers;
                    var keyNames      = ((IEnumerable <EdmMember>)keyMembers).Select(x => x.Name).ToArray();
#elif EFCORE
                    var context = currentQuery.OriginalQueryable.GetDbContext();

                    var keyNames = context.Model.FindEntityType(typeof(TResult).DisplayName(true))
                                   .GetKeys().ToList()[0]
                                   .Properties.Select(x => x.Name).ToArray();
#endif

                    var currentNewQuery = methodCall.Method.Name == "First" || methodCall.Method.Name == "FirstOrDefault" ?
                                          currentQuery.AddToRootOrAppendOrderBy(keyNames).Take(1) :
                                          methodCall.Method.Name == "Last" || methodCall.Method.Name == "LastOrDefault" ?
                                          currentQuery.AddToRootOrAppendOrderBy(keyNames).Reverse().Take(1) :
                                          currentQuery;

                    currentQuery.CreateQueryable(currentNewQuery);
                }

                value = futureValue.Value;
            }
            else
            {
#if EFCORE_3X
                value = immediateQuery.FutureValue().Value;
#else
                value = immediateQuery.Execute(objectQuery.MergeOption).FirstOrDefault();
#endif

                // RESOLVE child queries using .Future()
                {
                    // MODIFY query if necessary
#if EF5 || EF6
                    var objectContext = CurrentQueryable.OriginalQueryable.GetObjectQuery().Context;
                    var keyMembers    = ((dynamic)objectContext).CreateObjectSet <T>().EntitySet.ElementType.KeyMembers;
                    var keyNames      = ((IEnumerable <EdmMember>)keyMembers).Select(x => x.Name).ToArray();
#elif EFCORE
                    var context = currentQuery.OriginalQueryable.GetDbContext();

                    var keyNames = context.Model.FindEntityType(typeof(TResult).DisplayName(true))
                                   .GetKeys().ToList()[0]
                                   .Properties.Select(x => x.Name).ToArray();
#endif

                    var currentNewQuery = methodCall.Method.Name == "First" || methodCall.Method.Name == "FirstOrDefault" ?
                                          currentQuery.AddToRootOrAppendOrderBy(keyNames).Take(1) :
                                          methodCall.Method.Name == "Last" || methodCall.Method.Name == "LastOrDefault" ?
                                          currentQuery.AddToRootOrAppendOrderBy(keyNames).Reverse().Take(1) :
                                          currentQuery;

                    currentQuery.CreateQueryable(currentNewQuery);
                }
            }


            // EXECUTE the new expression
            //var value = QueryIncludeOptimizedManager.AllowQueryBatch ? immediateQuery.FutureValue().Value : immediateQuery.FirstOrDefault();

            // CHECK if a value has been returned
            if (value == null)
            {
                return((TResult)(object)null);
            }

#if EF6
            // FIX lazy loading
            QueryIncludeOptimizedLazyLoading.SetLazyLoaded(value, CurrentQueryable.Childs);
#endif

            // FIX null collection
            QueryIncludeOptimizedNullCollection.NullCollectionToEmpty(value, CurrentQueryable.Childs);

#if EF5 || EF6
            return((TResult)(object)value);
#elif EFCORE
            return((TResult)value);
#endif
        }
Exemplo n.º 4
0
        public async Task <List <T> > CreateEnumerableAsync(CancellationToken cancellationToken)
        {
            QueryIncludeOptimizedIncludeSubPath.RemoveLazyChild(this);

            // MODIFY query if necessary
#if EF5 || EF6
            var objectContext = OriginalQueryable.GetObjectQuery().Context;

            var keyMembers = objectContext.GetEntitySet <T>().ElementType.KeyMembers;
            var keyNames   = keyMembers.Select(x => x.Name).ToArray();
#elif EFCORE
            DbContext context = null;

            if (OriginalQueryable.IsInMemoryQueryContext())
            {
                context = OriginalQueryable.GetInMemoryContext();
            }
            else
            {
                // MODIFY query if necessary
                context = OriginalQueryable.GetDbContext();
            }

            var keyNames = context.Model.FindEntityType(typeof(T).DisplayName(true))
                           .GetKeys().ToList()[0]
                           .Properties.Select(x => x.Name).ToArray();
#endif
            var newQuery = OriginalQueryable.AddToRootOrAppendOrderBy(keyNames).Select(x => x);

            List <T> list;

            if (QueryIncludeOptimizedManager.AllowQueryBatch)
            {
                var future = newQuery.Future();

                foreach (var child in Childs)
                {
                    child.CreateIncludeQuery(newQuery);
                }

                list = await future.ToListAsync(cancellationToken).ConfigureAwait(false);
            }
            else
            {
                list = await newQuery.ToListAsync(cancellationToken).ConfigureAwait(false);

                foreach (var child in Childs)
                {
                    child.CreateIncludeQuery(newQuery);
                }
            }

#if EF6
            // FIX lazy loading
            QueryIncludeOptimizedLazyLoading.SetLazyLoaded(list, Childs);
#endif

            // FIX collection null
            QueryIncludeOptimizedNullCollection.NullCollectionToEmpty(list, Childs);

            return(list);
        }