This repository was archived by the owner on Jul 12, 2020. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 43
WIP. Adds query interception, resolves #60 #63
Open
da1rren
wants to merge
4
commits into
Elfocrash:develop
Choose a base branch
from
da1rren:develop
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq.Expressions; | ||
|
|
||
| namespace Cosmonaut.Extensions | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| using Cosmonaut.Interception.QueryTranslation; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Linq.Expressions; | ||
|
|
||
| namespace Cosmonaut.Extensions | ||
| { | ||
| public static class IQueryableExtensions | ||
| { | ||
| public static IQueryable<TEntity> ApplyInterception<TEntity>(this IQueryable<TEntity> queryable, IEnumerable<IQueryInterceptor> interceptors) | ||
| { | ||
| interceptors = interceptors?.Where(x => x.Type == typeof(TEntity)); | ||
|
|
||
| if (!interceptors?.Any() ?? true) | ||
| { | ||
| return queryable; | ||
| } | ||
|
|
||
| var visitors = interceptors.Cast<ExpressionVisitor>(); | ||
| return queryable.InterceptWith(visitors.ToArray()); | ||
| } | ||
|
|
||
| public static IQueryable<T> InterceptWith<T>(this IQueryable<T> source, params ExpressionVisitor[] visitors) | ||
| { | ||
| return new QueryTranslator<T>(source, visitors); | ||
| } | ||
| } | ||
| } | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| using System; | ||
| using System.Linq; | ||
| using System.Linq.Expressions; | ||
| using System.Reflection; | ||
|
|
||
| namespace Cosmonaut.Interception | ||
| { | ||
| public class QueryInterceptor<TEntity> : ExpressionVisitor, IQueryInterceptor where TEntity : class | ||
| { | ||
| public Type Type { get; } | ||
|
|
||
| public bool Applied { get; private set; } | ||
|
|
||
| private readonly Expression<Func<TEntity, bool>> _predicate; | ||
| private readonly ConstantExpression _constant; | ||
|
|
||
| public QueryInterceptor(Expression<Func<TEntity, bool>> interceptor) | ||
| { | ||
| if (interceptor == null) | ||
| { | ||
| throw new ArgumentException(nameof(interceptor)); | ||
| } | ||
|
|
||
| Type = typeof(TEntity); | ||
| _predicate = interceptor; | ||
| } | ||
|
|
||
| public override Expression Visit(Expression node) | ||
| { | ||
| if (!(node is ConstantExpression constant)) | ||
| { | ||
| return base.Visit(node); | ||
| } | ||
|
|
||
| if (!(constant.Value is IQueryable<TEntity>)) | ||
| { | ||
| return base.Visit(node); | ||
| } | ||
|
|
||
| var method = GetLinqWhere(); | ||
|
|
||
| Applied = true; | ||
|
|
||
| return Expression.Call(method, constant, _predicate); | ||
| } | ||
|
|
||
| private MethodInfo GetLinqWhere() | ||
| { | ||
| var method = typeof(Queryable).GetRuntimeMethods() | ||
| .Where(x => x.Name == nameof(Queryable.Where)) | ||
| .Select(x => x.MakeGenericMethod(new[] { typeof(TEntity) })) | ||
| .Single(methodInfo => | ||
| { | ||
| var parameters = methodInfo.GetParameters(); | ||
|
|
||
| if (parameters.Count() == 2 | ||
| && parameters[0].ParameterType == typeof(IQueryable<TEntity>) | ||
| && parameters[1].ParameterType == typeof(Expression<Func<TEntity, bool>>)) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| return false; | ||
| }); | ||
|
|
||
| return method; | ||
| } | ||
| } | ||
| } |
77 changes: 77 additions & 0 deletions
77
src/Cosmonaut/Interception/QueryTranslation/QueryTranslator.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| using System; | ||
| using System.Collections; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
| using System.Linq.Expressions; | ||
|
|
||
| namespace Cosmonaut.Interception.QueryTranslation | ||
| { | ||
| internal class QueryTranslator<T> : IOrderedQueryable<T> | ||
| { | ||
| private readonly Expression _expression; | ||
| private readonly QueryTranslatorProviderAsync _provider; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="QueryTranslator{T}"/> class. | ||
| /// </summary> | ||
| /// <param name="source">The source.</param> | ||
| /// <param name="visitors">The visitors.</param> | ||
| public QueryTranslator(IQueryable source, IEnumerable<ExpressionVisitor> visitors) | ||
| { | ||
| _expression = Expression.Constant(this); | ||
| _provider = new QueryTranslatorProviderAsync(source, visitors); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="QueryTranslator{T}"/> class. | ||
| /// </summary> | ||
| /// <param name="source">The source.</param> | ||
| /// <param name="expression">The expression.</param> | ||
| /// <param name="visitors">The visitors.</param> | ||
| public QueryTranslator(IQueryable source, Expression expression, IEnumerable<ExpressionVisitor> visitors) | ||
| { | ||
| _expression = expression; | ||
| _provider = new QueryTranslatorProviderAsync(source, visitors); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns an enumerator that iterates through the collection. | ||
| /// </summary> | ||
| /// <returns> | ||
| /// A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection. | ||
| /// </returns> | ||
| public IEnumerator<T> GetEnumerator() | ||
| { | ||
| return ((IEnumerable<T>)_provider.ExecuteEnumerable(_expression)).GetEnumerator(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Returns an enumerator that iterates through a collection. | ||
| /// </summary> | ||
| /// <returns> | ||
| /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection. | ||
| /// </returns> | ||
| IEnumerator IEnumerable.GetEnumerator() | ||
| { | ||
| return _provider.ExecuteEnumerable(_expression).GetEnumerator(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the type of the element(s) that are returned when the expression tree associated with this instance of <see cref="T:System.Linq.IQueryable" /> is executed. | ||
| /// </summary> | ||
| /// <returns>A <see cref="T:System.Type" /> that represents the type of the element(s) that are returned when the expression tree associated with this object is executed.</returns> | ||
| public Type ElementType => typeof(T); | ||
|
|
||
| /// <summary> | ||
| /// Gets the expression tree that is associated with the instance of <see cref="T:System.Linq.IQueryable" />. | ||
| /// </summary> | ||
| /// <returns>The <see cref="T:System.Linq.Expressions.Expression" /> that is associated with this instance of <see cref="T:System.Linq.IQueryable" />.</returns> | ||
| public Expression Expression => _expression; | ||
|
|
||
| /// <summary> | ||
| /// Gets the query provider that is associated with this data source. | ||
| /// </summary> | ||
| /// <returns>The <see cref="T:System.Linq.IQueryProvider" /> that is associated with this data source.</returns> | ||
| public IQueryProvider Provider => _provider; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.