mirror of
https://github.com/zoriya/EntityFrameworkCore.Projectables.git
synced 2026-05-23 03:06:30 +00:00
Some additional tests and a bit of refactoring
This commit is contained in:
+1
-1
@@ -13,7 +13,7 @@ namespace EntityFrameworkCore.Projections.Infrastructure.Internal
|
||||
{
|
||||
readonly IQueryTranslationPreprocessorFactory _decoratedFactory;
|
||||
readonly QueryTranslationPreprocessorDependencies _queryTranslationPreprocessorDependencies;
|
||||
readonly ProjectableExpressionReplacer _projectableExpressionReplacer = new();
|
||||
readonly ProjectableExpressionReplacer _projectableExpressionReplacer = new(new ProjectionExpressionResolver());
|
||||
|
||||
public CustomQueryTranslationPreprocessorFactory(IQueryTranslationPreprocessorFactory decoratedFactory, QueryTranslationPreprocessorDependencies queryTranslationPreprocessorDependencies)
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace EntityFrameworkCore.Projections.Services
|
||||
{
|
||||
public class ExpressionArgumentReplacer : ExpressionVisitor
|
||||
public sealed class ExpressionArgumentReplacer : ExpressionVisitor
|
||||
{
|
||||
readonly IEnumerable<(ParameterExpression parameter, Expression argument)>? _parameterArgumentMapping;
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace EntityFrameworkCore.Projections.Services
|
||||
{
|
||||
public interface IProjectionExpressionResolver
|
||||
{
|
||||
LambdaExpression FindGeneratedExpression(MemberInfo projectableMemberInfo);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EntityFrameworkCore.Projections.Services
|
||||
{
|
||||
public sealed class ParameterExtractor : ExpressionVisitor
|
||||
{
|
||||
List<ParameterExpression>? _extractedParameters = null;
|
||||
|
||||
protected override Expression VisitParameter(ParameterExpression node)
|
||||
{
|
||||
if (_extractedParameters is null)
|
||||
{
|
||||
_extractedParameters = new List<ParameterExpression>();
|
||||
}
|
||||
|
||||
_extractedParameters.Add(node);
|
||||
return base.VisitParameter(node);
|
||||
}
|
||||
|
||||
public IEnumerable<ParameterExpression> ExtractedParameters
|
||||
=> _extractedParameters ?? Enumerable.Empty<ParameterExpression>();
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,18 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace EntityFrameworkCore.Projections.Services
|
||||
{
|
||||
public class ProjectableExpressionReplacer : ExpressionVisitor
|
||||
public sealed class ProjectableExpressionReplacer : ExpressionVisitor
|
||||
{
|
||||
readonly ProjectionExpressionResolver _resolver = new();
|
||||
readonly IProjectionExpressionResolver _resolver;
|
||||
|
||||
public ProjectableExpressionReplacer(IProjectionExpressionResolver projectionExpressionResolver)
|
||||
{
|
||||
_resolver = projectionExpressionResolver;
|
||||
}
|
||||
|
||||
protected override Expression VisitMethodCall(MethodCallExpression node)
|
||||
{
|
||||
if (node.Method.GetCustomAttributes(true).OfType<ProjectableAttribute>().Any())
|
||||
if (node.Method.GetCustomAttributes(false).OfType<ProjectableAttribute>().Any())
|
||||
{
|
||||
var reflectedExpression = _resolver.FindGeneratedExpression(node.Method);
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ using EntityFrameworkCore.Projections.Extensions;
|
||||
|
||||
namespace EntityFrameworkCore.Projections.Services
|
||||
{
|
||||
public sealed class ProjectionExpressionResolver
|
||||
public sealed class ProjectionExpressionResolver : IProjectionExpressionResolver
|
||||
{
|
||||
readonly ConcurrentDictionary<string, LambdaExpression> _lookupCache = new();
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace EntityFrameworkCore.Projections.Services
|
||||
}
|
||||
|
||||
return expressionFactoryMethod.Invoke(null, null) as LambdaExpression ?? throw new InvalidOperationException("Expected lambda");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using EntityFrameworkCore.Projections.Services;
|
||||
using Xunit;
|
||||
|
||||
namespace EntityFrameworkCore.Projections.Tests.Services
|
||||
{
|
||||
public class ExpressionArgumentReplacerTests
|
||||
{
|
||||
[Fact]
|
||||
public void VisitParameter_MapsParametersWithArguments()
|
||||
{
|
||||
var parameter = Expression.Parameter(typeof(int));
|
||||
var argument = Expression.Constant(1);
|
||||
var subject = new ExpressionArgumentReplacer(new[] { (parameter, (Expression)argument) });
|
||||
|
||||
var result = subject.Visit(parameter);
|
||||
|
||||
Assert.Equal(argument, result);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+152
@@ -2,12 +2,164 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using EntityFrameworkCore.Projections.Services;
|
||||
using Xunit;
|
||||
|
||||
namespace EntityFrameworkCore.Projections.Tests.Services
|
||||
{
|
||||
public class ProjectableExpressionReplacerTests
|
||||
{
|
||||
public class ProjectableExpressionResolverStub : IProjectionExpressionResolver
|
||||
{
|
||||
readonly Func<MemberInfo, LambdaExpression> _implementation;
|
||||
|
||||
public ProjectableExpressionResolverStub(Func<MemberInfo, LambdaExpression> implementation)
|
||||
{
|
||||
_implementation = implementation;
|
||||
}
|
||||
|
||||
public LambdaExpression FindGeneratedExpression(MemberInfo projectableMemberInfo) => _implementation(projectableMemberInfo);
|
||||
}
|
||||
|
||||
public class Entity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
[Projectable]
|
||||
public int SimpleProperty => 0;
|
||||
|
||||
[Projectable]
|
||||
public int SimpleMethod() => 0;
|
||||
|
||||
[Projectable]
|
||||
public int SimpleMethodWithArguments(int a, object b) => 0;
|
||||
|
||||
[Projectable]
|
||||
public int SimpleStatefullProperty => Id;
|
||||
|
||||
[Projectable]
|
||||
public int SimpleStatefullMethod() => Id;
|
||||
|
||||
[Projectable]
|
||||
public static int SimpleStaticMethod() => 0;
|
||||
|
||||
[Projectable]
|
||||
public static int SimpleStaticMethodWithArguments(int a, Entity b) => 0;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VisitMember_SimpleProperty()
|
||||
{
|
||||
Expression<Func<Entity, int>> input = x => x.SimpleProperty;
|
||||
Expression<Func<Entity, int>> expected = x => 0;
|
||||
|
||||
var resolver = new ProjectableExpressionResolverStub(
|
||||
x => expected
|
||||
);
|
||||
var subject = new ProjectableExpressionReplacer(resolver);
|
||||
|
||||
var actual = subject.Visit(input);
|
||||
|
||||
Assert.Equal(expected.ToString(), actual.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VisitMember_SimpleMethod()
|
||||
{
|
||||
Expression<Func<Entity, int>> input = x => x.SimpleMethod();
|
||||
Expression<Func<Entity, int>> expected = x => 0;
|
||||
|
||||
var resolver = new ProjectableExpressionResolverStub(
|
||||
x => expected
|
||||
);
|
||||
var subject = new ProjectableExpressionReplacer(resolver);
|
||||
|
||||
var actual = subject.Visit(input);
|
||||
|
||||
Assert.Equal(expected.ToString(), actual.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VisitMember_SimpleMethodWithArguments()
|
||||
{
|
||||
Expression<Func<Entity, int>> input = x => x.SimpleMethodWithArguments(1, true);
|
||||
Expression<Func<Entity, int>> expected = x => 0;
|
||||
|
||||
var resolver = new ProjectableExpressionResolverStub(
|
||||
x => expected
|
||||
);
|
||||
var subject = new ProjectableExpressionReplacer(resolver);
|
||||
|
||||
var actual = subject.Visit(input);
|
||||
|
||||
Assert.Equal(expected.ToString(), actual.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VisitMember_SimpleStatefullProperty()
|
||||
{
|
||||
Expression<Func<Entity, int>> input = x => x.SimpleStatefullProperty;
|
||||
Expression<Func<Entity, int>> expected = x => x.Id;
|
||||
|
||||
var resolver = new ProjectableExpressionResolverStub(
|
||||
x => expected
|
||||
);
|
||||
var subject = new ProjectableExpressionReplacer(resolver);
|
||||
|
||||
var actual = subject.Visit(input);
|
||||
|
||||
Assert.Equal(expected.ToString(), actual.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VisitMember_SimpleStatefullMethod()
|
||||
{
|
||||
Expression<Func<Entity, int>> input = x => x.SimpleStatefullMethod();
|
||||
Expression<Func<Entity, int>> expected = x => x.Id;
|
||||
|
||||
var resolver = new ProjectableExpressionResolverStub(
|
||||
x => expected
|
||||
);
|
||||
var subject = new ProjectableExpressionReplacer(resolver);
|
||||
|
||||
var actual = subject.Visit(input);
|
||||
|
||||
Assert.Equal(expected.ToString(), actual.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VisitMember_SimpleStaticMethod()
|
||||
{
|
||||
Expression<Func<Entity, int>> input = x => Entity.SimpleStaticMethod();
|
||||
Expression<Func<Entity, int>> expected = x => 0;
|
||||
|
||||
var resolver = new ProjectableExpressionResolverStub(
|
||||
x => expected
|
||||
);
|
||||
var subject = new ProjectableExpressionReplacer(resolver);
|
||||
|
||||
var actual = subject.Visit(input);
|
||||
|
||||
Assert.Equal(expected.ToString(), actual.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VisitMember_SimpleStaticMethodWithArguments()
|
||||
{
|
||||
Expression<Func<Entity, int>> input = x => Entity.SimpleStaticMethodWithArguments(0, x);
|
||||
Expression<Func<Entity, int>> expected = x => 0;
|
||||
|
||||
var resolver = new ProjectableExpressionResolverStub(
|
||||
x => expected
|
||||
);
|
||||
var subject = new ProjectableExpressionReplacer(resolver);
|
||||
|
||||
var actual = subject.Visit(input);
|
||||
|
||||
Assert.Equal(expected.ToString(), actual.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user