diff --git a/Directory.Build.props b/Directory.Build.props
index 5eaf94b6ad6..16fec4c7d58 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -28,11 +28,6 @@
https://docs.microsoft.com/ef/core/
-
-
- $(NoWarn.Replace(';1591', ''))
-
-
$(NoWarn);NU5125
diff --git a/src/EFCore.Cosmos/Extensions/CosmosServiceCollectionExtensions.cs b/src/EFCore.Cosmos/Extensions/CosmosServiceCollectionExtensions.cs
index 64ae4627626..43305780825 100644
--- a/src/EFCore.Cosmos/Extensions/CosmosServiceCollectionExtensions.cs
+++ b/src/EFCore.Cosmos/Extensions/CosmosServiceCollectionExtensions.cs
@@ -8,6 +8,7 @@
using Microsoft.EntityFrameworkCore.Cosmos.Metadata.Conventions.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Query.ExpressionVisitors.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
+using Microsoft.EntityFrameworkCore.Cosmos.Query.Pipeline;
using Microsoft.EntityFrameworkCore.Cosmos.Query.Sql;
using Microsoft.EntityFrameworkCore.Cosmos.Query.Sql.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
@@ -17,6 +18,7 @@
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors;
using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -42,6 +44,12 @@ public static IServiceCollection AddEntityFrameworkCosmos([NotNull] this IServic
.TryAdd()
.TryAdd()
.TryAdd()
+
+ // New Query pipeline
+ .TryAdd()
+ .TryAdd()
+ .TryAdd()
+
.TryAddProviderSpecificServices(
b => b
.TryAddScoped()
diff --git a/src/EFCore.Cosmos/Query/Internal/EntityShaper.cs b/src/EFCore.Cosmos/Query/Internal/EntityShaper.cs
index 49c05afdb65..97204f75724 100644
--- a/src/EFCore.Cosmos/Query/Internal/EntityShaper.cs
+++ b/src/EFCore.Cosmos/Query/Internal/EntityShaper.cs
@@ -117,7 +117,7 @@ var materializationContextParameter
var materializer
= _entityMaterializerSource
.CreateMaterializeExpression(
- firstEntityType, materializationContextParameter);
+ firstEntityType, "instance", materializationContextParameter);
if (concreteEntityTypes.Count == 1)
{
@@ -145,7 +145,8 @@ var blockExpressions
.CreateReadValueExpression(
Expression.Call(materializationContextParameter, MaterializationContext.GetValueBufferMethod),
discriminatorProperty.ClrType,
- indexMap[discriminatorProperty.GetIndex()])),
+ indexMap[discriminatorProperty.GetIndex()],
+ discriminatorProperty)),
Expression.IfThenElse(
Expression.Equal(discriminatorValueVariable, firstDiscriminatorValue),
Expression.Return(returnLabelTarget, materializer),
@@ -196,7 +197,7 @@ var discriminatorValue
materializer
= _entityMaterializerSource
.CreateMaterializeExpression(
- concreteEntityType, materializationContextParameter);
+ concreteEntityType, "instance", materializationContextParameter);
blockExpressions[1]
= Expression.IfThenElse(
@@ -230,7 +231,7 @@ private static object Shape(
{
if (trackingQuery)
{
- var entry = queryContext.StateManager.TryGetEntry(entityInfo.Key, valueBuffer, throwOnNullKey: true);
+ var entry = queryContext.StateManager.TryGetEntry(entityInfo.Key, new object[] { }, throwOnNullKey: true, out var _);
if (entry != null)
{
return ShapeNestedEntities(
diff --git a/src/EFCore.Cosmos/Query/Pipeline/CosmosEntityQueryableTranslatorFactory.cs b/src/EFCore.Cosmos/Query/Pipeline/CosmosEntityQueryableTranslatorFactory.cs
new file mode 100644
index 00000000000..b52481ecd91
--- /dev/null
+++ b/src/EFCore.Cosmos/Query/Pipeline/CosmosEntityQueryableTranslatorFactory.cs
@@ -0,0 +1,53 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+
+namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Pipeline
+{
+ public class CosmosEntityQueryableTranslatorFactory : EntityQueryableTranslatorFactory
+ {
+ private readonly IModel _model;
+
+ public CosmosEntityQueryableTranslatorFactory(IModel model)
+ {
+ _model = model;
+ }
+
+ public override EntityQueryableTranslator Create(QueryCompilationContext2 queryCompilationContext)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public class CosmosQueryableMethodTranslatingExpressionVisitorFactory : IQueryableMethodTranslatingExpressionVisitorFactory
+ {
+ public QueryableMethodTranslatingExpressionVisitor Create(QueryCompilationContext2 queryCompilationContext)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+ public class CosmosShapedQueryCompilingExpressionVisitorFactory : IShapedQueryCompilingExpressionVisitorFactory
+ {
+ private readonly IEntityMaterializerSource _entityMaterializerSource;
+
+ public CosmosShapedQueryCompilingExpressionVisitorFactory(IEntityMaterializerSource entityMaterializerSource)
+ {
+ _entityMaterializerSource = entityMaterializerSource;
+ }
+
+ public ShapedQueryCompilingExpressionVisitor Create(QueryCompilationContext2 queryCompilationContext)
+ {
+ throw new NotImplementedException();
+ //return new CosmosShapedQueryCompilingExpressionVisitor(
+ // _entityMaterializerSource,
+ // queryCompilationContext.TrackQueryResults,
+ // queryCompilationContext.Async);
+ }
+ }
+}
diff --git a/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs b/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs
index b72f7775769..1835972d31f 100644
--- a/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs
+++ b/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Diagnostics;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Internal;
diff --git a/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs
index f817356e79c..60b4e2fef67 100644
--- a/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs
+++ b/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs
@@ -9,11 +9,13 @@
using Microsoft.EntityFrameworkCore.InMemory.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.InMemory.Query.ExpressionVisitors.Internal;
using Microsoft.EntityFrameworkCore.InMemory.Query.Internal;
+using Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline;
using Microsoft.EntityFrameworkCore.InMemory.Storage.Internal;
using Microsoft.EntityFrameworkCore.InMemory.ValueGeneration.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.EntityFrameworkCore.ValueGeneration;
@@ -70,6 +72,13 @@ public static IServiceCollection AddEntityFrameworkInMemoryDatabase([NotNull] th
.TryAdd()
.TryAdd()
.TryAdd()
+
+ // New Query pipeline
+ .TryAdd()
+ .TryAdd()
+ .TryAdd()
+
+
.TryAdd(p => p.GetService())
.TryAdd()
.TryAddProviderSpecificServices(
diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryMaterializerFactory.cs b/src/EFCore.InMemory/Query/Internal/InMemoryMaterializerFactory.cs
index efb32b10af6..6e055b69143 100644
--- a/src/EFCore.InMemory/Query/Internal/InMemoryMaterializerFactory.cs
+++ b/src/EFCore.InMemory/Query/Internal/InMemoryMaterializerFactory.cs
@@ -58,7 +58,7 @@ var concreteEntityTypes
return Expression.Lambda>(
_entityMaterializerSource
.CreateMaterializeExpression(
- concreteEntityTypes[0], materializationContextParameter),
+ concreteEntityTypes[0], "instance", materializationContextParameter),
entityTypeParameter,
materializationContextParameter);
}
@@ -76,7 +76,7 @@ var blockExpressions
returnLabelTarget,
_entityMaterializerSource
.CreateMaterializeExpression(
- concreteEntityTypes[0], materializationContextParameter))),
+ concreteEntityTypes[0], "instance", materializationContextParameter))),
Expression.Label(
returnLabelTarget,
Expression.Default(returnLabelTarget.Type))
@@ -92,7 +92,7 @@ var blockExpressions
Expression.Return(
returnLabelTarget,
_entityMaterializerSource
- .CreateMaterializeExpression(concreteEntityType, materializationContextParameter)),
+ .CreateMaterializeExpression(concreteEntityType, "instance", materializationContextParameter)),
blockExpressions[0]);
}
diff --git a/src/EFCore.InMemory/Query/PipeLine/EntityValuesExpression.cs b/src/EFCore.InMemory/Query/PipeLine/EntityValuesExpression.cs
new file mode 100644
index 00000000000..32213059f11
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/EntityValuesExpression.cs
@@ -0,0 +1,21 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Metadata;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class EntityProjectionExpression : Expression
+ {
+ public EntityProjectionExpression(IEntityType entityType, int startIndex)
+ {
+ EntityType = entityType;
+ StartIndex = startIndex;
+ }
+
+ public IEntityType EntityType { get; }
+ public int StartIndex { get; }
+ }
+
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryEntityQueryableExpressionVisitor2.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryEntityQueryableExpressionVisitor2.cs
new file mode 100644
index 00000000000..d8cb2605c4d
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryEntityQueryableExpressionVisitor2.cs
@@ -0,0 +1,24 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryEntityQueryableExpressionVisitor2 : EntityQueryableExpressionVisitor2
+ {
+ private readonly IModel _model;
+
+ public InMemoryEntityQueryableExpressionVisitor2(IModel model)
+ {
+ _model = model;
+ }
+
+ protected override ShapedQueryExpression CreateShapedQueryExpression(Type elementType)
+ {
+ return new InMemoryShapedQueryExpression(_model.FindEntityType(elementType));
+ }
+ }
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryEntityQueryableExpressionVisitorFactory2.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryEntityQueryableExpressionVisitorFactory2.cs
new file mode 100644
index 00000000000..9ad399898a9
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryEntityQueryableExpressionVisitorFactory2.cs
@@ -0,0 +1,23 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryEntityQueryableTranslatorFactory : EntityQueryableTranslatorFactory
+ {
+ private readonly IModel _model;
+
+ public InMemoryEntityQueryableTranslatorFactory(IModel model)
+ {
+ _model = model;
+ }
+
+ public override EntityQueryableTranslator Create(QueryCompilationContext2 queryCompilationContext)
+ {
+ return new InMemoryEntityQueryableTranslator(_model);
+ }
+ }
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryEntityQueryableExpressionVisitors.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryEntityQueryableExpressionVisitors.cs
new file mode 100644
index 00000000000..16300e0966e
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryEntityQueryableExpressionVisitors.cs
@@ -0,0 +1,25 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryEntityQueryableTranslator : EntityQueryableTranslator
+ {
+ private readonly IModel _model;
+
+ public InMemoryEntityQueryableTranslator(IModel model)
+ {
+ _model = model;
+ }
+
+ public override Expression Visit(Expression query)
+ {
+ return new InMemoryEntityQueryableExpressionVisitor2(_model).Visit(query);
+ }
+ }
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryLinqOperatorProvider.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryLinqOperatorProvider.cs
new file mode 100644
index 00000000000..daeff031995
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryLinqOperatorProvider.cs
@@ -0,0 +1,67 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Microsoft.EntityFrameworkCore.Utilities;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public static class InMemoryLinqOperatorProvider
+ {
+ private static MethodInfo GetMethod(string name, int parameterCount = 0)
+ => GetMethods(name, parameterCount).Single();
+
+ private static IEnumerable GetMethods(string name, int parameterCount = 0)
+ => typeof(Enumerable).GetTypeInfo().GetDeclaredMethods(name)
+ .Where(mi => mi.GetParameters().Length == parameterCount + 1);
+
+ public static MethodInfo Where = GetMethods(nameof(Enumerable.Where), 1)
+ .Single(mi => mi.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2);
+ public static MethodInfo Select = GetMethods(nameof(Enumerable.Select), 1)
+ .Single(mi => mi.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2);
+
+ public static MethodInfo Join = GetMethod(nameof(Enumerable.Join), 4);
+ public static MethodInfo Contains = GetMethod(nameof(Enumerable.Contains), 1);
+
+ public static MethodInfo OrderBy = GetMethod(nameof(Enumerable.OrderBy), 1);
+ public static MethodInfo OrderByDescending = GetMethod(nameof(Enumerable.OrderByDescending), 1);
+ public static MethodInfo ThenBy = GetMethod(nameof(Enumerable.ThenBy), 1);
+ public static MethodInfo ThenByDescending = GetMethod(nameof(Enumerable.ThenByDescending), 1);
+ public static MethodInfo All = GetMethod(nameof(Enumerable.All), 1);
+ public static MethodInfo Any = GetMethod(nameof(Enumerable.Any));
+ public static MethodInfo AnyPredicate = GetMethod(nameof(Enumerable.Any), 1);
+ public static MethodInfo Count = GetMethod(nameof(Enumerable.Count));
+ public static MethodInfo LongCount = GetMethod(nameof(Enumerable.LongCount));
+ public static MethodInfo CountPredicate = GetMethod(nameof(Enumerable.Count), 1);
+ public static MethodInfo LongCountPredicate = GetMethod(nameof(Enumerable.LongCount), 1);
+ public static MethodInfo Distinct = GetMethod(nameof(Enumerable.Distinct));
+ public static MethodInfo Take = GetMethod(nameof(Enumerable.Take), 1);
+ public static MethodInfo Skip = GetMethod(nameof(Enumerable.Skip), 1);
+
+ public static MethodInfo FirstPredicate = GetMethod(nameof(Enumerable.First), 1);
+ public static MethodInfo FirstOrDefaultPredicate = GetMethod(nameof(Enumerable.FirstOrDefault), 1);
+ public static MethodInfo LastPredicate = GetMethod(nameof(Enumerable.Last), 1);
+ public static MethodInfo LastOrDefaultPredicate = GetMethod(nameof(Enumerable.LastOrDefault), 1);
+ public static MethodInfo SinglePredicate = GetMethod(nameof(Enumerable.Single), 1);
+ public static MethodInfo SingleOrDefaultPredicate = GetMethod(nameof(Enumerable.SingleOrDefault), 1);
+
+ public static MethodInfo GetAggregateMethod(string methodName, Type elementType, int parameterCount = 0)
+ {
+ Check.NotEmpty(methodName, nameof(methodName));
+ Check.NotNull(elementType, nameof(elementType));
+
+ var aggregateMethods = GetMethods(methodName, parameterCount).ToList();
+
+ return
+ aggregateMethods
+ .Single(
+ mi => mi.GetParameters().Last().ParameterType.GetGenericArguments().Last() == elementType);
+ //?? aggregateMethods.Single(mi => mi.IsGenericMethod)
+ // .MakeGenericMethod(elementType);
+ }
+ }
+
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryProjectionBindingExpressionVisitor.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryProjectionBindingExpressionVisitor.cs
new file mode 100644
index 00000000000..21f551cae0f
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryProjectionBindingExpressionVisitor.cs
@@ -0,0 +1,136 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Extensions.Internal;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryProjectionBindingExpressionVisitor : ExpressionVisitor
+ {
+ private InMemoryQueryExpression _queryExpression;
+ private readonly IDictionary _projectionMapping
+ = new Dictionary();
+
+ private readonly Stack _projectionMembers = new Stack();
+ private readonly InMemoryExpressionTranslatingExpressionVisitor _expressionTranslatingExpressionVisitor;
+
+ public InMemoryProjectionBindingExpressionVisitor(
+ InMemoryExpressionTranslatingExpressionVisitor expressionTranslatingExpressionVisitor)
+ {
+ _expressionTranslatingExpressionVisitor = expressionTranslatingExpressionVisitor;
+ }
+
+ public Expression Translate(InMemoryQueryExpression queryExpression, Expression expression)
+ {
+ _queryExpression = queryExpression;
+
+ _projectionMembers.Push(new ProjectionMember());
+
+ var result = Visit(expression);
+
+ _queryExpression.ApplyProjection(_projectionMapping);
+
+ _queryExpression = null;
+ _projectionMapping.Clear();
+ _projectionMembers.Clear();
+
+ return result;
+ }
+
+ public override Expression Visit(Expression expression)
+ {
+ if (expression == null)
+ {
+ return null;
+ }
+
+ if (!(expression is NewExpression
+ || expression is MemberInitExpression
+ || expression is EntityShaperExpression))
+ {
+ // This converts object[] from GetDatabaseValues to appropriate projection.
+ if (expression is NewArrayExpression newArrayExpression
+ && newArrayExpression.NodeType == ExpressionType.NewArrayInit
+ && newArrayExpression.Expressions.Count > 0
+ && newArrayExpression.Expressions[0] is UnaryExpression unaryExpression
+ && unaryExpression.NodeType == ExpressionType.Convert
+ && unaryExpression.Type == typeof(object)
+ && unaryExpression.Operand is MethodCallExpression methodCall
+ && methodCall.Method.IsEFPropertyMethod()
+ && methodCall.Arguments[0] is EntityShaperExpression entityShaperExpression
+ && entityShaperExpression.EntityType.GetProperties().Count() == newArrayExpression.Expressions.Count)
+ {
+ _projectionMapping[_projectionMembers.Peek()]
+ = _queryExpression.GetProjectionExpression(
+ entityShaperExpression.ValueBufferExpression.ProjectionMember);
+
+ return new EntityValuesExpression(entityShaperExpression.EntityType, entityShaperExpression.ValueBufferExpression);
+ }
+
+ var translation = _expressionTranslatingExpressionVisitor.Translate(_queryExpression, expression);
+
+ _projectionMapping[_projectionMembers.Peek()] = translation;
+
+ return new ProjectionBindingExpression(_queryExpression, _projectionMembers.Peek(), expression.Type);
+ }
+
+ return base.Visit(expression);
+ }
+
+ protected override Expression VisitExtension(Expression extensionExpression)
+ {
+ if (extensionExpression is EntityShaperExpression entityShaperExpression)
+ {
+ _projectionMapping[_projectionMembers.Peek()]
+ = _queryExpression.GetProjectionExpression(
+ entityShaperExpression.ValueBufferExpression.ProjectionMember);
+
+ return entityShaperExpression.Update(
+ new ProjectionBindingExpression(_queryExpression, _projectionMembers.Peek(), typeof(ValueBuffer)));
+ }
+
+ throw new InvalidOperationException();
+ }
+
+ protected override Expression VisitNew(NewExpression newExpression)
+ {
+ var newArguments = new Expression[newExpression.Arguments.Count];
+ for (var i = 0; i < newExpression.Arguments.Count; i++)
+ {
+ // TODO: Members can be null????
+ var projectionMember = _projectionMembers.Peek().AddMember(newExpression.Members[i]);
+ _projectionMembers.Push(projectionMember);
+
+ newArguments[i] = Visit(newExpression.Arguments[i]);
+ _projectionMembers.Pop();
+ }
+
+ return newExpression.Update(newArguments);
+ }
+
+ protected override Expression VisitMemberInit(MemberInitExpression memberInitExpression)
+ {
+ var newExpression = (NewExpression)Visit(memberInitExpression.NewExpression);
+ var newBindings = new MemberAssignment[memberInitExpression.Bindings.Count];
+ for (var i = 0; i < newBindings.Length; i++)
+ {
+ // TODO: Members can be null????
+ var memberAssignment = (MemberAssignment)memberInitExpression.Bindings[i];
+
+ var projectionMember = _projectionMembers.Peek().AddMember(memberAssignment.Member);
+ _projectionMembers.Push(projectionMember);
+
+ newBindings[i] = memberAssignment.Update(Visit(memberAssignment.Expression));
+ _projectionMembers.Pop();
+ }
+
+ return memberInitExpression.Update(newExpression, newBindings);
+ }
+ }
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryQueryExpression.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryQueryExpression.cs
new file mode 100644
index 00000000000..f7aa8c9353a
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryQueryExpression.cs
@@ -0,0 +1,352 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryQueryExpression : Expression
+ {
+ private sealed class ResultEnumerable : IEnumerable
+ {
+ private readonly Func _getElement;
+
+ public ResultEnumerable(Func getElement)
+ {
+ _getElement = getElement;
+ }
+
+ public IEnumerator GetEnumerator() => new ResultEnumerator(_getElement());
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ private sealed class ResultEnumerator : IEnumerator
+ {
+ private readonly ValueBuffer _value;
+ private bool _moved;
+
+ public ResultEnumerator(ValueBuffer value) => _value = value;
+
+ public bool MoveNext()
+ {
+ if (!_moved)
+ {
+ _moved = true;
+
+ return _moved;
+ }
+
+ return false;
+ }
+
+ public void Reset()
+ {
+ _moved = false;
+ }
+
+ object IEnumerator.Current => Current;
+
+ public ValueBuffer Current => !_moved ? ValueBuffer.Empty : _value;
+
+ void IDisposable.Dispose()
+ {
+ }
+ }
+ }
+
+ public static ParameterExpression ValueBufferParameter = Parameter(typeof(ValueBuffer), "valueBuffer");
+ private static ConstructorInfo _valueBufferConstructor = typeof(ValueBuffer).GetConstructors().Single(ci => ci.GetParameters().Length == 1);
+
+ private List _valueBufferSlots = new List();
+ private IDictionary _projectionMapping = new Dictionary();
+
+ public InMemoryQueryExpression(IEntityType entityType)
+ {
+ ServerQueryExpression = new InMemoryTableExpression(entityType);
+
+ var entityValues = new EntityProjectionExpression(entityType, 0);
+ _projectionMapping[new ProjectionMember()] = entityValues;
+ foreach (var property in entityType.GetProperties())
+ {
+ _valueBufferSlots.Add(CreateReadValueExpression(property.ClrType, property.GetIndex(), property));
+ }
+ }
+
+ public Expression GetSingleScalarProjection()
+ {
+ _valueBufferSlots.Clear();
+ _valueBufferSlots.Add(CreateReadValueExpression(ServerQueryExpression.Type, 0, null));
+
+ _projectionMapping.Clear();
+ _projectionMapping[new ProjectionMember()] = _valueBufferSlots[0];
+
+ return new ProjectionBindingExpression(this, new ProjectionMember(), ServerQueryExpression.Type);
+ }
+
+ public Expression BindProperty(Expression projectionExpression, IProperty property)
+ {
+ var member = (projectionExpression as ProjectionBindingExpression).ProjectionMember;
+
+ var entityValuesExpression = (EntityProjectionExpression)_projectionMapping[member];
+ var offset = entityValuesExpression.StartIndex;
+
+ return _valueBufferSlots[offset + property.GetIndex()];
+ }
+
+ public void ApplyProjection(IDictionary projectionMappings)
+ {
+ _valueBufferSlots.Clear();
+ _projectionMapping.Clear();
+
+ foreach (var kvp in projectionMappings)
+ {
+ var member = kvp.Key;
+ var expression = kvp.Value;
+ var currentIndex = _valueBufferSlots.Count;
+ if (expression is EntityProjectionExpression entityValuesExpression)
+ {
+ foreach (var property in entityValuesExpression.EntityType.GetProperties())
+ {
+ _valueBufferSlots.Add(CreateReadValueExpression(property.ClrType, currentIndex + property.GetIndex(), property));
+ }
+
+ _projectionMapping[member] = new EntityProjectionExpression(entityValuesExpression.EntityType, currentIndex);
+ }
+ else
+ {
+ _valueBufferSlots.Add(expression);
+ _projectionMapping[member] = CreateReadValueExpression(expression.Type, currentIndex, InferPropertyFromInner(expression));
+ }
+ }
+ }
+
+ public Expression GetProjectionExpression(ProjectionMember member)
+ {
+ var projection = _projectionMapping[member];
+ if (projection is EntityProjectionExpression entityValues)
+ {
+ return entityValues;
+ }
+
+ var readValueExpression = (MethodCallExpression)projection;
+ var index = (int)((ConstantExpression)readValueExpression.Arguments[1]).Value;
+
+ return _valueBufferSlots[index];
+ }
+
+ public LambdaExpression GetScalarProjectionLambda()
+ {
+ //Debug.Assert(_valueBufferSlots.Count == 1, "Not a scalar query");
+ if (_valueBufferSlots.Count != 1)
+ {
+ throw new InvalidOperationException();
+ }
+
+ return Lambda(_valueBufferSlots[0], ValueBufferParameter);
+ }
+
+ public void ApplyServerProjection()
+ {
+ if (ServerQueryExpression.Type.TryGetSequenceType() == null)
+ {
+ if (ServerQueryExpression.Type != typeof(ValueBuffer))
+ {
+ ServerQueryExpression = New(
+ typeof(ResultEnumerable).GetConstructors().Single(),
+ Lambda>(
+ New(
+ _valueBufferConstructor,
+ NewArrayInit(
+ typeof(object),
+ new[]
+ {
+ Convert(ServerQueryExpression, typeof(object))
+ }))));
+ }
+ else
+ {
+ ServerQueryExpression = New(
+ typeof(ResultEnumerable).GetConstructors().Single(),
+ Lambda>(ServerQueryExpression));
+ }
+
+ return;
+ }
+
+
+ var newValueBufferSlots = _valueBufferSlots
+ .Select((e, i) => CreateReadValueExpression(
+ e.Type,
+ i,
+ null))
+ .ToList();
+
+ var lambda = Lambda(
+ New(
+ _valueBufferConstructor,
+ NewArrayInit(
+ typeof(object),
+ _valueBufferSlots
+ .Select(e => Convert(e, typeof(object)))
+ .ToArray())),
+ ValueBufferParameter);
+
+ _valueBufferSlots.Clear();
+ _valueBufferSlots.AddRange(newValueBufferSlots);
+
+ ServerQueryExpression = Call(
+ InMemoryLinqOperatorProvider.Select.MakeGenericMethod(typeof(ValueBuffer), typeof(ValueBuffer)),
+ ServerQueryExpression,
+ lambda);
+ }
+
+ public Expression ServerQueryExpression { get; set; }
+ public override Type Type => typeof(IEnumerable);
+ public override ExpressionType NodeType => ExpressionType.Extension;
+
+ private Expression CreateReadValueExpression(
+ Type type,
+ int index,
+ IPropertyBase property)
+ => Call(
+ _tryReadValueMethod.MakeGenericMethod(type),
+ ValueBufferParameter,
+ Constant(index),
+ Constant(property, typeof(IPropertyBase)));
+
+ public void AddInnerJoin(
+ InMemoryQueryExpression queryExpression,
+ LambdaExpression outerKeySelector,
+ LambdaExpression innerKeySelector,
+ Type transparentIdentifierType)
+ {
+ var outerParameter = Parameter(typeof(ValueBuffer), "outer");
+ var innerParameter = Parameter(typeof(ValueBuffer), "inner");
+
+ var valueBufferExpressions = new List();
+ var valueBufferSlots = new List();
+ var projectionMapping = new Dictionary();
+ var replacingVisitor = new ReplacingExpressionVisitor(
+ new Dictionary
+ {
+ { ValueBufferParameter, outerParameter }
+ });
+
+ var index = 0;
+ foreach (var item in _valueBufferSlots)
+ {
+ var updatedItem = replacingVisitor.Visit(item);
+ valueBufferExpressions.Add(updatedItem);
+ valueBufferSlots.Add(
+ CreateReadValueExpression(updatedItem.Type, index++, InferPropertyFromInner(updatedItem)));
+ }
+
+ var outerMemberInfo = transparentIdentifierType.GetTypeInfo().GetDeclaredField("Outer");
+ foreach (var projection in _projectionMapping)
+ {
+ projectionMapping[projection.Key.ShiftMember(outerMemberInfo)] = projection.Value;
+ }
+
+ replacingVisitor = new ReplacingExpressionVisitor(
+ new Dictionary
+ {
+ { ValueBufferParameter, innerParameter }
+ });
+
+ var offset = index;
+ foreach (var item in queryExpression._valueBufferSlots)
+ {
+ var updatedItem = replacingVisitor.Visit(item);
+ valueBufferExpressions.Add(updatedItem);
+ valueBufferSlots.Add(
+ CreateReadValueExpression(updatedItem.Type, index++, InferPropertyFromInner(updatedItem)));
+ }
+
+ var innerMemberInfo = transparentIdentifierType.GetTypeInfo().GetDeclaredField("Inner");
+ foreach (var projection in queryExpression._projectionMapping)
+ {
+ var updatedProjection = projection.Value;
+ if (updatedProjection is EntityProjectionExpression entityValues)
+ {
+ updatedProjection = new EntityProjectionExpression(entityValues.EntityType, entityValues.StartIndex + offset);
+ }
+ projectionMapping[projection.Key.ShiftMember(innerMemberInfo)] = updatedProjection;
+ }
+
+ var resultSelector = Lambda(
+ New(
+ _valueBufferConstructor,
+ NewArrayInit(
+ typeof(object),
+ valueBufferExpressions
+ .Select(e => Convert(e, typeof(object)))
+ .ToArray())),
+ outerParameter,
+ innerParameter);
+
+ ServerQueryExpression = Call(
+ InMemoryLinqOperatorProvider.Join.MakeGenericMethod(
+ typeof(ValueBuffer), typeof(ValueBuffer), outerKeySelector.ReturnType, typeof(ValueBuffer)),
+ ServerQueryExpression,
+ queryExpression.ServerQueryExpression,
+ outerKeySelector,
+ innerKeySelector,
+ resultSelector);
+
+ _valueBufferSlots = valueBufferSlots;
+ _projectionMapping = projectionMapping;
+ }
+
+ private IPropertyBase InferPropertyFromInner(Expression expression)
+ {
+ if (expression is MethodCallExpression methodCallExpression
+ && methodCallExpression.Method.IsGenericMethod
+ && methodCallExpression.Method.GetGenericMethodDefinition() == _tryReadValueMethod)
+ {
+ return (IPropertyBase)((ConstantExpression)methodCallExpression.Arguments[2]).Value;
+ }
+
+ return null;
+ }
+
+ private static readonly MethodInfo _tryReadValueMethod
+ = typeof(InMemoryQueryExpression).GetTypeInfo()
+ .GetDeclaredMethod(nameof(TryReadValue));
+
+
+#pragma warning disable IDE0052 // Remove unread private members
+ private static TValue TryReadValue(
+#pragma warning restore IDE0052 // Remove unread private members
+ in ValueBuffer valueBuffer, int index, IPropertyBase property)
+ {
+ // TODO: For debugging only. Cleanup
+ try
+ {
+ return (TValue)valueBuffer[index];
+ }
+ catch(Exception e)
+ {
+ Console.WriteLine(index);
+ Console.WriteLine(property);
+ Console.WriteLine(typeof(TValue));
+ Console.WriteLine(e);
+
+ throw;
+ }
+
+ }
+
+
+ }
+
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryQueryableMethodTranslatingExpressionVisitor.cs
new file mode 100644
index 00000000000..b81f296ef1b
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryQueryableMethodTranslatingExpressionVisitor.cs
@@ -0,0 +1,417 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryQueryableMethodTranslatingExpressionVisitor : QueryableMethodTranslatingExpressionVisitor
+ {
+ private readonly InMemoryExpressionTranslatingExpressionVisitor _expressionTranslator;
+ private readonly InMemoryProjectionBindingExpressionVisitor _projectionBindingExpressionVisitor;
+
+ public InMemoryQueryableMethodTranslatingExpressionVisitor()
+ {
+ _expressionTranslator = new InMemoryExpressionTranslatingExpressionVisitor();
+ _projectionBindingExpressionVisitor = new InMemoryProjectionBindingExpressionVisitor(_expressionTranslator);
+ }
+
+ protected override ShapedQueryExpression TranslateAll(ShapedQueryExpression source, LambdaExpression predicate)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ inMemoryQueryExpression.ServerQueryExpression =
+ Expression.Call(
+ InMemoryLinqOperatorProvider.All.MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression,
+ TranslateLambdaExpression(source, predicate));
+
+ source.ShaperExpression
+ = Expression.Lambda(
+ inMemoryQueryExpression.GetSingleScalarProjection(),
+ source.ShaperExpression.Parameters);
+
+ return source;
+ }
+
+ protected override ShapedQueryExpression TranslateAny(ShapedQueryExpression source, LambdaExpression predicate)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ inMemoryQueryExpression.ServerQueryExpression = predicate == null
+ ? Expression.Call(
+ InMemoryLinqOperatorProvider.Any.MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression)
+ : Expression.Call(
+ InMemoryLinqOperatorProvider.AnyPredicate.MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression,
+ TranslateLambdaExpression(source, predicate));
+
+ source.ShaperExpression
+ = Expression.Lambda(
+ inMemoryQueryExpression.GetSingleScalarProjection(),
+ source.ShaperExpression.Parameters);
+
+ return source;
+ }
+
+ protected override ShapedQueryExpression TranslateAverage(ShapedQueryExpression source, LambdaExpression selector, Type resultType)
+ => TranslateScalarAggregate(source, selector, nameof(Enumerable.Average));
+
+ protected override ShapedQueryExpression TranslateCast(ShapedQueryExpression source, Type resultType) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateConcat(ShapedQueryExpression source1, ShapedQueryExpression source2) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateContains(ShapedQueryExpression source, Expression item)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ item = TranslateExpression(inMemoryQueryExpression, item);
+
+ inMemoryQueryExpression.ServerQueryExpression =
+ Expression.Call(
+ InMemoryLinqOperatorProvider.Contains.MakeGenericMethod(item.Type),
+ Expression.Call(
+ InMemoryLinqOperatorProvider.Select.MakeGenericMethod(typeof(ValueBuffer), item.Type),
+ inMemoryQueryExpression.ServerQueryExpression,
+ inMemoryQueryExpression.GetScalarProjectionLambda()),
+ item);
+
+ source.ShaperExpression
+ = Expression.Lambda(
+ inMemoryQueryExpression.GetSingleScalarProjection(),
+ source.ShaperExpression.Parameters);
+
+ return source;
+ }
+
+ protected override ShapedQueryExpression TranslateCount(ShapedQueryExpression source, LambdaExpression predicate)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ if (predicate == null)
+ {
+ inMemoryQueryExpression.ServerQueryExpression =
+ Expression.Call(
+ InMemoryLinqOperatorProvider.Count.MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression);
+ }
+ else
+ {
+ inMemoryQueryExpression.ServerQueryExpression =
+ Expression.Call(
+ InMemoryLinqOperatorProvider.CountPredicate.MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression,
+ TranslateLambdaExpression(source, predicate));
+ }
+
+ source.ShaperExpression
+ = Expression.Lambda(
+ inMemoryQueryExpression.GetSingleScalarProjection(),
+ source.ShaperExpression.Parameters);
+
+ return source;
+ }
+
+ protected override ShapedQueryExpression TranslateDefaultIfEmpty(ShapedQueryExpression source, Expression defaultValue) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateDistinct(ShapedQueryExpression source)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ inMemoryQueryExpression.ApplyServerProjection();
+ inMemoryQueryExpression.ServerQueryExpression
+ = Expression.Call(
+ InMemoryLinqOperatorProvider.Distinct.MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression);
+
+ return source;
+ }
+
+ protected override ShapedQueryExpression TranslateElementAtOrDefault(ShapedQueryExpression source, Expression index, bool returnDefault) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateExcept(ShapedQueryExpression source1, ShapedQueryExpression source2) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateFirstOrDefault(ShapedQueryExpression source, LambdaExpression predicate, Type returnType, bool returnDefault)
+ {
+ return TranslateSingleResultOperator(
+ source,
+ predicate,
+ returnType,
+ returnDefault
+ ? InMemoryLinqOperatorProvider.FirstOrDefaultPredicate
+ : InMemoryLinqOperatorProvider.FirstPredicate);
+ }
+
+ protected override ShapedQueryExpression TranslateGroupBy(ShapedQueryExpression source, LambdaExpression keySelector, LambdaExpression elementSelector, LambdaExpression resultSelector) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateGroupJoin(ShapedQueryExpression outer, ShapedQueryExpression inner, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateIntersect(ShapedQueryExpression source1, ShapedQueryExpression source2) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateJoin(ShapedQueryExpression outer, ShapedQueryExpression inner, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector)
+ {
+ outerKeySelector = TranslateLambdaExpression(outer, outerKeySelector);
+ innerKeySelector = TranslateLambdaExpression(inner, innerKeySelector);
+
+ var transparentIdentifierType = CreateTransparentIdentifierType(
+ resultSelector.Parameters[0].Type,
+ resultSelector.Parameters[1].Type);
+
+ ((InMemoryQueryExpression)outer.QueryExpression).AddInnerJoin(
+ (InMemoryQueryExpression)inner.QueryExpression,
+ outerKeySelector,
+ innerKeySelector,
+ transparentIdentifierType);
+
+ return TranslateResultSelectorForJoin(
+ outer,
+ resultSelector,
+ inner.ShaperExpression,
+ transparentIdentifierType,
+ false);
+ }
+
+ protected override ShapedQueryExpression TranslateLastOrDefault(ShapedQueryExpression source, LambdaExpression predicate, Type returnType, bool returnDefault)
+ {
+ return TranslateSingleResultOperator(
+ source,
+ predicate,
+ returnType,
+ returnDefault
+ ? InMemoryLinqOperatorProvider.LastOrDefaultPredicate
+ : InMemoryLinqOperatorProvider.LastPredicate);
+ }
+
+ protected override ShapedQueryExpression TranslateLeftJoin(ShapedQueryExpression outer, ShapedQueryExpression inner, LambdaExpression outerKeySelector, LambdaExpression innerKeySelector, LambdaExpression resultSelector)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected override ShapedQueryExpression TranslateLongCount(ShapedQueryExpression source, LambdaExpression predicate)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ if (predicate == null)
+ {
+ inMemoryQueryExpression.ServerQueryExpression =
+ Expression.Call(
+ InMemoryLinqOperatorProvider.LongCount.MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression);
+ }
+ else
+ {
+ inMemoryQueryExpression.ServerQueryExpression =
+ Expression.Call(
+ InMemoryLinqOperatorProvider.LongCountPredicate.MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression,
+ TranslateLambdaExpression(source, predicate));
+ }
+
+ source.ShaperExpression
+ = Expression.Lambda(
+ inMemoryQueryExpression.GetSingleScalarProjection(),
+ source.ShaperExpression.Parameters);
+
+ return source;
+ }
+
+ protected override ShapedQueryExpression TranslateMax(ShapedQueryExpression source, LambdaExpression selector, Type resultType)
+ => TranslateScalarAggregate(source, selector, nameof(Enumerable.Max));
+
+ protected override ShapedQueryExpression TranslateMin(ShapedQueryExpression source, LambdaExpression selector, Type resultType)
+ => TranslateScalarAggregate(source, selector, nameof(Enumerable.Min));
+
+ protected override ShapedQueryExpression TranslateOfType(ShapedQueryExpression source, Type resultType) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateOrderBy(ShapedQueryExpression source, LambdaExpression keySelector, bool ascending)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ keySelector = TranslateLambdaExpression(source, keySelector);
+
+ inMemoryQueryExpression.ServerQueryExpression
+ = Expression.Call(
+ (ascending ? InMemoryLinqOperatorProvider.OrderBy : InMemoryLinqOperatorProvider.OrderByDescending)
+ .MakeGenericMethod(typeof(ValueBuffer), keySelector.ReturnType),
+ inMemoryQueryExpression.ServerQueryExpression,
+ keySelector);
+
+ return source;
+ }
+
+ protected override ShapedQueryExpression TranslateReverse(ShapedQueryExpression source) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression source, LambdaExpression selector)
+ {
+ if (selector.Body == selector.Parameters[0])
+ {
+ return source;
+ }
+
+ var newSelectorBody = ReplacingExpressionVisitor.Replace(
+ selector.Parameters.Single(), source.ShaperExpression.Body, selector.Body);
+
+ newSelectorBody = _projectionBindingExpressionVisitor
+ .Translate((InMemoryQueryExpression)source.QueryExpression, newSelectorBody);
+
+ source.ShaperExpression = Expression.Lambda(newSelectorBody, source.ShaperExpression.Parameters);
+
+ return source;
+ }
+
+ protected override ShapedQueryExpression TranslateSelectMany(ShapedQueryExpression source, LambdaExpression collectionSelector, LambdaExpression resultSelector) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateSelectMany(ShapedQueryExpression source, LambdaExpression selector) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateSingleOrDefault(ShapedQueryExpression source, LambdaExpression predicate, Type returnType, bool returnDefault)
+ {
+ return TranslateSingleResultOperator(
+ source,
+ predicate,
+ returnType,
+ returnDefault
+ ? InMemoryLinqOperatorProvider.SingleOrDefaultPredicate
+ : InMemoryLinqOperatorProvider.SinglePredicate);
+ }
+
+ protected override ShapedQueryExpression TranslateSkip(ShapedQueryExpression source, Expression count)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ inMemoryQueryExpression.ServerQueryExpression
+ = Expression.Call(
+ InMemoryLinqOperatorProvider.Skip.MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression,
+ TranslateExpression(inMemoryQueryExpression, count));
+
+ return source;
+ }
+
+ protected override ShapedQueryExpression TranslateSkipWhile(ShapedQueryExpression source, LambdaExpression predicate) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateSum(ShapedQueryExpression source, LambdaExpression selector, Type resultType)
+ => TranslateScalarAggregate(source, selector, nameof(Enumerable.Sum));
+
+ protected override ShapedQueryExpression TranslateTake(ShapedQueryExpression source, Expression count)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ inMemoryQueryExpression.ServerQueryExpression
+ = Expression.Call(
+ InMemoryLinqOperatorProvider.Take.MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression,
+ TranslateExpression(inMemoryQueryExpression, count));
+
+ return source;
+ }
+
+ protected override ShapedQueryExpression TranslateTakeWhile(ShapedQueryExpression source, LambdaExpression predicate) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateThenBy(ShapedQueryExpression source, LambdaExpression keySelector, bool ascending)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ keySelector = TranslateLambdaExpression(source, keySelector);
+
+ inMemoryQueryExpression.ServerQueryExpression
+ = Expression.Call(
+ (ascending ? InMemoryLinqOperatorProvider.ThenBy : InMemoryLinqOperatorProvider.ThenByDescending)
+ .MakeGenericMethod(typeof(ValueBuffer), keySelector.ReturnType),
+ inMemoryQueryExpression.ServerQueryExpression,
+ keySelector);
+
+ return source;
+ }
+
+ protected override ShapedQueryExpression TranslateUnion(ShapedQueryExpression source1, ShapedQueryExpression source2) => throw new NotImplementedException();
+
+ protected override ShapedQueryExpression TranslateWhere(ShapedQueryExpression source, LambdaExpression predicate)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ inMemoryQueryExpression.ServerQueryExpression
+ = Expression.Call(
+ InMemoryLinqOperatorProvider.Where
+ .MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression,
+ TranslateLambdaExpression(source, predicate));
+
+ return source;
+ }
+
+ private Expression TranslateExpression(
+ InMemoryQueryExpression inMemoryQueryExpression,
+ Expression expression)
+ {
+ return _expressionTranslator.Translate(inMemoryQueryExpression, expression);
+ }
+
+ private LambdaExpression TranslateLambdaExpression(
+ ShapedQueryExpression shapedQueryExpression, LambdaExpression lambdaExpression)
+ {
+ var lambdaBody = ReplacingExpressionVisitor.Replace(
+ lambdaExpression.Parameters.Single(), shapedQueryExpression.ShaperExpression.Body, lambdaExpression.Body);
+
+ return Expression.Lambda(
+ TranslateExpression((InMemoryQueryExpression)shapedQueryExpression.QueryExpression, lambdaBody),
+ InMemoryQueryExpression.ValueBufferParameter);
+ }
+
+ private ShapedQueryExpression TranslateScalarAggregate(
+ ShapedQueryExpression source, LambdaExpression selector, string methodName)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ selector = selector != null
+ ? TranslateLambdaExpression(source, selector)
+ : inMemoryQueryExpression.GetScalarProjectionLambda();
+
+ inMemoryQueryExpression.ServerQueryExpression
+ = Expression.Call(
+ InMemoryLinqOperatorProvider
+ .GetAggregateMethod(methodName, selector.ReturnType, parameterCount: 1)
+ .MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression,
+ selector);
+
+ source.ShaperExpression
+ = Expression.Lambda(
+ inMemoryQueryExpression.GetSingleScalarProjection(),
+ source.ShaperExpression.Parameters);
+
+ return source;
+ }
+
+ private ShapedQueryExpression TranslateSingleResultOperator(
+ ShapedQueryExpression source, LambdaExpression predicate, Type returnType, MethodInfo method)
+ {
+ var inMemoryQueryExpression = (InMemoryQueryExpression)source.QueryExpression;
+
+ predicate = predicate == null
+ ? Expression.Lambda(Expression.Constant(true), Expression.Parameter(typeof(ValueBuffer)))
+ : TranslateLambdaExpression(source, predicate);
+
+ inMemoryQueryExpression.ServerQueryExpression =
+ Expression.Call(
+ method.MakeGenericMethod(typeof(ValueBuffer)),
+ inMemoryQueryExpression.ServerQueryExpression,
+ predicate);
+
+ if (source.ShaperExpression.ReturnType != returnType)
+ {
+ source.ShaperExpression = Expression.Lambda(
+ Expression.Convert(source.ShaperExpression.Body, returnType),
+ source.ShaperExpression.Parameters);
+ }
+
+ return source;
+ }
+ }
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryQueryableMethodTranslatingExpressionVisitorFactory.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryQueryableMethodTranslatingExpressionVisitorFactory.cs
new file mode 100644
index 00000000000..617b05cafac
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryQueryableMethodTranslatingExpressionVisitorFactory.cs
@@ -0,0 +1,15 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryQueryableMethodTranslatingExpressionVisitorFactory : IQueryableMethodTranslatingExpressionVisitorFactory
+ {
+ public QueryableMethodTranslatingExpressionVisitor Create(QueryCompilationContext2 queryCompilationContext)
+ {
+ return new InMemoryQueryableMethodTranslatingExpressionVisitor();
+ }
+ }
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryShapedQueryExpression.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryShapedQueryExpression.cs
new file mode 100644
index 00000000000..173c8b7030d
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryShapedQueryExpression.cs
@@ -0,0 +1,27 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryShapedQueryExpression : ShapedQueryExpression
+ {
+ public InMemoryShapedQueryExpression(IEntityType entityType)
+ {
+ QueryExpression = new InMemoryQueryExpression(entityType);
+ var resultParameter = Parameter(typeof(InMemoryQueryExpression), "result");
+ ShaperExpression = Lambda(new EntityShaperExpression(
+ entityType,
+ new ProjectionBindingExpression(
+ QueryExpression,
+ new ProjectionMember(),
+ typeof(ValueBuffer)),
+ false),
+ resultParameter);
+ }
+ }
+
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryShapedQueryExpressionVisitor.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryShapedQueryExpressionVisitor.cs
new file mode 100644
index 00000000000..c6022764736
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryShapedQueryExpressionVisitor.cs
@@ -0,0 +1,247 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore.InMemory.Query.Internal;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryShapedQueryCompilingExpressionVisitor : ShapedQueryCompilingExpressionVisitor
+ {
+ public InMemoryShapedQueryCompilingExpressionVisitor(
+ IEntityMaterializerSource entityMaterializerSource, bool trackQueryResults, bool async)
+ : base(entityMaterializerSource, trackQueryResults, async)
+ {
+ }
+
+ protected override Expression VisitExtension(Expression extensionExpression)
+ {
+ switch (extensionExpression)
+ {
+ case InMemoryQueryExpression inMemoryQueryExpression:
+ inMemoryQueryExpression.ApplyServerProjection();
+
+ return Visit(inMemoryQueryExpression.ServerQueryExpression);
+
+ case InMemoryTableExpression inMemoryTableExpression:
+ return Expression.Call(
+ _queryMethodInfo,
+ QueryCompilationContext2.QueryContextParameter,
+ Expression.Constant(inMemoryTableExpression.EntityType));
+ }
+
+ return base.VisitExtension(extensionExpression);
+ }
+
+
+ protected override Expression VisitShapedQueryExpression(ShapedQueryExpression shapedQueryExpression)
+ {
+ var shaperLambda = InjectEntityMaterializer(shapedQueryExpression.ShaperExpression);
+
+ var innerEnumerable = Visit(shapedQueryExpression.QueryExpression);
+
+ var enumeratorParameter = Expression.Parameter(typeof(IEnumerator), "enumerator");
+
+ var newBody = new InMemoryProjectionBindingRemovingExpressionVisitor(
+ (InMemoryQueryExpression)shapedQueryExpression.QueryExpression)
+ .Visit(shaperLambda.Body);
+
+ newBody = ReplacingExpressionVisitor.Replace(
+ InMemoryQueryExpression.ValueBufferParameter,
+ Expression.MakeMemberAccess(enumeratorParameter, _enumeratorCurrent),
+ newBody);
+
+ shaperLambda = Expression.Lambda(
+ newBody,
+ QueryCompilationContext2.QueryContextParameter,
+ enumeratorParameter);
+
+ return Expression.Call(
+ Async
+ ? _shapeAsyncMethodInfo.MakeGenericMethod(shaperLambda.ReturnType.GetGenericArguments().Single())
+ : _shapeMethodInfo.MakeGenericMethod(shaperLambda.ReturnType),
+ innerEnumerable,
+ QueryCompilationContext2.QueryContextParameter,
+ Expression.Constant(shaperLambda.Compile()));
+ }
+
+ private readonly MemberInfo _enumeratorCurrent = typeof(IEnumerator)
+ .GetProperty(nameof(IEnumerator.Current));
+
+
+ private static readonly MethodInfo _queryMethodInfo
+ = typeof(InMemoryShapedQueryCompilingExpressionVisitor).GetTypeInfo()
+ .GetDeclaredMethod(nameof(Query));
+
+ private static IEnumerable Query(
+ QueryContext queryContext,
+ IEntityType entityType)
+ {
+ return ((InMemoryQueryContext)queryContext).Store
+ .GetTables(entityType)
+ .SelectMany(t => t.Rows.Select(vs => new ValueBuffer(vs)));
+ }
+
+ private static readonly MethodInfo _shapeMethodInfo
+ = typeof(InMemoryShapedQueryCompilingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(_Shape));
+
+ private static IEnumerable _Shape(
+ IEnumerable innerEnumerable,
+ QueryContext queryContext,
+ Func, TResult> shaper)
+ {
+ var enumerator = innerEnumerable.GetEnumerator();
+ while (enumerator.MoveNext())
+ {
+ yield return shaper(queryContext, enumerator);
+ }
+ }
+
+ private static readonly MethodInfo _shapeAsyncMethodInfo
+ = typeof(InMemoryShapedQueryCompilingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(_ShapeAsync));
+
+ private static IAsyncEnumerable _ShapeAsync(
+ IEnumerable innerEnumerable,
+ QueryContext queryContext,
+ Func, Task> shaper)
+ {
+ return new AsyncQueryingEnumerable(queryContext, innerEnumerable, shaper);
+ }
+
+ private class AsyncQueryingEnumerable : IAsyncEnumerable
+ {
+ private readonly QueryContext _queryContext;
+ private readonly IEnumerable _innerEnumerable;
+ private readonly Func, Task> _shaper;
+
+ public AsyncQueryingEnumerable(
+ QueryContext queryContext,
+ IEnumerable innerEnumerable,
+ Func, Task> shaper)
+ {
+ _queryContext = queryContext;
+ _innerEnumerable = innerEnumerable;
+ _shaper = shaper;
+ }
+
+ public IAsyncEnumerator GetEnumerator()
+ {
+ return new AsyncEnumerator(this);
+ }
+
+ private sealed class AsyncEnumerator : IAsyncEnumerator
+ {
+ private IEnumerator _enumerator;
+ private readonly QueryContext _queryContext;
+ private readonly IEnumerable _innerEnumerable;
+ private readonly Func, Task> _shaper;
+
+ public AsyncEnumerator(AsyncQueryingEnumerable asyncQueryingEnumerable)
+ {
+ _queryContext = asyncQueryingEnumerable._queryContext;
+ _innerEnumerable = asyncQueryingEnumerable._innerEnumerable;
+ _shaper = asyncQueryingEnumerable._shaper;
+ }
+
+ public T Current { get; private set; }
+
+ public void Dispose()
+ {
+ _enumerator?.Dispose();
+ }
+
+ public async Task MoveNext(CancellationToken cancellationToken)
+ {
+ if (_enumerator == null)
+ {
+ _enumerator = _innerEnumerable.GetEnumerator();
+ }
+
+ var hasNext = _enumerator.MoveNext();
+
+ Current = hasNext
+ ? await _shaper(_queryContext, _enumerator)
+ : default;
+
+ return hasNext;
+ }
+ }
+ }
+
+ private class InMemoryProjectionBindingRemovingExpressionVisitor : ExpressionVisitor
+ {
+ private readonly InMemoryQueryExpression _queryExpression;
+ private readonly IDictionary _materializationContextBindings
+ = new Dictionary();
+
+ public InMemoryProjectionBindingRemovingExpressionVisitor(InMemoryQueryExpression queryExpression)
+ {
+ _queryExpression = queryExpression;
+ }
+
+ protected override Expression VisitBinary(BinaryExpression binaryExpression)
+ {
+ if (binaryExpression.NodeType == ExpressionType.Assign
+ && binaryExpression.Left is ParameterExpression parameterExpression
+ && parameterExpression.Type == typeof(MaterializationContext))
+ {
+ var newExpression = (NewExpression)binaryExpression.Right;
+
+ var innerExpression = Visit(newExpression.Arguments[0]);
+
+ var entityStartIndex = ((EntityProjectionExpression)innerExpression).StartIndex;
+ _materializationContextBindings[parameterExpression] = entityStartIndex;
+
+ var updatedExpression = Expression.New(newExpression.Constructor,
+ Expression.Constant(ValueBuffer.Empty),
+ newExpression.Arguments[1]);
+
+ return Expression.MakeBinary(ExpressionType.Assign, binaryExpression.Left, updatedExpression);
+ }
+
+ return base.VisitBinary(binaryExpression);
+ }
+
+ protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
+ {
+ if (methodCallExpression.Method.IsGenericMethod
+ && methodCallExpression.Method.GetGenericMethodDefinition() == EntityMaterializerSource.TryReadValueMethod)
+ {
+ var originalIndex = (int)((ConstantExpression)methodCallExpression.Arguments[1]).Value;
+ var indexOffset = methodCallExpression.Arguments[0] is ProjectionBindingExpression projectionBindingExpression
+ ? ((EntityProjectionExpression)_queryExpression.GetProjectionExpression(projectionBindingExpression.ProjectionMember)).StartIndex
+ : _materializationContextBindings[(ParameterExpression)((MethodCallExpression)methodCallExpression.Arguments[0]).Object];
+
+ return Expression.Call(
+ methodCallExpression.Method,
+ InMemoryQueryExpression.ValueBufferParameter,
+ Expression.Constant(indexOffset + originalIndex),
+ methodCallExpression.Arguments[2]);
+ }
+
+ return base.VisitMethodCall(methodCallExpression);
+ }
+
+ protected override Expression VisitExtension(Expression extensionExpression)
+ {
+ if (extensionExpression is ProjectionBindingExpression projectionBindingExpression)
+ {
+ return _queryExpression.GetProjectionExpression(projectionBindingExpression.ProjectionMember);
+ }
+
+ return base.VisitExtension(extensionExpression);
+ }
+ }
+ }
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryShapedQueryExpressionVisitorFactory.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryShapedQueryExpressionVisitorFactory.cs
new file mode 100644
index 00000000000..c7e1d257395
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryShapedQueryExpressionVisitorFactory.cs
@@ -0,0 +1,28 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryShapedQueryCompilingExpressionVisitorFactory : IShapedQueryCompilingExpressionVisitorFactory
+ {
+ private readonly IEntityMaterializerSource _entityMaterializerSource;
+
+ public InMemoryShapedQueryCompilingExpressionVisitorFactory(IEntityMaterializerSource entityMaterializerSource)
+ {
+ _entityMaterializerSource = entityMaterializerSource;
+ }
+
+ public ShapedQueryCompilingExpressionVisitor Create(QueryCompilationContext2 queryCompilationContext)
+ {
+ return new InMemoryShapedQueryCompilingExpressionVisitor(
+ _entityMaterializerSource,
+ queryCompilationContext.TrackQueryResults,
+ queryCompilationContext.Async);
+ }
+ }
+
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/InMemoryTableExpression.cs b/src/EFCore.InMemory/Query/PipeLine/InMemoryTableExpression.cs
new file mode 100644
index 00000000000..e0deab371e9
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/InMemoryTableExpression.cs
@@ -0,0 +1,26 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryTableExpression : Expression
+ {
+ public InMemoryTableExpression(IEntityType entityType)
+ {
+ EntityType = entityType;
+ }
+
+ public override Type Type => typeof(IEnumerable);
+
+ public IEntityType EntityType { get; }
+
+ public override ExpressionType NodeType => ExpressionType.Extension;
+ }
+
+}
diff --git a/src/EFCore.InMemory/Query/PipeLine/Translator.cs b/src/EFCore.InMemory/Query/PipeLine/Translator.cs
new file mode 100644
index 00000000000..7427fee4607
--- /dev/null
+++ b/src/EFCore.InMemory/Query/PipeLine/Translator.cs
@@ -0,0 +1,111 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq.Expressions;
+using System.Reflection;
+using Microsoft.EntityFrameworkCore.Extensions.Internal;
+using Microsoft.EntityFrameworkCore.Query;
+using Microsoft.EntityFrameworkCore.Query.Expressions.Internal;
+using Microsoft.EntityFrameworkCore.Query.Internal;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
+
+namespace Microsoft.EntityFrameworkCore.InMemory.Query.Pipeline
+{
+ public class InMemoryExpressionTranslatingExpressionVisitor : ExpressionVisitor
+ {
+ private InMemoryQueryExpression _inMemoryQueryExpression;
+
+ public Expression Translate(InMemoryQueryExpression inMemoryQueryExpression, Expression expression)
+ {
+ _inMemoryQueryExpression = inMemoryQueryExpression;
+
+ try
+ {
+ return Visit(expression);
+ }
+ finally
+ {
+ _inMemoryQueryExpression = null;
+ }
+ }
+
+ protected override Expression VisitMember(MemberExpression memberExpression)
+ {
+ var innerExpression = Visit(memberExpression.Expression);
+ if (innerExpression is EntityShaperExpression entityShaper)
+ {
+ var entityType = entityShaper.EntityType;
+ var property = entityType.FindProperty(memberExpression.Member.GetSimpleMemberName());
+
+ return _inMemoryQueryExpression.BindProperty(entityShaper.ValueBufferExpression, property);
+ }
+
+ return memberExpression.Update(innerExpression);
+ }
+
+ protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
+ {
+ if (methodCallExpression.Method.IsEFPropertyMethod())
+ {
+ var firstArgument = Visit(methodCallExpression.Arguments[0]);
+ if (firstArgument is EntityShaperExpression entityShaper)
+ {
+ var entityType = entityShaper.EntityType;
+ var property = entityType.FindProperty((string)((ConstantExpression)methodCallExpression.Arguments[1]).Value);
+
+ return _inMemoryQueryExpression.BindProperty(entityShaper.ValueBufferExpression, property);
+ }
+ }
+
+ return base.VisitMethodCall(methodCallExpression);
+ }
+
+ protected override Expression VisitExtension(Expression extensionExpression)
+ {
+ if (extensionExpression is EntityShaperExpression)
+ {
+ return extensionExpression;
+ }
+
+ if (extensionExpression is ProjectionBindingExpression projectionBindingExpression)
+ {
+ return _inMemoryQueryExpression.GetProjectionExpression(projectionBindingExpression.ProjectionMember);
+ }
+
+ if (extensionExpression is NullConditionalExpression nullConditionalExpression)
+ {
+ var translation = Visit(nullConditionalExpression.AccessOperation);
+
+ return translation.Type == nullConditionalExpression.Type
+ ? translation
+ : Expression.Convert(translation, nullConditionalExpression.Type);
+ }
+
+ return base.VisitExtension(extensionExpression);
+ }
+
+ protected override Expression VisitParameter(ParameterExpression parameterExpression)
+ {
+ if (parameterExpression.Name.StartsWith(CompiledQueryCache.CompiledQueryParameterPrefix))
+ {
+ return Expression.Call(
+ _getParameterValueMethodInfo.MakeGenericMethod(parameterExpression.Type),
+ QueryCompilationContext2.QueryContextParameter,
+ Expression.Constant(parameterExpression.Name));
+ }
+
+ throw new InvalidOperationException();
+ }
+
+ private static readonly MethodInfo _getParameterValueMethodInfo
+ = typeof(InMemoryExpressionTranslatingExpressionVisitor)
+ .GetTypeInfo().GetDeclaredMethod(nameof(GetParameterValue));
+
+#pragma warning disable IDE0052 // Remove unread private members
+ private static T GetParameterValue(QueryContext queryContext, string parameterName)
+#pragma warning restore IDE0052 // Remove unread private members
+ => (T)queryContext.ParameterValues[parameterName];
+ }
+
+}
diff --git a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
index 99548d1ce99..d5db7a50471 100644
--- a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
+++ b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs
@@ -10,11 +10,12 @@
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Expressions;
-using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors;
using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors.Internal;
using Microsoft.EntityFrameworkCore.Query.Internal;
+using Microsoft.EntityFrameworkCore.Query.Pipeline;
using Microsoft.EntityFrameworkCore.Query.Sql;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using Microsoft.EntityFrameworkCore.Update;
@@ -69,11 +70,8 @@ public static readonly IDictionary RelationalServi
{ typeof(IRelationalValueBufferFactoryFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IMaterializerFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IConditionalRemovingExpressionVisitorFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
- { typeof(IExpressionFragmentTranslator), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(ISqlTranslatingExpressionVisitorFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IUpdateSqlGenerator), new ServiceCharacteristics(ServiceLifetime.Singleton) },
- { typeof(IMemberTranslator), new ServiceCharacteristics(ServiceLifetime.Singleton) },
- { typeof(ICompositeMethodCallTranslator), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IQuerySqlGeneratorFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRelationalTransactionFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IRelationalCommandBuilderFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
@@ -94,8 +92,16 @@ public static readonly IDictionary RelationalServi
{ typeof(IHistoryRepository), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(INamedConnectionStringResolver), new ServiceCharacteristics(ServiceLifetime.Scoped) },
{ typeof(IRelationalTypeMappingSourcePlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
+
+ // New Query Pipeline
+ { typeof(IQuerySqlGeneratorFactory2), new ServiceCharacteristics(ServiceLifetime.Scoped) },
+ { typeof(IRelationalSqlTranslatingExpressionVisitorFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
+ { typeof(IMethodCallTranslatorProvider), new ServiceCharacteristics(ServiceLifetime.Singleton) },
+ { typeof(IMemberTranslatorProvider), new ServiceCharacteristics(ServiceLifetime.Singleton) },
+ { typeof(ISqlExpressionFactory), new ServiceCharacteristics(ServiceLifetime.Singleton) },
{ typeof(IMethodCallTranslatorPlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
- { typeof(IMemberTranslatorPlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) }
+ { typeof(IMemberTranslatorPlugin), new ServiceCharacteristics(ServiceLifetime.Singleton, multipleRegistrations: true) },
+
};
///
@@ -166,22 +172,29 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
TryAdd();
TryAdd();
TryAdd();
- TryAdd();
TryAdd();
TryAdd();
TryAdd();
TryAdd();
TryAdd();
+ // New Query pipeline
+ TryAdd();
+ TryAdd();
+ TryAdd();
+ TryAdd();
+ TryAdd();
+ TryAdd();
+ TryAdd();
+ TryAdd();
+ TryAdd();
+
ServiceCollectionMap.GetInfrastructure()
- .AddDependencySingleton()
.AddDependencySingleton()
.AddDependencySingleton()
- .AddDependencySingleton()
.AddDependencySingleton()
.AddDependencySingleton()
.AddDependencySingleton()
- .AddDependencySingleton()
.AddDependencySingleton()
.AddDependencySingleton()
.AddDependencySingleton()
diff --git a/src/EFCore.Relational/Metadata/Builders/DbFunctionBuilder.cs b/src/EFCore.Relational/Metadata/Builders/DbFunctionBuilder.cs
index a14d1f75f3a..ac2d2189838 100644
--- a/src/EFCore.Relational/Metadata/Builders/DbFunctionBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Builders/DbFunctionBuilder.cs
@@ -4,10 +4,10 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
-using System.Linq.Expressions;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
using Microsoft.EntityFrameworkCore.Utilities;
namespace Microsoft.EntityFrameworkCore.Metadata.Builders
@@ -83,7 +83,7 @@ public virtual DbFunctionBuilder HasSchema([CanBeNull] string schema)
///
/// The translation to use.
/// The same builder instance so that multiple configuration calls can be chained.
- public virtual DbFunctionBuilder HasTranslation([NotNull] Func, Expression> translation)
+ public virtual DbFunctionBuilder HasTranslation([NotNull] Func, SqlExpression> translation)
{
Check.NotNull(translation, nameof(translation));
diff --git a/src/EFCore.Relational/Metadata/IDbFunction.cs b/src/EFCore.Relational/Metadata/IDbFunction.cs
index 4daecfd6fee..7fc8b0ad426 100644
--- a/src/EFCore.Relational/Metadata/IDbFunction.cs
+++ b/src/EFCore.Relational/Metadata/IDbFunction.cs
@@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
-using System.Linq.Expressions;
using System.Reflection;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
namespace Microsoft.EntityFrameworkCore.Metadata
{
@@ -31,6 +31,6 @@ public interface IDbFunction
///
/// A translation callback for performing custom translation of the method call into a SQL expression fragment.
///
- Func, Expression> Translation { get; }
+ Func, SqlExpression> Translation { get; }
}
}
diff --git a/src/EFCore.Relational/Metadata/IMutableDbFunction.cs b/src/EFCore.Relational/Metadata/IMutableDbFunction.cs
index b77d4082dee..289c9626690 100644
--- a/src/EFCore.Relational/Metadata/IMutableDbFunction.cs
+++ b/src/EFCore.Relational/Metadata/IMutableDbFunction.cs
@@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
-using System.Linq.Expressions;
using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
namespace Microsoft.EntityFrameworkCore.Metadata
{
@@ -27,6 +27,6 @@ public interface IMutableDbFunction : IDbFunction
///
/// A translation callback for performing custom translation of the method call into a SQL expression fragment.
///
- new Func, Expression> Translation { get; [param: CanBeNull] set; }
+ new Func, SqlExpression> Translation { get; [param: CanBeNull] set; }
}
}
diff --git a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
index fd69e704eaf..121ac607da4 100644
--- a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
+++ b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
@@ -4,14 +4,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Linq.Expressions;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
-using Microsoft.EntityFrameworkCore.Query.Expressions;
-using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
using Microsoft.EntityFrameworkCore.Utilities;
namespace Microsoft.EntityFrameworkCore.Metadata.Internal
@@ -23,7 +22,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
// Issue#11266 This type is being used by provider code. Do not break.
- public class DbFunction : IMutableDbFunction, IMethodCallTranslator
+ public class DbFunction : IMutableDbFunction
{
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -213,22 +212,6 @@ private void UpdateNameConfigurationSource(ConfigurationSource configurationSour
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public virtual Func, Expression> Translation { get; set; }
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- Expression IMethodCallTranslator.Translate(
- MethodCallExpression methodCallExpression,
- IDiagnosticsLogger logger)
- {
- Check.NotNull(methodCallExpression, nameof(methodCallExpression));
-
- return Translation?.Invoke(methodCallExpression.Arguments)
- ?? new SqlFunctionExpression(FunctionName, MethodInfo.ReturnType, Schema, methodCallExpression.Arguments);
- }
+ public virtual Func, SqlExpression> Translation { get; set; }
}
}
diff --git a/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionBuilder.cs b/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionBuilder.cs
index 7e8f5fadd7a..4dcc98927dc 100644
--- a/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Internal/InternalDbFunctionBuilder.cs
@@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
-using System.Linq.Expressions;
using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
using Microsoft.EntityFrameworkCore.Utilities;
namespace Microsoft.EntityFrameworkCore.Metadata.Internal
@@ -78,7 +78,7 @@ public virtual InternalDbFunctionBuilder HasName([NotNull] string name, Configur
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
- public virtual InternalDbFunctionBuilder HasTranslation([NotNull] Func, Expression> translation)
+ public virtual InternalDbFunctionBuilder HasTranslation([NotNull] Func, SqlExpression> translation)
{
Check.NotNull(translation, nameof(translation));
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
index a19b657d637..698caf25381 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
+++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
@@ -1,6 +1,5 @@
//
-using System;
using System.Reflection;
using System.Resources;
using System.Threading;
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/ICompositeMethodCallTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/ICompositeMethodCallTranslator.cs
deleted file mode 100644
index f0e33ecba84..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/ICompositeMethodCallTranslator.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Linq.Expressions;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- ///
- /// A LINQ expression translator for CLR expressions.
- ///
- ///
- /// The service lifetime is . This means a single instance
- /// is used by many instances. The implementation must be thread-safe.
- /// This service cannot depend on services registered as .
- ///
- ///
- public interface ICompositeMethodCallTranslator
- {
- ///
- /// Translates the given method call expression.
- ///
- /// The method call expression.
- /// The current model.
- /// The logger.
- ///
- /// A SQL expression representing the translated MethodCallExpression.
- ///
- Expression Translate(
- [NotNull] MethodCallExpression methodCallExpression,
- [NotNull] IModel model,
- [NotNull] IDiagnosticsLogger logger);
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/IExpressionFragmentTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/IExpressionFragmentTranslator.cs
deleted file mode 100644
index 2fbc8e86c8f..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/IExpressionFragmentTranslator.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Linq.Expressions;
-using JetBrains.Annotations;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- ///
- /// A LINQ expression translator for arbitrary CLR expression fragments.
- ///
- ///
- /// The service lifetime is . This means a single instance
- /// is used by many instances. The implementation must be thread-safe.
- /// This service cannot depend on services registered as .
- ///
- ///
- public interface IExpressionFragmentTranslator
- {
- ///
- /// Translates the given expression.
- ///
- /// The expression.
- ///
- /// A SQL expression representing the translated expression.
- ///
- Expression Translate([NotNull] Expression expression);
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/IMemberTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/IMemberTranslator.cs
deleted file mode 100644
index 352c27d3ce3..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/IMemberTranslator.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Linq.Expressions;
-using JetBrains.Annotations;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- ///
- /// A LINQ expression translator for CLR expressions.
- ///
- ///
- /// The service lifetime is . This means a single instance
- /// is used by many instances. The implementation must be thread-safe.
- /// This service cannot depend on services registered as .
- ///
- ///
- public interface IMemberTranslator
- {
- ///
- /// Translates the given member expression.
- ///
- /// The member expression.
- ///
- /// A SQL expression representing the translated MemberExpression.
- ///
- Expression Translate([NotNull] MemberExpression memberExpression);
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/IMethodCallTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/IMethodCallTranslator.cs
deleted file mode 100644
index 802c2e59215..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/IMethodCallTranslator.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Linq.Expressions;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- /// A LINQ expression translator for CLR expressions.
- ///
- public interface IMethodCallTranslator
- {
- ///
- /// Translates the given method call expression.
- ///
- /// The method call expression.
- /// The logger to use.
- ///
- /// A SQL expression representing the translated MethodCallExpression.
- ///
- Expression Translate(
- [NotNull] MethodCallExpression methodCallExpression,
- [NotNull] IDiagnosticsLogger logger);
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/ComparisonTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/Internal/ComparisonTranslator.cs
deleted file mode 100644
index 89c4a26eaec..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/ComparisonTranslator.cs
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Collections.Generic;
-using System.Linq.Expressions;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Internal;
-using Microsoft.EntityFrameworkCore.Query.Expressions;
-using Microsoft.EntityFrameworkCore.Query.Expressions.Internal;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators.Internal
-{
- ///
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- ///
- /// The service lifetime is . This means a single instance
- /// is used by many instances. The implementation must be thread-safe.
- /// This service cannot depend on services registered as .
- ///
- ///
- public class ComparisonTranslator : IExpressionFragmentTranslator
- {
- private static readonly Dictionary _operatorMap = new Dictionary
- {
- { ExpressionType.LessThan, ExpressionType.GreaterThan },
- { ExpressionType.LessThanOrEqual, ExpressionType.GreaterThanOrEqual },
- { ExpressionType.GreaterThan, ExpressionType.LessThan },
- { ExpressionType.GreaterThanOrEqual, ExpressionType.LessThanOrEqual },
- { ExpressionType.Equal, ExpressionType.Equal },
- { ExpressionType.NotEqual, ExpressionType.NotEqual }
- };
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual Expression Translate(Expression expression)
- {
- if (expression is BinaryExpression binaryExpression)
- {
- if (!_operatorMap.ContainsKey(expression.NodeType))
- {
- return null;
- }
-
- var leftMethodCall = RemoveNullConditional(binaryExpression.Left) as MethodCallExpression;
- var rightConstant = binaryExpression.Right.RemoveConvert() as ConstantExpression;
- var translated = TranslateInternal(t => t, expression.NodeType, leftMethodCall, rightConstant);
- if (translated != null)
- {
- return translated;
- }
-
- var leftConstant = binaryExpression.Left.RemoveConvert() as ConstantExpression;
- var rightMethodCall = RemoveNullConditional(binaryExpression.Right) as MethodCallExpression;
- var translatedReverse = TranslateInternal(t => _operatorMap[t], expression.NodeType, rightMethodCall, leftConstant);
-
- return translatedReverse;
- }
-
- return null;
- }
-
- private static Expression RemoveNullConditional(Expression expression)
- => expression.RemoveConvert() is NullConditionalExpression nullConditionalExpression
- ? RemoveNullConditional(nullConditionalExpression.AccessOperation)
- : expression;
-
- private static Expression TranslateInternal(
- Func opFunc,
- ExpressionType op,
- MethodCallExpression methodCall,
- ConstantExpression constant)
- {
- if (methodCall != null
- && methodCall.Type == typeof(int)
- && constant != null
- && constant.Type == typeof(int))
- {
- var constantValue = (int)constant.Value;
- Expression left = null, right = null;
-
- if (methodCall.Method.Name == "Compare"
- && methodCall.Arguments.Count == 2
- && methodCall.Arguments[0].Type == methodCall.Arguments[1].Type)
- {
- left = methodCall.Arguments[0];
- right = methodCall.Arguments[1];
- }
- else if (methodCall.Method.Name == "CompareTo"
- && methodCall.Arguments.Count == 1
- && methodCall.Object != null
- && methodCall.Object.Type == methodCall.Arguments[0].Type)
- {
- left = methodCall.Object;
- right = methodCall.Arguments[0];
- }
-
- if (left != null)
- {
- if (constantValue == 0)
- {
- // Compare(strA, strB) > 0 => strA > strB
- return new ComparisonExpression(opFunc(op), left, right);
- }
-
- if (constantValue == 1)
- {
- if (op == ExpressionType.Equal)
- {
- // Compare(strA, strB) == 1 => strA > strB
- return new ComparisonExpression(ExpressionType.GreaterThan, left, right);
- }
-
- if (op == opFunc(ExpressionType.LessThan))
- {
- // Compare(strA, strB) < 1 => strA <= strB
- return new ComparisonExpression(ExpressionType.LessThanOrEqual, left, right);
- }
- }
-
- if (constantValue == -1)
- {
- if (op == ExpressionType.Equal)
- {
- // Compare(strA, strB) == -1 => strA < strB
- return new ComparisonExpression(ExpressionType.LessThan, left, right);
- }
-
- if (op == opFunc(ExpressionType.GreaterThan))
- {
- // Compare(strA, strB) > -1 => strA >= strB
- return new ComparisonExpression(ExpressionType.GreaterThanOrEqual, left, right);
- }
- }
- }
- }
-
- return null;
- }
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/EnumHasFlagTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/Internal/EnumHasFlagTranslator.cs
deleted file mode 100644
index 38a1dcb9319..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/EnumHasFlagTranslator.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Linq.Expressions;
-using System.Reflection;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Internal;
-using Microsoft.EntityFrameworkCore.Utilities;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators.Internal
-{
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public class EnumHasFlagTranslator : IMethodCallTranslator
- {
- private static readonly MethodInfo _methodInfo
- = typeof(Enum).GetRuntimeMethod(nameof(Enum.HasFlag), new[] { typeof(Enum) });
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual Expression Translate(
- MethodCallExpression methodCallExpression,
- IDiagnosticsLogger logger)
- {
- Check.NotNull(methodCallExpression, nameof(methodCallExpression));
-
- if (Equals(methodCallExpression.Method, _methodInfo))
- {
- var argument = methodCallExpression.Arguments[0];
- argument = argument.RemoveConvert();
-
- // ReSharper disable once PossibleNullReferenceException
- var objectEnumType = methodCallExpression.Object.Type.UnwrapNullableType();
- var argumentEnumType = argument.Type.UnwrapNullableType();
-
- if (argument is ConstantExpression constantExpression)
- {
- if (constantExpression.Value == null)
- {
- return null;
- }
-
- argumentEnumType = constantExpression.Value.GetType();
- argument = Expression.Constant(constantExpression.Value, argumentEnumType);
- }
-
- if (objectEnumType != argumentEnumType)
- {
- return null;
- }
-
- var objectType = objectEnumType.UnwrapEnumType();
-
- var convertedObjectExpression = Expression.Convert(methodCallExpression.Object, objectType);
- var convertedArgumentExpression = Expression.Convert(argument, objectType);
-
- return Expression.Equal(
- Expression.And(
- convertedObjectExpression,
- convertedArgumentExpression),
- convertedArgumentExpression);
- }
-
- return null;
- }
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/EqualsTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/Internal/EqualsTranslator.cs
deleted file mode 100644
index 56f2d79c687..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/EqualsTranslator.cs
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Linq.Expressions;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Internal;
-using Microsoft.EntityFrameworkCore.Utilities;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators.Internal
-{
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public class EqualsTranslator : IMethodCallTranslator
- {
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual Expression Translate(
- MethodCallExpression methodCallExpression,
- IDiagnosticsLogger logger)
- {
- Check.NotNull(methodCallExpression, nameof(methodCallExpression));
- Check.NotNull(logger, nameof(logger));
-
- if (methodCallExpression.Method.Name == nameof(object.Equals)
- && methodCallExpression.Arguments.Count == 1
- && methodCallExpression.Object != null)
- {
- var argument = methodCallExpression.Arguments[0];
-
- return methodCallExpression.Method.GetParameters()[0].ParameterType == typeof(object)
- && methodCallExpression.Object.Type != argument.Type
- ? TranslateEquals(methodCallExpression.Object, argument.RemoveConvert(), methodCallExpression, logger)
- : Expression.Equal(methodCallExpression.Object, argument);
- }
-
- if (methodCallExpression.Method.Name == nameof(object.Equals)
- && methodCallExpression.Arguments.Count == 2
- && methodCallExpression.Arguments[0].Type == methodCallExpression.Arguments[1].Type)
- {
- var left = methodCallExpression.Arguments[0].RemoveConvert();
- var right = methodCallExpression.Arguments[1].RemoveConvert();
- return methodCallExpression.Method.GetParameters()[0].ParameterType == typeof(object)
- && left.Type != right.Type
- ? TranslateEquals(left, right, methodCallExpression, logger)
- : Expression.Equal(left, right);
- }
-
- return null;
- }
-
- private Expression TranslateEquals(
- Expression left,
- Expression right,
- MethodCallExpression methodCallExpression,
- IDiagnosticsLogger logger)
- {
- var unwrappedLeftType = left.Type.UnwrapNullableType();
- var unwrappedRightType = right.Type.UnwrapNullableType();
-
- if (unwrappedLeftType == unwrappedRightType)
- {
- return Expression.Equal(
- Expression.Convert(left, unwrappedLeftType),
- Expression.Convert(right, unwrappedRightType));
- }
-
- logger.QueryPossibleUnintendedUseOfEqualsWarning(methodCallExpression);
-
- // Equals(object) always returns false if when comparing objects of different types
- return Expression.Constant(false);
- }
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/GetValueOrDefaultTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/Internal/GetValueOrDefaultTranslator.cs
deleted file mode 100644
index 1693991711e..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/GetValueOrDefaultTranslator.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Linq.Expressions;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Internal;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators.Internal
-{
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public class GetValueOrDefaultTranslator : IMethodCallTranslator
- {
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual Expression Translate(
- MethodCallExpression methodCallExpression,
- IDiagnosticsLogger logger)
- {
- if (methodCallExpression.Method.Name == nameof(Nullable.GetValueOrDefault)
- && methodCallExpression.Type.IsNumeric())
- {
- if (methodCallExpression.Arguments.Count == 0)
- {
- return Expression.Coalesce(
- methodCallExpression.Object,
- methodCallExpression.Type.GenerateDefaultValueConstantExpression());
- }
-
- if (methodCallExpression.Arguments.Count == 1)
- {
- return Expression.Coalesce(
- methodCallExpression.Object,
- methodCallExpression.Arguments[0]);
- }
- }
-
- return null;
- }
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/IsNullOrEmptyTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/Internal/IsNullOrEmptyTranslator.cs
deleted file mode 100644
index 143ae4bdc1e..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/IsNullOrEmptyTranslator.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Linq.Expressions;
-using System.Reflection;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Query.Expressions;
-using Microsoft.EntityFrameworkCore.Utilities;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators.Internal
-{
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public class IsNullOrEmptyTranslator : IMethodCallTranslator
- {
- private static readonly MethodInfo _methodInfo
- = typeof(string).GetRuntimeMethod(nameof(string.IsNullOrEmpty), new[] { typeof(string) });
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual Expression Translate(
- MethodCallExpression methodCallExpression,
- IDiagnosticsLogger logger)
- {
- Check.NotNull(methodCallExpression, nameof(methodCallExpression));
-
- return Equals(methodCallExpression.Method, _methodInfo)
- ? Expression.MakeBinary(
- ExpressionType.OrElse,
- new IsNullExpression(methodCallExpression.Arguments[0]),
- Expression.Equal(methodCallExpression.Arguments[0], Expression.Constant("", typeof(string))))
- : null;
- }
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/LikeTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/Internal/LikeTranslator.cs
deleted file mode 100644
index 3f102ebd2f2..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/LikeTranslator.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Linq.Expressions;
-using System.Reflection;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Query.Expressions;
-using Microsoft.EntityFrameworkCore.Utilities;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators.Internal
-{
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public class LikeTranslator : IMethodCallTranslator
- {
- private static readonly MethodInfo _methodInfo
- = typeof(DbFunctionsExtensions).GetRuntimeMethod(
- nameof(DbFunctionsExtensions.Like),
- new[] { typeof(DbFunctions), typeof(string), typeof(string) });
-
- private static readonly MethodInfo _methodInfoWithEscape
- = typeof(DbFunctionsExtensions).GetRuntimeMethod(
- nameof(DbFunctionsExtensions.Like),
- new[] { typeof(DbFunctions), typeof(string), typeof(string), typeof(string) });
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual Expression Translate(
- MethodCallExpression methodCallExpression,
- IDiagnosticsLogger logger)
- {
- Check.NotNull(methodCallExpression, nameof(methodCallExpression));
-
- return Equals(methodCallExpression.Method, _methodInfo)
- ? new LikeExpression(methodCallExpression.Arguments[1], methodCallExpression.Arguments[2])
- : Equals(methodCallExpression.Method, _methodInfoWithEscape)
- ? new LikeExpression(methodCallExpression.Arguments[1], methodCallExpression.Arguments[2], methodCallExpression.Arguments[3])
- : null;
- }
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/StringConcatTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/Internal/StringConcatTranslator.cs
deleted file mode 100644
index 3e25b33670e..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/Internal/StringConcatTranslator.cs
+++ /dev/null
@@ -1,73 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Internal;
-using Microsoft.EntityFrameworkCore.Query.Expressions;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators.Internal
-{
- ///
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- ///
- /// The service lifetime is . This means a single instance
- /// is used by many instances. The implementation must be thread-safe.
- /// This service cannot depend on services registered as .
- ///
- ///
- public class StringConcatTranslator : IExpressionFragmentTranslator
- {
- private static readonly MethodInfo _stringConcatMethodInfo = typeof(string).GetTypeInfo()
- .GetDeclaredMethods(nameof(string.Concat))
- .Single(
- m => m.GetParameters().Length == 2
- && m.GetParameters()[0].ParameterType == typeof(object)
- && m.GetParameters()[1].ParameterType == typeof(object));
-
- ///
- /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
- /// the same compatibility standards as public APIs. It may be changed or removed without notice in
- /// any release. You should only use it directly in your code with extreme caution and knowing that
- /// doing so can result in application failures when updating to a new Entity Framework Core release.
- ///
- public virtual Expression Translate(Expression expression)
- {
- if (expression is BinaryExpression binaryExpression
- && binaryExpression.NodeType == ExpressionType.Add
- && _stringConcatMethodInfo.Equals(binaryExpression.Method))
- {
- var newLeft = binaryExpression.Left.Type != typeof(string)
- ? new ExplicitCastExpression(HandleNullTypedConstant(binaryExpression.Left.RemoveConvert()), typeof(string))
- : binaryExpression.Left;
-
- var newRight = binaryExpression.Right.Type != typeof(string)
- ? new ExplicitCastExpression(HandleNullTypedConstant(binaryExpression.Right.RemoveConvert()), typeof(string))
- : binaryExpression.Right;
-
- if (newLeft != binaryExpression.Left
- || newRight != binaryExpression.Right)
- {
- return Expression.Add(newLeft, newRight, _stringConcatMethodInfo);
- }
- }
-
- return null;
- }
-
- private static Expression HandleNullTypedConstant(Expression expression)
- => expression is ConstantExpression constantExpression
- && constantExpression.Type == typeof(object)
- && constantExpression.Value != null
- ? Expression.Constant(constantExpression.Value)
- : expression;
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/MultipleOverloadStaticMethodCallTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/MultipleOverloadStaticMethodCallTranslator.cs
deleted file mode 100644
index 35d2dfe70cc..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/MultipleOverloadStaticMethodCallTranslator.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Query.Expressions;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- /// A base LINQ expression translator for CLR expressions that
- /// have multiple overloads.
- ///
- public abstract class MultipleOverloadStaticMethodCallTranslator : IMethodCallTranslator
- {
- private readonly Type _declaringType;
- private readonly string _clrMethodName;
- private readonly string _sqlFunctionName;
-
- ///
- /// Specialized constructor for use only by derived class.
- ///
- /// The declaring type of the method.
- /// Name of the method.
- /// The name of the target SQL function.
- protected MultipleOverloadStaticMethodCallTranslator(
- [NotNull] Type declaringType,
- [NotNull] string clrMethodName,
- [NotNull] string sqlFunctionName)
- {
- _declaringType = declaringType;
- _clrMethodName = clrMethodName;
- _sqlFunctionName = sqlFunctionName;
- }
-
- ///
- /// Translates the given method call expression.
- ///
- /// The method call expression.
- /// The logger.
- ///
- /// A SQL expression representing the translated MethodCallExpression.
- ///
- public virtual Expression Translate(
- MethodCallExpression methodCallExpression,
- IDiagnosticsLogger logger)
- {
- var methodInfos = _declaringType.GetTypeInfo().GetDeclaredMethods(_clrMethodName);
- return methodInfos.Contains(methodCallExpression.Method)
- ? new SqlFunctionExpression(_sqlFunctionName, methodCallExpression.Type, methodCallExpression.Arguments)
- : null;
- }
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/ParameterlessInstanceMethodCallTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/ParameterlessInstanceMethodCallTranslator.cs
deleted file mode 100644
index d8bba7d1eaf..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/ParameterlessInstanceMethodCallTranslator.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Query.Expressions;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- /// A base LINQ expression translator for CLR expressions that
- /// are instance methods and do not take arguments.
- ///
- public abstract class ParameterlessInstanceMethodCallTranslator : IMethodCallTranslator
- {
- private readonly MethodInfo _methodInfo;
- private readonly string _sqlFunctionName;
-
- ///
- /// Specialized constructor for use only by derived class.
- ///
- /// The declaring type of the method.
- /// Name of the method.
- /// The name of the target SQL function.
- protected ParameterlessInstanceMethodCallTranslator(
- [NotNull] Type declaringType, [NotNull] string clrMethodName, [NotNull] string sqlFunctionName)
- {
- _methodInfo = declaringType.GetRuntimeMethod(clrMethodName, Array.Empty());
-
- _sqlFunctionName = sqlFunctionName;
- }
-
- ///
- /// Translates the given method call expression.
- ///
- /// The method call expression.
- /// The loger.
- ///
- /// A SQL expression representing the translated MethodCallExpression.
- ///
- public virtual Expression Translate(
- MethodCallExpression methodCallExpression,
- IDiagnosticsLogger logger)
- {
- if (_methodInfo.Equals(methodCallExpression.Method))
- {
- var sqlArguments = new[] { methodCallExpression.Object }.Concat(methodCallExpression.Arguments);
- return new SqlFunctionExpression(_sqlFunctionName, methodCallExpression.Type, sqlArguments);
- }
-
- return null;
- }
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeExpressionFragmentTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeExpressionFragmentTranslator.cs
deleted file mode 100644
index af7d9ed5025..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeExpressionFragmentTranslator.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Generic;
-using System.Linq.Expressions;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators.Internal;
-using Microsoft.EntityFrameworkCore.Utilities;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- ///
- /// A composite expression fragment translator that dispatches to multiple specialized
- /// fragment translators.
- ///
- ///
- /// The service lifetime is . This means a single instance
- /// is used by many instances. The implementation must be thread-safe.
- /// This service cannot depend on services registered as .
- ///
- ///
- public class RelationalCompositeExpressionFragmentTranslator : IExpressionFragmentTranslator
- {
- private readonly List _translators
- = new List
- {
- new ComparisonTranslator(),
- new StringConcatTranslator()
- };
-
- ///
- /// Initializes a new instance of the this class.
- ///
- /// Parameter object containing dependencies for this service.
- public RelationalCompositeExpressionFragmentTranslator(
- [NotNull] RelationalCompositeExpressionFragmentTranslatorDependencies dependencies)
- {
- Check.NotNull(dependencies, nameof(dependencies));
- }
-
- ///
- /// Translates the given expression.
- ///
- /// The expression to translate.
- ///
- /// A SQL expression representing the translated expression.
- ///
- public virtual Expression Translate(Expression expression)
- {
- // ReSharper disable once LoopCanBeConvertedToQuery
- foreach (var translator in _translators)
- {
- var result = translator.Translate(expression);
- if (result != null)
- {
- return result;
- }
- }
-
- return null;
- }
-
- ///
- /// Adds additional translators to the dispatch list.
- ///
- /// The translators.
- protected virtual void AddTranslators([NotNull] IEnumerable translators)
- => _translators.InsertRange(0, translators);
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeExpressionFragmentTranslatorDependencies.cs b/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeExpressionFragmentTranslatorDependencies.cs
deleted file mode 100644
index 117e03b0043..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeExpressionFragmentTranslatorDependencies.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- ///
- /// Service dependencies parameter class for
- ///
- ///
- /// This type is typically used by database providers (and other extensions). It is generally
- /// not used in application code.
- ///
- ///
- /// Do not construct instances of this class directly from either provider or application code as the
- /// constructor signature may change as new dependencies are added. Instead, use this type in
- /// your constructor so that an instance will be created and injected automatically by the
- /// dependency injection container. To create an instance with some dependent services replaced,
- /// first resolve the object from the dependency injection container, then replace selected
- /// services using the 'With...' methods. Do not call the constructor at any point in this process.
- ///
- ///
- /// The service lifetime is .
- /// This means a single instance of each service is used by many instances.
- /// The implementation must be thread-safe.
- /// This service cannot depend on services registered as .
- ///
- ///
- public sealed class RelationalCompositeExpressionFragmentTranslatorDependencies
- {
- ///
- ///
- /// Creates the service dependencies parameter object for a .
- ///
- ///
- /// Do not call this constructor directly from either provider or application code as it may change
- /// as new dependencies are added. Instead, use this type in your constructor so that an instance
- /// will be created and injected automatically by the dependency injection container. To create
- /// an instance with some dependent services replaced, first resolve the object from the dependency
- /// injection container, then replace selected services using the 'With...' methods. Do not call
- /// the constructor at any point in this process.
- ///
- ///
- // ReSharper disable once EmptyConstructor
- public RelationalCompositeExpressionFragmentTranslatorDependencies()
- {
- }
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMemberTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMemberTranslator.cs
deleted file mode 100644
index c2ff30ea335..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMemberTranslator.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Generic;
-using System.Linq;
-using System.Linq.Expressions;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Utilities;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- ///
- /// A base composite member translator that dispatches to multiple specialized
- /// member translators.
- ///
- ///
- /// The service lifetime is . This means a single instance
- /// is used by many instances. The implementation must be thread-safe.
- /// This service cannot depend on services registered as .
- ///
- ///
- public abstract class RelationalCompositeMemberTranslator : IMemberTranslator
- {
- private readonly List _plugins = new List();
- private readonly List _translators = new List();
-
- ///
- /// Initializes a new instance of the this class.
- ///
- /// Parameter object containing dependencies for this service.
- protected RelationalCompositeMemberTranslator([NotNull] RelationalCompositeMemberTranslatorDependencies dependencies)
- {
- Check.NotNull(dependencies, nameof(dependencies));
-
- _plugins.AddRange(dependencies.Plugins.SelectMany(p => p.Translators));
- }
-
- ///
- /// Translates the given member expression.
- ///
- /// The member expression.
- ///
- /// A SQL expression representing the translated MemberExpression.
- ///
- public virtual Expression Translate(MemberExpression memberExpression)
- => _plugins.Concat(_translators)
- .Select(translator => translator.Translate(memberExpression))
- .FirstOrDefault(translatedMember => translatedMember != null);
-
- ///
- /// Adds additional translators to the dispatch list.
- ///
- /// The translators.
- protected virtual void AddTranslators([NotNull] IEnumerable translators)
- => _translators.AddRange(translators);
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMemberTranslatorDependencies.cs b/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMemberTranslatorDependencies.cs
deleted file mode 100644
index baaa1b26a53..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMemberTranslatorDependencies.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Generic;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Utilities;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- ///
- /// Service dependencies parameter class for
- ///
- ///
- /// This type is typically used by database providers (and other extensions). It is generally
- /// not used in application code.
- ///
- ///
- /// Do not construct instances of this class directly from either provider or application code as the
- /// constructor signature may change as new dependencies are added. Instead, use this type in
- /// your constructor so that an instance will be created and injected automatically by the
- /// dependency injection container. To create an instance with some dependent services replaced,
- /// first resolve the object from the dependency injection container, then replace selected
- /// services using the 'With...' methods. Do not call the constructor at any point in this process.
- ///
- ///
- /// The service lifetime is .
- /// This means a single instance of each service is used by many instances.
- /// The implementation must be thread-safe.
- /// This service cannot depend on services registered as .
- ///
- ///
- public sealed class RelationalCompositeMemberTranslatorDependencies
- {
- ///
- ///
- /// Creates the service dependencies parameter object for a .
- ///
- ///
- /// Do not call this constructor directly from either provider or application code as it may change
- /// as new dependencies are added. Instead, use this type in your constructor so that an instance
- /// will be created and injected automatically by the dependency injection container. To create
- /// an instance with some dependent services replaced, first resolve the object from the dependency
- /// injection container, then replace selected services using the 'With...' methods. Do not call
- /// the constructor at any point in this process.
- ///
- ///
- /// The plugins.
- public RelationalCompositeMemberTranslatorDependencies([NotNull] IEnumerable plugins)
- {
- Check.NotNull(plugins, nameof(plugins));
-
- Plugins = plugins;
- }
-
- ///
- /// Gets the plugins.
- ///
- public IEnumerable Plugins { get; }
-
- ///
- /// Clones this dependency parameter object with one service replaced.
- ///
- /// A replacement for the current dependency of this type.
- /// A new parameter object with the given service replaced.
- public RelationalCompositeMemberTranslatorDependencies With(
- [NotNull] IEnumerable plugins)
- => new RelationalCompositeMemberTranslatorDependencies(plugins);
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMethodCallTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMethodCallTranslator.cs
deleted file mode 100644
index 355f9c5947a..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMethodCallTranslator.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Generic;
-using System.Linq;
-using System.Linq.Expressions;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators.Internal;
-using Microsoft.EntityFrameworkCore.Utilities;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- ///
- /// A base composite method call translator that dispatches to multiple specialized
- /// method call translators.
- ///
- ///
- /// The service lifetime is . This means a single instance
- /// is used by many instances. The implementation must be thread-safe.
- /// This service cannot depend on services registered as .
- ///
- ///
- public abstract class RelationalCompositeMethodCallTranslator : ICompositeMethodCallTranslator
- {
- private readonly List _plugins = new List();
- private readonly List _methodCallTranslators;
-
- ///
- /// Initializes a new instance of the this class.
- ///
- /// Parameter object containing dependencies for this service.
- protected RelationalCompositeMethodCallTranslator(
- [NotNull] RelationalCompositeMethodCallTranslatorDependencies dependencies)
- {
- Check.NotNull(dependencies, nameof(dependencies));
-
- Dependencies = dependencies;
-
- _plugins.AddRange(dependencies.Plugins.SelectMany(p => p.Translators));
-
- _methodCallTranslators
- = new List
- {
- new EnumHasFlagTranslator(),
- new EqualsTranslator(),
- new GetValueOrDefaultTranslator(),
- new IsNullOrEmptyTranslator(),
- new LikeTranslator()
- };
- }
-
- ///
- /// Parameter object containing service dependencies.
- ///
- protected virtual RelationalCompositeMethodCallTranslatorDependencies Dependencies { get; }
-
- ///
- /// Translates the given method call expression.
- ///
- /// The method call expression.
- /// The current model.
- /// The logger.
- ///
- /// A SQL expression representing the translated MethodCallExpression.
- ///
- public virtual Expression Translate(
- MethodCallExpression methodCallExpression,
- IModel model,
- IDiagnosticsLogger logger)
- => ((IMethodCallTranslator)model.Relational().FindDbFunction(methodCallExpression.Method))?.Translate(methodCallExpression, logger)
- ?? _plugins.Concat(_methodCallTranslators)
- .Select(translator => translator.Translate(methodCallExpression, logger))
- .FirstOrDefault(translatedMethodCall => translatedMethodCall != null);
-
- ///
- /// Adds additional translators to the dispatch list.
- ///
- /// The translators.
- protected virtual void AddTranslators([NotNull] IEnumerable translators)
- => _methodCallTranslators.InsertRange(0, translators);
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMethodCallTranslatorDependencies.cs b/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMethodCallTranslatorDependencies.cs
deleted file mode 100644
index 8ed77de4871..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/RelationalCompositeMethodCallTranslatorDependencies.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System.Collections.Generic;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Utilities;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- ///
- /// Service dependencies parameter class for
- ///
- ///
- /// This type is typically used by database providers (and other extensions). It is generally
- /// not used in application code.
- ///
- ///
- /// Do not construct instances of this class directly from either provider or application code as the
- /// constructor signature may change as new dependencies are added. Instead, use this type in
- /// your constructor so that an instance will be created and injected automatically by the
- /// dependency injection container. To create an instance with some dependent services replaced,
- /// first resolve the object from the dependency injection container, then replace selected
- /// services using the 'With...' methods. Do not call the constructor at any point in this process.
- ///
- ///
- /// The service lifetime is .
- /// This means a single instance of each service is used by many instances.
- /// The implementation must be thread-safe.
- /// This service cannot depend on services registered as .
- ///
- ///
- public sealed class RelationalCompositeMethodCallTranslatorDependencies
- {
- ///
- ///
- /// Creates the service dependencies parameter object for a .
- ///
- ///
- /// Do not call this constructor directly from either provider or application code as it may change
- /// as new dependencies are added. Instead, use this type in your constructor so that an instance
- /// will be created and injected automatically by the dependency injection container. To create
- /// an instance with some dependent services replaced, first resolve the object from the dependency
- /// injection container, then replace selected services using the 'With...' methods. Do not call
- /// the constructor at any point in this process.
- ///
- ///
- /// The plugins.
- public RelationalCompositeMethodCallTranslatorDependencies(
- [NotNull] IEnumerable plugins)
- {
- Check.NotNull(plugins, nameof(plugins));
-
- Plugins = plugins;
- }
-
- ///
- /// Gets the plugins.
- ///
- public IEnumerable Plugins { get; }
-
- ///
- /// Clones this dependency parameter object with one service replaced.
- ///
- /// A replacement for the current dependency of this type.
- /// A new parameter object with the given service replaced.
- public RelationalCompositeMethodCallTranslatorDependencies With(
- [NotNull] IEnumerable plugins)
- => new RelationalCompositeMethodCallTranslatorDependencies(plugins);
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionTranslators/SingleOverloadStaticMethodCallTranslator.cs b/src/EFCore.Relational/Query/ExpressionTranslators/SingleOverloadStaticMethodCallTranslator.cs
deleted file mode 100644
index 0d5b3040fab..00000000000
--- a/src/EFCore.Relational/Query/ExpressionTranslators/SingleOverloadStaticMethodCallTranslator.cs
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Query.Expressions;
-
-namespace Microsoft.EntityFrameworkCore.Query.ExpressionTranslators
-{
- ///
- /// A base LINQ expression translator for CLR expressions that
- /// are static and are not overloaded.
- ///
- public abstract class SingleOverloadStaticMethodCallTranslator : IMethodCallTranslator
- {
- private readonly MethodInfo _methodInfo;
- private readonly string _sqlFunctionName;
-
- ///
- /// Specialized constructor for use only by derived class.
- ///
- /// The declaring type of the method.
- /// Name of the method.
- /// The name of the target SQL function.
- protected SingleOverloadStaticMethodCallTranslator(
- [NotNull] Type declaringType,
- [NotNull] string clrMethodName,
- [NotNull] string sqlFunctionName)
- {
- _methodInfo = declaringType.GetTypeInfo().GetDeclaredMethods(clrMethodName).Single();
-
- _sqlFunctionName = sqlFunctionName;
- }
-
- ///
- /// Translates the given method call expression.
- ///
- /// The method call expression.
- /// The logger.
- ///
- /// A SQL expression representing the translated MethodCallExpression.
- ///
- public virtual Expression Translate(
- MethodCallExpression methodCallExpression,
- IDiagnosticsLogger logger)
- => _methodInfo.Equals(methodCallExpression.Method)
- ? new SqlFunctionExpression(_sqlFunctionName, methodCallExpression.Type, methodCallExpression.Arguments)
- : null;
- }
-}
diff --git a/src/EFCore.Relational/Query/ExpressionVisitors/Internal/MaterializerFactory.cs b/src/EFCore.Relational/Query/ExpressionVisitors/Internal/MaterializerFactory.cs
index 6d16d0feaf7..2c2796ba949 100644
--- a/src/EFCore.Relational/Query/ExpressionVisitors/Internal/MaterializerFactory.cs
+++ b/src/EFCore.Relational/Query/ExpressionVisitors/Internal/MaterializerFactory.cs
@@ -81,7 +81,7 @@ var materializationContextParameter
var materializer
= _entityMaterializerSource
.CreateMaterializeExpression(
- firstEntityType, materializationContextParameter, indexMap);
+ firstEntityType, "instance", materializationContextParameter, indexMap);
if (concreteEntityTypes.Count == 1)
{
@@ -109,7 +109,8 @@ var blockExpressions
.CreateReadValueExpression(
Expression.Call(materializationContextParameter, MaterializationContext.GetValueBufferMethod),
discriminatorProperty.ClrType,
- indexMap[discriminatorProperty.GetIndex()])),
+ indexMap[discriminatorProperty.GetIndex()],
+ discriminatorProperty)),
Expression.IfThenElse(
Expression.Equal(discriminatorValueVariable, firstDiscriminatorValue),
Expression.Return(returnLabelTarget, materializer),
@@ -153,7 +154,7 @@ var discriminatorValue
materializer
= _entityMaterializerSource
.CreateMaterializeExpression(
- concreteEntityType, materializationContextParameter, indexMap);
+ concreteEntityType, "instance", materializationContextParameter, indexMap);
blockExpressions[1]
= Expression.IfThenElse(
diff --git a/src/EFCore.Relational/Query/ExpressionVisitors/Internal/UnbufferedEntityShaper.cs b/src/EFCore.Relational/Query/ExpressionVisitors/Internal/UnbufferedEntityShaper.cs
index 404ba5e4c06..f1fe3453d22 100644
--- a/src/EFCore.Relational/Query/ExpressionVisitors/Internal/UnbufferedEntityShaper.cs
+++ b/src/EFCore.Relational/Query/ExpressionVisitors/Internal/UnbufferedEntityShaper.cs
@@ -53,7 +53,7 @@ public virtual TEntity Shape(QueryContext queryContext, in ValueBuffer valueBuff
{
if (IsTrackingQuery)
{
- var entry = queryContext.StateManager.TryGetEntry(Key, valueBuffer, !AllowNullResult);
+ var entry = queryContext.StateManager.TryGetEntry(Key, new object[] { }, !AllowNullResult, out var _);
if (entry != null)
{
diff --git a/src/EFCore.Relational/Query/ExpressionVisitors/RelationalProjectionExpressionVisitor.cs b/src/EFCore.Relational/Query/ExpressionVisitors/RelationalProjectionExpressionVisitor.cs
index 9be4a529cca..678a2b95c66 100644
--- a/src/EFCore.Relational/Query/ExpressionVisitors/RelationalProjectionExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/ExpressionVisitors/RelationalProjectionExpressionVisitor.cs
@@ -305,7 +305,8 @@ var readValueExpression
.CreateReadValueExpression(
targetExpression,
expression.Type.MakeNullable(),
- index);
+ index,
+ sqlExpression.FindProperty(expression.Type));
var outputDataInfo
= (expression as SubQueryExpression)?.QueryModel
diff --git a/src/EFCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs
index f9e787ec36f..6906b591bc7 100644
--- a/src/EFCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs
@@ -12,7 +12,6 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query.Expressions;
using Microsoft.EntityFrameworkCore.Query.Expressions.Internal;
-using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Query.NavigationExpansion;
using Microsoft.EntityFrameworkCore.Storage;
@@ -43,9 +42,6 @@ private static readonly Dictionary _inverseOpera
{ ExpressionType.NotEqual, ExpressionType.Equal }
};
- private readonly IExpressionFragmentTranslator _compositeExpressionFragmentTranslator;
- private readonly ICompositeMethodCallTranslator _methodCallTranslator;
- private readonly IMemberTranslator _memberTranslator;
private readonly RelationalQueryModelVisitor _queryModelVisitor;
private readonly IRelationalTypeMappingSource _typeMappingSource;
private readonly SelectExpression _targetSelectExpression;
@@ -74,9 +70,6 @@ public SqlTranslatingExpressionVisitor(
Check.NotNull(dependencies, nameof(dependencies));
Check.NotNull(queryModelVisitor, nameof(queryModelVisitor));
- _compositeExpressionFragmentTranslator = dependencies.CompositeExpressionFragmentTranslator;
- _methodCallTranslator = dependencies.MethodCallTranslator;
- _memberTranslator = dependencies.MemberTranslator;
_typeMappingSource = dependencies.TypeMappingSource;
_queryModelVisitor = queryModelVisitor;
_targetSelectExpression = targetSelectExpression;
@@ -104,14 +97,6 @@ public SqlTranslatingExpressionVisitor(
///
public override Expression Visit(Expression expression)
{
- var translatedExpression = _compositeExpressionFragmentTranslator.Translate(expression);
-
- if (translatedExpression != null
- && translatedExpression != expression)
- {
- return Visit(translatedExpression);
- }
-
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (expression != null
&& (expression.NodeType == ExpressionType.Convert
@@ -624,40 +609,6 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
? methodCallExpression.Object
: Visit(methodCallExpression.Object);
- if (operand != null
- || methodCallExpression.Object == null)
- {
- var arguments
- = methodCallExpression.Arguments
- .Where(
- e => !(e.RemoveConvert() is QuerySourceReferenceExpression)
- && !IsNonTranslatableSubquery(e.RemoveConvert()))
- .Select(
- e => (e.RemoveConvert() as ConstantExpression)?.Value is Array || e.RemoveConvert().Type == typeof(DbFunctions)
- ? e
- : Visit(e))
- .Where(e => e != null)
- .ToArray();
-
- if (arguments.Length == methodCallExpression.Arguments.Count)
- {
- var boundExpression
- = operand != null
- ? Expression.Call(operand, methodCallExpression.Method, arguments)
- : Expression.Call(methodCallExpression.Method, arguments);
-
- var translatedExpression = _methodCallTranslator.Translate(
- boundExpression,
- compilationContext.Model,
- compilationContext.Logger);
-
- if (translatedExpression != null)
- {
- return translatedExpression;
- }
- }
- }
-
if (AnonymousObject.IsGetValueExpression(methodCallExpression, out var querySourceReferenceExpression)
|| MaterializedAnonymousObject.IsGetValueExpression(methodCallExpression, out querySourceReferenceExpression))
{
@@ -724,13 +675,6 @@ protected override Expression VisitMember(MemberExpression memberExpression)
{
newMemberExpression = memberExpression;
}
-
- var translatedExpression = _memberTranslator.Translate(newMemberExpression);
-
- if (translatedExpression != null)
- {
- return translatedExpression;
- }
}
}
@@ -778,7 +722,7 @@ private Expression TryBindMemberOrMethodToSelectExpression(
TExpression sourceExpression,
Func, Expression> binder)
{
- Expression BindPropertyToSelectExpression(
+ Expression bindPropertyToSelectExpression(
IProperty property, IQuerySource querySource, SelectExpression selectExpression)
=> selectExpression.BindProperty(
property,
@@ -787,7 +731,7 @@ Expression BindPropertyToSelectExpression(
var boundExpression = binder(
sourceExpression, _queryModelVisitor, (property, querySource, selectExpression) =>
{
- var boundPropertyExpression = BindPropertyToSelectExpression(property, querySource, selectExpression);
+ var boundPropertyExpression = bindPropertyToSelectExpression(property, querySource, selectExpression);
if (_targetSelectExpression != null
&& selectExpression != _targetSelectExpression)
@@ -809,7 +753,7 @@ Expression BindPropertyToSelectExpression(
while (outerQueryModelVisitor != null && canBindToOuterQueryModelVisitor)
{
- boundExpression = binder(sourceExpression, outerQueryModelVisitor, BindPropertyToSelectExpression);
+ boundExpression = binder(sourceExpression, outerQueryModelVisitor, bindPropertyToSelectExpression);
if (boundExpression != null)
{
diff --git a/src/EFCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitorDependencies.cs b/src/EFCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitorDependencies.cs
index 3d6a904d224..0e820d1dfbc 100644
--- a/src/EFCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitorDependencies.cs
+++ b/src/EFCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitorDependencies.cs
@@ -3,7 +3,6 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Query.ExpressionTranslators;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;
@@ -54,84 +53,21 @@ public sealed class SqlTranslatingExpressionVisitorDependencies
/// the constructor at any point in this process.
///
///
- /// The composite expression fragment translator.
- /// The method call translator.
- /// The member translator.
/// The type mapper.
[EntityFrameworkInternal]
public SqlTranslatingExpressionVisitorDependencies(
- [NotNull] IExpressionFragmentTranslator compositeExpressionFragmentTranslator,
- [NotNull] ICompositeMethodCallTranslator methodCallTranslator,
- [NotNull] IMemberTranslator memberTranslator,
[NotNull] IRelationalTypeMappingSource typeMappingSource)
{
- Check.NotNull(compositeExpressionFragmentTranslator, nameof(compositeExpressionFragmentTranslator));
- Check.NotNull(methodCallTranslator, nameof(methodCallTranslator));
- Check.NotNull(memberTranslator, nameof(memberTranslator));
Check.NotNull(typeMappingSource, nameof(typeMappingSource));
- CompositeExpressionFragmentTranslator = compositeExpressionFragmentTranslator;
- MethodCallTranslator = methodCallTranslator;
- MemberTranslator = memberTranslator;
TypeMappingSource = typeMappingSource;
}
- ///
- /// The composite expression fragment translator.
- ///
- public IExpressionFragmentTranslator CompositeExpressionFragmentTranslator { get; }
-
- ///
- /// The method call translator.
- ///
- public ICompositeMethodCallTranslator MethodCallTranslator { get; }
-
- ///
- /// The member translator.
- ///
- public IMemberTranslator MemberTranslator { get; }
-
///
/// The type mapping source.
///
public IRelationalTypeMappingSource TypeMappingSource { get; }
- ///
- /// Clones this dependency parameter object with one service replaced.
- ///
- /// A replacement for the current dependency of this type.
- /// A new parameter object with the given service replaced.
- public SqlTranslatingExpressionVisitorDependencies With([NotNull] IExpressionFragmentTranslator compositeExpressionFragmentTranslator)
- => new SqlTranslatingExpressionVisitorDependencies(
- compositeExpressionFragmentTranslator,
- MethodCallTranslator,
- MemberTranslator,
- TypeMappingSource);
-
- ///
- /// Clones this dependency parameter object with one service replaced.
- ///
- /// A replacement for the current dependency of this type.
- /// A new parameter object with the given service replaced.
- public SqlTranslatingExpressionVisitorDependencies With([NotNull] ICompositeMethodCallTranslator methodCallTranslator)
- => new SqlTranslatingExpressionVisitorDependencies(
- CompositeExpressionFragmentTranslator,
- methodCallTranslator,
- MemberTranslator,
- TypeMappingSource);
-
- ///
- /// Clones this dependency parameter object with one service replaced.
- ///
- /// A replacement for the current dependency of this type.
- /// A new parameter object with the given service replaced.
- public SqlTranslatingExpressionVisitorDependencies With([NotNull] IMemberTranslator memberTranslator)
- => new SqlTranslatingExpressionVisitorDependencies(
- CompositeExpressionFragmentTranslator,
- MethodCallTranslator,
- memberTranslator,
- TypeMappingSource);
-
///
/// Clones this dependency parameter object with one service replaced.
///
@@ -139,9 +75,6 @@ public SqlTranslatingExpressionVisitorDependencies With([NotNull] IMemberTransla
/// A new parameter object with the given service replaced.
public SqlTranslatingExpressionVisitorDependencies With([NotNull] IRelationalTypeMappingSource typeMappingSource)
=> new SqlTranslatingExpressionVisitorDependencies(
- CompositeExpressionFragmentTranslator,
- MethodCallTranslator,
- MemberTranslator,
typeMappingSource);
}
}
diff --git a/src/EFCore.Relational/Query/PipeLine/ComparisonTranslator.cs b/src/EFCore.Relational/Query/PipeLine/ComparisonTranslator.cs
new file mode 100644
index 00000000000..7de02c7acea
--- /dev/null
+++ b/src/EFCore.Relational/Query/PipeLine/ComparisonTranslator.cs
@@ -0,0 +1,61 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
+
+namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline
+{
+ public class ComparisonTranslator : IMethodCallTranslator
+ {
+ private readonly ISqlExpressionFactory _sqlExpressionFactory;
+
+ public ComparisonTranslator(ISqlExpressionFactory sqlExpressionFactory)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ }
+
+ public SqlExpression Translate(SqlExpression instance, MethodInfo method, IList arguments)
+ {
+ if (method.ReturnType == typeof(int))
+ {
+ SqlExpression left = null;
+ SqlExpression right = null;
+ if (method.Name == nameof(string.Compare)
+ && arguments.Count == 2
+ && arguments[0].Type.UnwrapNullableType() == arguments[1].Type.UnwrapNullableType())
+ {
+ left = arguments[0];
+ right = arguments[1];
+ }
+ else if (method.Name == nameof(string.CompareTo)
+ && arguments.Count == 1
+ && instance != null
+ && instance.Type.UnwrapNullableType() == arguments[0].Type.UnwrapNullableType())
+ {
+ left = instance;
+ right = arguments[0];
+ }
+
+ if (left != null && right != null)
+ {
+ return _sqlExpressionFactory.Case(
+ new CaseWhenClause[]
+ {
+ new CaseWhenClause(
+ _sqlExpressionFactory.Equal(left, right), _sqlExpressionFactory.Constant(0)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.GreaterThan(left, right), _sqlExpressionFactory.Constant(1)),
+ new CaseWhenClause(
+ _sqlExpressionFactory.LessThan(left, right), _sqlExpressionFactory.Constant(-1)),
+ },
+ null);
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/EFCore.Relational/Query/PipeLine/ContainsTranslator.cs b/src/EFCore.Relational/Query/PipeLine/ContainsTranslator.cs
new file mode 100644
index 00000000000..c0562a0a560
--- /dev/null
+++ b/src/EFCore.Relational/Query/PipeLine/ContainsTranslator.cs
@@ -0,0 +1,42 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
+
+namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline
+{
+ public class ContainsTranslator : IMethodCallTranslator
+ {
+ private static MethodInfo _containsMethod = typeof(Enumerable).GetTypeInfo()
+ .GetDeclaredMethods(nameof(Enumerable.Contains))
+ .Single(mi => mi.GetParameters().Length == 2)
+ .GetGenericMethodDefinition();
+
+ private readonly ISqlExpressionFactory _sqlExpressionFactory;
+
+ public ContainsTranslator(ISqlExpressionFactory sqlExpressionFactory)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ }
+
+ public SqlExpression Translate(SqlExpression instance, MethodInfo method, IList arguments)
+ {
+ if (method.IsGenericMethod
+ && method.GetGenericMethodDefinition().Equals(_containsMethod))
+ {
+ return _sqlExpressionFactory.In(arguments[1], arguments[0], false);
+ }
+ else if (method.DeclaringType.GetInterfaces().Contains(typeof(IList))
+ && string.Equals(method.Name, nameof(IList.Contains)))
+ {
+ return _sqlExpressionFactory.In(arguments[0], instance, false);
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/EFCore.Relational/Query/PipeLine/EntityProjectionExpression.cs b/src/EFCore.Relational/Query/PipeLine/EntityProjectionExpression.cs
new file mode 100644
index 00000000000..0b8fe670053
--- /dev/null
+++ b/src/EFCore.Relational/Query/PipeLine/EntityProjectionExpression.cs
@@ -0,0 +1,90 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
+
+namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline
+{
+ public class EntityProjectionExpression : Expression
+ {
+ private readonly IDictionary _propertyExpressionsCache
+ = new Dictionary();
+ private readonly TableExpressionBase _innerTable;
+
+ public EntityProjectionExpression(IEntityType entityType, TableExpressionBase innerTable, bool nullable)
+ {
+ EntityType = entityType;
+ _innerTable = innerTable;
+ Nullable = nullable;
+ }
+
+ public EntityProjectionExpression(IEntityType entityType, IDictionary propertyExpressions)
+ {
+ EntityType = entityType;
+ _propertyExpressionsCache = propertyExpressions;
+ }
+
+ protected override Expression VisitChildren(ExpressionVisitor visitor)
+ {
+ if (_innerTable != null)
+ {
+ var table = (TableExpressionBase)visitor.Visit(_innerTable);
+
+ return table != _innerTable
+ ? new EntityProjectionExpression(EntityType, table, Nullable)
+ : this;
+ }
+ else
+ {
+ var changed = false;
+ var newCache = new Dictionary();
+ foreach (var expression in _propertyExpressionsCache)
+ {
+ var newExpression = (ColumnExpression)visitor.Visit(expression.Value);
+ changed |= newExpression != expression.Value;
+
+ newCache[expression.Key] = newExpression;
+ }
+
+ return changed
+ ? new EntityProjectionExpression(EntityType, newCache)
+ : this;
+ }
+ }
+
+ public EntityProjectionExpression MakeNullable()
+ {
+ if (_innerTable != null)
+ {
+ return new EntityProjectionExpression(EntityType, _innerTable, true);
+ }
+ else
+ {
+ var newCache = new Dictionary();
+ foreach (var expression in _propertyExpressionsCache)
+ {
+ newCache[expression.Key] = expression.Value.MakeNullable();
+ }
+
+ return new EntityProjectionExpression(EntityType, newCache);
+ }
+ }
+
+ public IEntityType EntityType { get; }
+ public bool Nullable { get; }
+
+ public ColumnExpression GetProperty(IProperty property)
+ {
+ if (!_propertyExpressionsCache.TryGetValue(property, out var expression))
+ {
+ expression = new ColumnExpression(property, _innerTable, Nullable);
+ _propertyExpressionsCache[property] = expression;
+ }
+
+ return expression;
+ }
+ }
+}
diff --git a/src/EFCore.Relational/Query/PipeLine/EnumHasFlagTranslator.cs b/src/EFCore.Relational/Query/PipeLine/EnumHasFlagTranslator.cs
new file mode 100644
index 00000000000..ba08751dca7
--- /dev/null
+++ b/src/EFCore.Relational/Query/PipeLine/EnumHasFlagTranslator.cs
@@ -0,0 +1,41 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
+
+namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline
+{
+ public class EnumHasFlagTranslator : IMethodCallTranslator
+ {
+ private static readonly MethodInfo _methodInfo
+ = typeof(Enum).GetRuntimeMethod(nameof(Enum.HasFlag), new[] { typeof(Enum) });
+
+ private readonly ISqlExpressionFactory _sqlExpressionFactory;
+
+ public EnumHasFlagTranslator(ISqlExpressionFactory sqlExpressionFactory)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ }
+
+ public SqlExpression Translate(SqlExpression instance, MethodInfo method, IList arguments)
+ {
+ if (Equals(method, _methodInfo))
+ {
+ var argument = arguments[0];
+ if (instance.Type.UnwrapNullableType() != argument.Type.UnwrapNullableType())
+ {
+ return null;
+ }
+
+ return _sqlExpressionFactory.Equal(
+ _sqlExpressionFactory.And(instance, argument),
+ argument);
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/EFCore.Relational/Query/PipeLine/EqualsTranslator.cs b/src/EFCore.Relational/Query/PipeLine/EqualsTranslator.cs
new file mode 100644
index 00000000000..44d803ad114
--- /dev/null
+++ b/src/EFCore.Relational/Query/PipeLine/EqualsTranslator.cs
@@ -0,0 +1,69 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Reflection;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
+
+namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline
+{
+ public class EqualsTranslator : IMethodCallTranslator
+ {
+ private readonly ISqlExpressionFactory _sqlExpressionFactory;
+
+ public EqualsTranslator(ISqlExpressionFactory sqlExpressionFactory)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ }
+
+
+ public SqlExpression Translate(SqlExpression instance, MethodInfo method, IList arguments)
+ {
+ SqlExpression left = null;
+ SqlExpression right = null;
+
+ if (method.Name == nameof(object.Equals)
+ && instance != null
+ && arguments.Count == 1)
+ {
+ left = instance;
+ right = RemoveObjectConvert(arguments[0]);
+ }
+ else if (method.Name == nameof(object.Equals)
+ && arguments.Count == 2
+ && arguments[0].Type == arguments[1].Type)
+ {
+ left = RemoveObjectConvert(arguments[0]);
+ right = RemoveObjectConvert(arguments[1]);
+ }
+
+ if (left != null && right != null)
+ {
+ if (left.Type.UnwrapNullableType() == right.Type.UnwrapNullableType())
+ {
+ return _sqlExpressionFactory.Equal(left, right);
+ }
+ else
+ {
+ return _sqlExpressionFactory.Constant(false);
+ }
+ }
+
+ return null;
+ }
+
+ private SqlExpression RemoveObjectConvert(SqlExpression expression)
+ {
+ if (expression is SqlUnaryExpression sqlUnaryExpression
+ && sqlUnaryExpression.OperatorType == ExpressionType.Convert
+ && sqlUnaryExpression.Type == typeof(object))
+ {
+ return sqlUnaryExpression.Operand;
+ }
+
+ return expression;
+ }
+ }
+}
diff --git a/src/EFCore.Relational/Query/PipeLine/ExpressionExtensions.cs b/src/EFCore.Relational/Query/PipeLine/ExpressionExtensions.cs
new file mode 100644
index 00000000000..74d55c652b2
--- /dev/null
+++ b/src/EFCore.Relational/Query/PipeLine/ExpressionExtensions.cs
@@ -0,0 +1,26 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
+using Microsoft.EntityFrameworkCore.Storage;
+
+namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline
+{
+ public static class ExpressionExtensions
+ {
+ public static RelationalTypeMapping InferTypeMapping(params Expression[] expressions)
+ {
+ for (var i = 0; i < expressions.Length; i++)
+ {
+ if (expressions[i] is SqlExpression sql
+ && sql.TypeMapping != null)
+ {
+ return sql.TypeMapping;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/EFCore.Relational/Query/PipeLine/GetValueOrDefaultTranslator.cs b/src/EFCore.Relational/Query/PipeLine/GetValueOrDefaultTranslator.cs
new file mode 100644
index 00000000000..08d4cb68677
--- /dev/null
+++ b/src/EFCore.Relational/Query/PipeLine/GetValueOrDefaultTranslator.cs
@@ -0,0 +1,48 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using System.Reflection;
+using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions;
+
+namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline
+{
+ public class GetValueOrDefaultTranslator : IMethodCallTranslator
+ {
+ private readonly ISqlExpressionFactory _sqlExpressionFactory;
+ public GetValueOrDefaultTranslator(ISqlExpressionFactory sqlExpressionFactory)
+ {
+ _sqlExpressionFactory = sqlExpressionFactory;
+ }
+
+ public SqlExpression Translate(SqlExpression instance, MethodInfo method, IList arguments)
+ {
+ if (method.Name == nameof(Nullable.GetValueOrDefault)
+ && method.ReturnType.IsNumeric())
+ {
+ return _sqlExpressionFactory.Coalesce(
+ instance,
+ arguments.Count == 0
+ ? GetDefaultConstant(method.ReturnType)
+ : arguments[0],
+ instance.TypeMapping);
+ }
+
+ return null;
+ }
+
+ private SqlConstantExpression GetDefaultConstant(Type type)
+ {
+ return (SqlConstantExpression)_generateDefaultValueConstantMethod
+ .MakeGenericMethod(type).Invoke(null, Array.Empty