private static IQueryable <T> IncludeOptimizedSingle <T, TChild>(this IQueryable <T> query, Expression <Func <T, TChild> > queryIncludeFilter) where T : class where TChild : class { // INCLUDE sub path query = QueryIncludeFilterIncludeSubPath.IncludeSubPath(query, queryIncludeFilter); // GET query root var includeOrderedQueryable = query as QueryIncludeFilterParentQueryable <T> ?? new QueryIncludeFilterParentQueryable <T>(query); // ADD sub query includeOrderedQueryable.Childs.Add(new QueryIncludeFilterChild <T, TChild>(queryIncludeFilter)); // RETURN root return(includeOrderedQueryable); }
/// <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)); } QueryIncludeFilterIncludeSubPath.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 = (QueryIncludeFilterParentQueryable <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.QueryIncludeFilter_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); } #if EFCORE_3X // RESOLE parent queries using .FutureValue(); var immediateQuery = new EntityQueryable <TResult>((IAsyncQueryProvider)OriginalProvider, expression); #else // RESOLE parent queries using .FutureValue(); var immediateQuery = new EntityQueryable <TResult>((IAsyncQueryProvider)OriginalProvider); var expressionProperty = typeof(QueryableBase <>).MakeGenericType(typeof(TResult)).GetProperty("Expression", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); expressionProperty.SetValue(immediateQuery, expression); #endif object value = null; DbContext context = null; if (currentQuery.OriginalQueryable.IsInMemoryQueryContext()) { context = currentQuery.OriginalQueryable.GetInMemoryContext(); } else { // MODIFY query if necessary context = currentQuery.OriginalQueryable.GetDbContext(); } if (QueryIncludeFilterManager.AllowQueryBatch) { var futureValue = immediateQuery.FutureValue(); // RESOLVE child queries using .Future() { // MODIFY query if necessary var keyNames = context.Model.FindEntityType(typeof(TResult).DisplayName(true)) .GetKeys().ToList()[0] .Properties.Select(x => x.Name).ToArray(); 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 { // TODO: Find a better way value = immediateQuery.FutureValue().Value; //value = immediateQuery.Execute(objectQuery.MergeOption).FirstOrDefault(); // RESOLVE child queries using .Future() { // MODIFY query if necessary var keyNames = context.Model.FindEntityType(typeof(TResult).DisplayName(true)) .GetKeys().ToList()[0] .Properties.Select(x => x.Name).ToArray(); 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 = QueryIncludeFilterManager.AllowQueryBatch ? immediateQuery.FutureValue().Value : immediateQuery.FirstOrDefault(); // CHECK if a value has been returned if (value == null) { return((TResult)(object)null); } // FIX null collection QueryIncludeFilterNullCollection.NullCollectionToEmpty(value, CurrentQueryable.Childs); return((TResult)value); }
/// <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 = QueryIncludeFilterManager.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 QueryIncludeFilterExpressionReduceVisitor2(); // expression = visitor.Visit(expression); // } //} QueryIncludeFilterIncludeSubPath.RemoveLazyChild(this); // MODIFY query if necessary var context = OriginalQueryable.GetDbContext(); var keyNames = context.Model.FindEntityType(typeof(T).DisplayName(true)) .GetKeys().ToList()[0] .Properties.Select(x => x.Name).ToArray(); //var newQuery = OriginalQueryable.AddToRootOrAppendOrderBy(keyNames).Select(x => x); var newQuery = OriginalQueryable.AddToRootOrAppendOrderBy(keyNames); List <T> list; if (QueryIncludeFilterManager.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); } } // FIX collection null QueryIncludeFilterNullCollection.NullCollectionToEmpty(list, Childs); return(list); }