/// <summary> /// Executes the <see cref="System.Linq.Expressions.Expression"/> and returns the raw result. /// </summary> /// <remarks> /// <see cref="InvalidOperationException"/> get handled for failing /// <see cref="Queryable.Single{TSource}(IQueryable{TSource})"/> and /// <see cref="Queryable.Single{TSource}(IQueryable{TSource}, System.Linq.Expressions.Expression{Func{TSource, bool}})"/>, /// <see cref="Queryable.First{TSource}(IQueryable{TSource})"/>, /// <see cref="Queryable.First{TSource}(IQueryable{TSource}, System.Linq.Expressions.Expression{Func{TSource, bool}})"/>, /// <see cref="Queryable.Last{TSource}(IQueryable{TSource})"/>, /// <see cref="Queryable.Last{TSource}(IQueryable{TSource}, System.Linq.Expressions.Expression{Func{TSource, bool}})"/>. /// Instead of throwing an exception, an array with the length of zero respectively two elements is returned. /// </remarks> /// <param name="expression">The <see cref="System.Linq.Expressions.Expression"/> to be executed.</param> /// <returns>Execution result of the <see cref="System.Linq.Expressions.Expression"/> specified.</returns> protected virtual object?Execute(System.Linq.Expressions.Expression expression) { expression.CheckNotNull(nameof(expression)); try { return(ExecuteCore(expression)); } catch (InvalidOperationException ex) { if (string.Equals(ex.Message, "Sequence contains no elements", StringComparison.Ordinal)) { return(Array.CreateInstance(expression.Type, 0)); } if (string.Equals(ex.Message, "Sequence contains no matching element", StringComparison.Ordinal)) { return(Array.CreateInstance(expression.Type, 0)); } if (string.Equals(ex.Message, "Sequence contains more than one element", StringComparison.Ordinal)) { return(Array.CreateInstance(expression.Type, 2)); } if (string.Equals(ex.Message, "Sequence contains more than one matching element", StringComparison.Ordinal)) { return(Array.CreateInstance(expression.Type, 2)); } throw; } }
public IQueryable CreateQuery(Expression expression) { var elementType = TypeHelper.GetElementType(expression.CheckNotNull(nameof(expression)).Type) ?? throw new RemoteLinqException($"Failed to get element type of {expression.Type}"); return(new RemoteQueryable(elementType, this, expression)); }
/// <summary> /// Executes the <see cref="System.Linq.Expressions.Expression"/> and returns the raw result. /// </summary> /// <remarks> /// <see cref="InvalidOperationException"/> get handled for failing /// <see cref="Queryable.Single{TSource}(IQueryable{TSource})"/> and /// <see cref="Queryable.Single{TSource}(IQueryable{TSource}, System.Linq.Expressions.Expression{Func{TSource, bool}})"/>, /// <see cref="Queryable.First{TSource}(IQueryable{TSource})"/>, /// <see cref="Queryable.First{TSource}(IQueryable{TSource}, System.Linq.Expressions.Expression{Func{TSource, bool}})"/>, /// <see cref="Queryable.Last{TSource}(IQueryable{TSource})"/>, /// <see cref="Queryable.Last{TSource}(IQueryable{TSource}, System.Linq.Expressions.Expression{Func{TSource, bool}})"/>. /// Instead of throwing an exception, an array with the length of zero respectively two elements is returned. /// </remarks> /// <param name="expression">The <see cref="System.Linq.Expressions.Expression"/> to be executed.</param> /// <param name="cancellation">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param> /// <returns>Execution result of the <see cref="System.Linq.Expressions.Expression"/> specified.</returns> protected virtual async Task <object?> ExecuteAsync(System.Linq.Expressions.Expression expression, CancellationToken cancellation) { expression.CheckNotNull(nameof(expression)); try { return(await ExecuteCoreAsync(expression, cancellation).ConfigureAwait(false)); } catch (InvalidOperationException ex) { if (string.Equals(ex.Message, "Sequence contains no elements", StringComparison.Ordinal)) { return(Array.CreateInstance(expression.Type, 0)); } if (string.Equals(ex.Message, "Sequence contains no matching element", StringComparison.Ordinal)) { return(Array.CreateInstance(expression.Type, 0)); } if (string.Equals(ex.Message, "Sequence contains more than one element", StringComparison.Ordinal)) { return(Array.CreateInstance(expression.Type, 2)); } if (string.Equals(ex.Message, "Sequence contains more than one matching element", StringComparison.Ordinal)) { return(Array.CreateInstance(expression.Type, 2)); } throw; } }
internal static Expressions.Expression TranslateExpression(Expression expression, ITypeInfoProvider?typeInfoProvider, Func <Expression, bool>?canBeEvaluatedLocally) { var slinq1 = expression.CheckNotNull(nameof(expression)).SimplifyIncorporationOfRemoteQueryables(); var rlinq1 = slinq1.ToRemoteLinqExpression(typeInfoProvider, canBeEvaluatedLocally); var rlinq2 = rlinq1.ReplaceQueryableByResourceDescriptors(typeInfoProvider); var rlinq3 = rlinq2.ReplaceGenericQueryArgumentsByNonGenericArguments(); return(rlinq3); }
/// <summary> /// Executes the <see cref="System.Linq.Expressions.Expression"/> and returns the raw result. /// </summary> /// <param name="expression">The <see cref="System.Linq.Expressions.Expression"/> to be executed.</param> /// <param name="cancellation">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param> /// <returns>Execution result of the <see cref="System.Linq.Expressions.Expression"/> specified.</returns> protected override async Task <object?> ExecuteCoreAsync(System.Linq.Expressions.Expression expression, CancellationToken cancellation) { cancellation.ThrowIfCancellationRequested(); var queryResult = expression.CheckNotNull(nameof(expression)).CompileAndInvokeExpression(); if (queryResult is Task task) { if (!expression.Type.Implements(typeof(Task <>), out var resultType)) { resultType = task .GetType() .GetGenericArguments() .Where(x => x != typeof(CancellationToken)) .ToArray(); } if (resultType.Length != 1) { throw new RemoteLinqException($"Failed to retrieve the result type for async query result {task.GetType()}"); } try { queryResult = await GetTaskResultAsync(task, resultType[0]).ConfigureAwait(false); } catch (InvalidOperationException ex) { // workarround for issue https://github.com/dotnet/efcore/issues/18742 (relevant for ef core prior version 5.0) if (string.Equals(ex.Message, "Enumerator failed to MoveNextAsync.", StringComparison.Ordinal)) { throw new InvalidOperationException("Sequence contains no elements", ex); } throw; } } if (queryResult is null) { return(null); } cancellation.ThrowIfCancellationRequested(); if (queryResult is IQueryable queryable) { // force query execution task = Helper.ToListAsync(queryable, cancellation); queryResult = await GetTaskResultAsync(task, typeof(List <>).MakeGenericType(queryable.ElementType)).ConfigureAwait(false); } return(queryResult); }
protected override IAsyncEnumerable <object?> ExecuteAsyncStream(System.Linq.Expressions.Expression expression) { if (!expression.CheckNotNull(nameof(expression)).Type.Implements(typeof(IQueryable <>))) { throw new ArgumentException("Expression must be of type IQueryable<>"); } var queryable = (IQueryable)expression.CompileAndInvokeExpression() !; return(queryable.ExecuteAsAsyncStream()); }
/// <summary> /// Executes the <see cref="System.Linq.Expressions.Expression"/> and returns the raw result. /// </summary> /// <param name="expression">The <see cref="System.Linq.Expressions.Expression"/> to be executed.</param> /// <returns>Execution result of the <see cref="System.Linq.Expressions.Expression"/> specified.</returns> protected static object?ExecuteCore(System.Linq.Expressions.Expression expression) { var queryResult = expression.CheckNotNull(nameof(expression)).CompileAndInvokeExpression(); if (queryResult is null) { return(null); } var queryableType = queryResult.GetType(); if (queryableType.Implements(typeof(IQueryable <>))) { // force query execution var elementType = TypeHelper.GetElementType(queryableType); queryResult = MethodInfos.Enumerable.ToArray.MakeGenericMethod(elementType).InvokeAndUnwrap(null, queryResult); } return(queryResult); }
/// <summary> /// Executes the <see cref="System.Linq.Expressions.Expression"/> and returns the raw result. /// </summary> /// <param name="expression">The <see cref="System.Linq.Expressions.Expression"/> to be executed.</param> /// <param name="cancellation">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param> /// <returns>Execution result of the <see cref="System.Linq.Expressions.Expression"/> specified.</returns> protected override async Task <object?> ExecuteCoreAsync(System.Linq.Expressions.Expression expression, CancellationToken cancellation) { cancellation.ThrowIfCancellationRequested(); var queryResult = expression.CheckNotNull(nameof(expression)).CompileAndInvokeExpression(); if (queryResult is Task task) { if (!expression.Type.Implements(typeof(Task <>), out var resultType)) { resultType = task .GetType() .GetGenericArguments() .Where(x => x != typeof(CancellationToken)) .ToArray(); } if (resultType.Length != 1) { throw new RemoteLinqException($"Failed to retrieve the result type for async query result {task.GetType()}"); } queryResult = await GetTaskResultAsync(task, resultType[0]).ConfigureAwait(false); } if (queryResult is null) { return(null); } cancellation.ThrowIfCancellationRequested(); if (queryResult is IQueryable queryable) { // force query execution task = Helper.ToListAsync(queryable, cancellation); queryResult = await GetTaskResultAsync(task, typeof(List <>).MakeGenericType(queryable.ElementType)).ConfigureAwait(false); } return(queryResult); }
/// <summary> /// Checks whether the give <see cref="SystemExpression"/> is assignable to the given <typeparamref name="TResult"/> type in any form, throws an <see cref="ArgumentException"/> otherwise. /// </summary> public static void CheckExpressionResultType <TResult>(SystemExpression expression) { var expressionType = expression.CheckNotNull(nameof(expression)).Type; if (typeof(TResult).IsAssignableFrom(expressionType)) { return; } if (typeof(IRemoteLinqQueryable).IsAssignableFrom(expressionType)) { return; } if (expressionType.Implements(typeof(IQueryable <>), out var typeArgs) && typeArgs.Length == 1 && typeof(TResult).IsAssignableFrom(typeArgs[0])) { return; } throw new ArgumentException("The specified expression is not assignable to the result type.", nameof(expression)); }