/// <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) { QueryIncludeOptimizedIncludeSubPath.RemoveLazyChild(CurrentQueryable); var methodCall = expression as MethodCallExpression; if (methodCall == null) { throw new Exception(ExceptionMessage.GeneralException); } 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); } // 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); } // RESOLE parent queries using .FutureValue(); #if EF5 || EF6 var objectQuery = CurrentQueryable.OriginalQueryable.GetObjectQuery(); // GET provider var objectQueryProviderField = typeof(ObjectQuery).GetField("_provider", BindingFlags.NonPublic | BindingFlags.Instance); var provider = (IQueryProvider)objectQueryProviderField.GetValue(objectQuery); // 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 }); #elif EFCORE 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; if (QueryIncludeOptimizedManager.AllowQueryBatch) { value = immediateQuery.FutureValue().Value; } else { value = immediateQuery.Execute(objectQuery.MergeOption).FirstOrDefault(); } // 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 }
/// <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); }