diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosProjectionBindingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosProjectionBindingExpressionVisitor.cs
index c875cb00104..2bfbcce59d7 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosProjectionBindingExpressionVisitor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosProjectionBindingExpressionVisitor.cs
@@ -26,6 +26,10 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal
///
public class CosmosProjectionBindingExpressionVisitor : ExpressionVisitor
{
+ private static readonly MethodInfo _getParameterValueMethodInfo
+ = typeof(CosmosProjectionBindingExpressionVisitor)
+ .GetTypeInfo().GetDeclaredMethod(nameof(GetParameterValue));
+
private readonly CosmosSqlTranslatingExpressionVisitor _sqlTranslator;
private readonly IModel _model;
private SelectExpression _selectExpression;
@@ -83,6 +87,8 @@ public virtual Expression Translate([NotNull] SelectExpression selectExpression,
_projectionMembers.Clear();
_projectionMapping.Clear();
+ result = MatchTypes(result, expression.Type);
+
return result;
}
@@ -148,7 +154,7 @@ public override Expression Visit(Expression expression)
}
return new ProjectionBindingExpression(
- _selectExpression, _selectExpression.AddToProjection(translation), expression.Type);
+ _selectExpression, _selectExpression.AddToProjection(translation), expression.Type.MakeNullable());
}
else
{
@@ -160,17 +166,117 @@ public override Expression Visit(Expression expression)
_projectionMapping[_projectionMembers.Peek()] = translation;
- return new ProjectionBindingExpression(_selectExpression, _projectionMembers.Peek(), expression.Type);
+ return new ProjectionBindingExpression(_selectExpression, _projectionMembers.Peek(), expression.Type.MakeNullable());
}
}
- private static readonly MethodInfo _getParameterValueMethodInfo
- = typeof(CosmosProjectionBindingExpressionVisitor)
- .GetTypeInfo().GetDeclaredMethod(nameof(GetParameterValue));
+ ///
+ /// 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.
+ ///
+ protected override Expression VisitBinary(BinaryExpression binaryExpression)
+ {
+ var left = MatchTypes(Visit(binaryExpression.Left), binaryExpression.Left.Type);
+ var right = MatchTypes(Visit(binaryExpression.Right), binaryExpression.Right.Type);
- [UsedImplicitly]
- private static T GetParameterValue(QueryContext queryContext, string parameterName)
- => (T)queryContext.ParameterValues[parameterName];
+ return binaryExpression.Update(left, VisitAndConvert(binaryExpression.Conversion, "VisitBinary"), right);
+ }
+
+ ///
+ /// 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.
+ ///
+ protected override Expression VisitConditional(ConditionalExpression conditionalExpression)
+ {
+ var test = Visit(conditionalExpression.Test);
+ var ifTrue = Visit(conditionalExpression.IfTrue);
+ var ifFalse = Visit(conditionalExpression.IfFalse);
+
+ if (test.Type == typeof(bool?))
+ {
+ test = Expression.Equal(test, Expression.Constant(true, typeof(bool?)));
+ }
+
+ return conditionalExpression.Update(test, ifTrue, ifFalse);
+ }
+
+ ///
+ /// 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.
+ ///
+ protected override Expression VisitExtension(Expression extensionExpression)
+ {
+ Check.NotNull(extensionExpression, nameof(extensionExpression));
+
+ switch (extensionExpression)
+ {
+ case EntityShaperExpression entityShaperExpression:
+ {
+ var projectionBindingExpression = (ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression;
+ VerifySelectExpression(projectionBindingExpression);
+
+ if (_clientEval)
+ {
+ var entityProjection = (EntityProjectionExpression)_selectExpression.GetMappedProjection(
+ projectionBindingExpression.ProjectionMember);
+
+ return entityShaperExpression.Update(
+ new ProjectionBindingExpression(
+ _selectExpression, _selectExpression.AddToProjection(entityProjection), typeof(ValueBuffer)));
+ }
+
+ _projectionMapping[_projectionMembers.Peek()]
+ = _selectExpression.GetMappedProjection(projectionBindingExpression.ProjectionMember);
+
+ return entityShaperExpression.Update(
+ new ProjectionBindingExpression(_selectExpression, _projectionMembers.Peek(), typeof(ValueBuffer)));
+ }
+
+ case MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression:
+ return materializeCollectionNavigationExpression.Navigation is INavigation embeddableNavigation
+ && embeddableNavigation.IsEmbedded()
+ ? base.Visit(materializeCollectionNavigationExpression.Subquery)
+ : base.VisitExtension(materializeCollectionNavigationExpression);
+
+ case IncludeExpression includeExpression:
+ if (!_clientEval)
+ {
+ return null;
+ }
+
+ if (!(includeExpression.Navigation is INavigation includableNavigation
+ && includableNavigation.IsEmbedded()))
+ {
+ throw new InvalidOperationException(CosmosStrings.NonEmbeddedIncludeNotSupported(includeExpression.Print()));
+ }
+
+ _includedNavigations.Push(includableNavigation);
+
+ var newIncludeExpression = base.VisitExtension(includeExpression);
+
+ _includedNavigations.Pop();
+
+ return newIncludeExpression;
+
+ default:
+ throw new InvalidOperationException(CoreStrings.QueryFailed(extensionExpression.Print(), GetType().Name));
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ protected override ElementInit VisitElementInit(ElementInit elementInit)
+ => elementInit.Update(elementInit.Arguments.Select(e => MatchTypes(Visit(e), e.Type)));
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -201,13 +307,13 @@ protected override Expression VisitMember(MemberExpression memberExpression)
if (shaperExpression == null
|| unaryExpression.NodeType != ExpressionType.Convert)
{
- return memberExpression.Update(innerExpression);
+ return NullSafeUpdate(innerExpression);
}
break;
default:
- return memberExpression.Update(innerExpression);
+ return NullSafeUpdate(innerExpression);
}
EntityProjectionExpression innerEntityProjection;
@@ -233,7 +339,7 @@ protected override Expression VisitMember(MemberExpression memberExpression)
if (!(propertyBase is INavigation navigation)
|| !navigation.IsEmbedded())
{
- return memberExpression.Update(innerExpression);
+ return NullSafeUpdate(innerExpression);
}
switch (navigationProjection)
@@ -262,6 +368,96 @@ protected override Expression VisitMember(MemberExpression memberExpression)
default:
throw new InvalidOperationException(CoreStrings.QueryFailed(memberExpression.Print(), GetType().Name));
}
+
+ Expression NullSafeUpdate(Expression expression)
+ {
+ Expression updatedMemberExpression = memberExpression.Update(
+ expression != null ? MatchTypes(expression, memberExpression.Expression.Type) : expression);
+
+ if (expression?.Type.IsNullableType() == true)
+ {
+ var nullableReturnType = memberExpression.Type.MakeNullable();
+ if (!memberExpression.Type.IsNullableType())
+ {
+ updatedMemberExpression = Expression.Convert(updatedMemberExpression, nullableReturnType);
+ }
+
+ updatedMemberExpression = Expression.Condition(
+ Expression.Equal(expression, Expression.Default(expression.Type)),
+ Expression.Constant(null, nullableReturnType),
+ updatedMemberExpression);
+ }
+
+ return updatedMemberExpression;
+ }
+ }
+
+ ///
+ /// 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.
+ ///
+ protected override MemberAssignment VisitMemberAssignment(MemberAssignment memberAssignment)
+ {
+ var expression = memberAssignment.Expression;
+ Expression visitedExpression;
+ if (_clientEval)
+ {
+ visitedExpression = Visit(memberAssignment.Expression);
+ }
+ else
+ {
+ var projectionMember = _projectionMembers.Peek().Append(memberAssignment.Member);
+ _projectionMembers.Push(projectionMember);
+
+ visitedExpression = Visit(memberAssignment.Expression);
+ if (visitedExpression == null)
+ {
+ return null;
+ }
+
+ _projectionMembers.Pop();
+ }
+
+ visitedExpression = MatchTypes(visitedExpression, expression.Type);
+
+ return memberAssignment.Update(visitedExpression);
+ }
+
+ ///
+ /// 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.
+ ///
+ protected override Expression VisitMemberInit(MemberInitExpression memberInitExpression)
+ {
+ Check.NotNull(memberInitExpression, nameof(memberInitExpression));
+
+ var newExpression = Visit(memberInitExpression.NewExpression);
+ if (newExpression == null)
+ {
+ return null;
+ }
+
+ var newBindings = new MemberBinding[memberInitExpression.Bindings.Count];
+ for (var i = 0; i < newBindings.Length; i++)
+ {
+ if (memberInitExpression.Bindings[i].BindingType != MemberBindingType.Assignment)
+ {
+ return null;
+ }
+
+ newBindings[i] = VisitMemberBinding(memberInitExpression.Bindings[i]);
+
+ if (newBindings[i] == null)
+ {
+ return null;
+ }
+ }
+
+ return memberInitExpression.Update((NewExpression)newExpression, newBindings);
}
///
@@ -413,72 +609,34 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
}
}
- return base.VisitMethodCall(methodCallExpression);
- }
+ var @object = Visit(methodCallExpression.Object);
+ var arguments = new Expression[methodCallExpression.Arguments.Count];
+ for (var i = 0; i < methodCallExpression.Arguments.Count; i++)
+ {
+ var argument = methodCallExpression.Arguments[i];
+ arguments[i] = MatchTypes(Visit(argument), argument.Type);
+ }
- ///
- /// 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.
- ///
- protected override Expression VisitExtension(Expression extensionExpression)
- {
- Check.NotNull(extensionExpression, nameof(extensionExpression));
+ Expression updatedMethodCallExpression = methodCallExpression.Update(
+ @object != null ? MatchTypes(@object, methodCallExpression.Object.Type) : @object,
+ arguments);
- switch (extensionExpression)
+ if (@object?.Type.IsNullableType() == true
+ && !methodCallExpression.Object.Type.IsNullableType())
{
- case EntityShaperExpression entityShaperExpression:
+ var nullableReturnType = methodCallExpression.Type.MakeNullable();
+ if (!methodCallExpression.Type.IsNullableType())
{
- var projectionBindingExpression = (ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression;
- VerifySelectExpression(projectionBindingExpression);
-
- if (_clientEval)
- {
- var entityProjection = (EntityProjectionExpression)_selectExpression.GetMappedProjection(
- projectionBindingExpression.ProjectionMember);
-
- return entityShaperExpression.Update(
- new ProjectionBindingExpression(
- _selectExpression, _selectExpression.AddToProjection(entityProjection), typeof(ValueBuffer)));
- }
-
- _projectionMapping[_projectionMembers.Peek()]
- = _selectExpression.GetMappedProjection(projectionBindingExpression.ProjectionMember);
-
- return entityShaperExpression.Update(
- new ProjectionBindingExpression(_selectExpression, _projectionMembers.Peek(), typeof(ValueBuffer)));
+ updatedMethodCallExpression = Expression.Convert(updatedMethodCallExpression, nullableReturnType);
}
- case MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression:
- return materializeCollectionNavigationExpression.Navigation is INavigation embeddableNavigation
- && embeddableNavigation.IsEmbedded()
- ? base.Visit(materializeCollectionNavigationExpression.Subquery)
- : base.VisitExtension(materializeCollectionNavigationExpression);
-
- case IncludeExpression includeExpression:
- if (!_clientEval)
- {
- return null;
- }
-
- if (!(includeExpression.Navigation is INavigation includableNavigation
- && includableNavigation.IsEmbedded()))
- {
- throw new InvalidOperationException(CosmosStrings.NonEmbeddedIncludeNotSupported(includeExpression.Print()));
- }
-
- _includedNavigations.Push(includableNavigation);
-
- var newIncludeExpression = base.VisitExtension(includeExpression);
-
- _includedNavigations.Pop();
-
- return newIncludeExpression;
-
- default:
- throw new InvalidOperationException(CoreStrings.QueryFailed(extensionExpression.Print(), GetType().Name));
+ return Expression.Condition(
+ Expression.Equal(@object, Expression.Default(@object.Type)),
+ Expression.Constant(null, nullableReturnType),
+ updatedMethodCallExpression);
}
+
+ return updatedMethodCallExpression;
}
///
@@ -505,22 +663,26 @@ protected override Expression VisitNew(NewExpression newExpression)
var newArguments = new Expression[newExpression.Arguments.Count];
for (var i = 0; i < newArguments.Length; i++)
{
+ var argument = newExpression.Arguments[i];
+ Expression visitedArgument;
if (_clientEval)
{
- newArguments[i] = Visit(newExpression.Arguments[i]);
+ visitedArgument = Visit(argument);
}
else
{
var projectionMember = _projectionMembers.Peek().Append(newExpression.Members[i]);
_projectionMembers.Push(projectionMember);
- newArguments[i] = Visit(newExpression.Arguments[i]);
- if (newArguments[i] == null)
+ visitedArgument = Visit(argument);
+ if (visitedArgument == null)
{
return null;
}
_projectionMembers.Pop();
}
+
+ newArguments[i] = MatchTypes(visitedArgument, argument.Type);
}
return newExpression.Update(newArguments);
@@ -532,34 +694,8 @@ protected override Expression VisitNew(NewExpression newExpression)
/// 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.
///
- protected override Expression VisitMemberInit(MemberInitExpression memberInitExpression)
- {
- Check.NotNull(memberInitExpression, nameof(memberInitExpression));
-
- var newExpression = Visit(memberInitExpression.NewExpression);
- if (newExpression == null)
- {
- return null;
- }
-
- var newBindings = new MemberBinding[memberInitExpression.Bindings.Count];
- for (var i = 0; i < newBindings.Length; i++)
- {
- if (memberInitExpression.Bindings[i].BindingType != MemberBindingType.Assignment)
- {
- return null;
- }
-
- newBindings[i] = VisitMemberBinding(memberInitExpression.Bindings[i]);
-
- if (newBindings[i] == null)
- {
- return null;
- }
- }
-
- return memberInitExpression.Update((NewExpression)newExpression, newBindings);
- }
+ protected override Expression VisitNewArray(NewArrayExpression newArrayExpression)
+ => newArrayExpression.Update(newArrayExpression.Expressions.Select(e => MatchTypes(Visit(e), e.Type)));
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -567,24 +703,15 @@ protected override Expression VisitMemberInit(MemberInitExpression memberInitExp
/// 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.
///
- protected override MemberAssignment VisitMemberAssignment(MemberAssignment memberAssignment)
+ protected override Expression VisitUnary(UnaryExpression unaryExpression)
{
- if (_clientEval)
- {
- return memberAssignment.Update(Visit(memberAssignment.Expression));
- }
+ var operand = Visit(unaryExpression.Operand);
- var projectionMember = _projectionMembers.Peek().Append(memberAssignment.Member);
- _projectionMembers.Push(projectionMember);
-
- var visitedExpression = Visit(memberAssignment.Expression);
- if (visitedExpression == null)
- {
- return null;
- }
-
- _projectionMembers.Pop();
- return memberAssignment.Update(visitedExpression);
+ return (unaryExpression.NodeType == ExpressionType.Convert
+ || unaryExpression.NodeType == ExpressionType.ConvertChecked)
+ && unaryExpression.Type == operand.Type
+ ? operand
+ : unaryExpression.Update(MatchTypes(operand, unaryExpression.Operand.Type));
}
// TODO: Debugging
@@ -595,5 +722,22 @@ private void VerifySelectExpression(ProjectionBindingExpression projectionBindin
throw new InvalidOperationException(CoreStrings.QueryFailed(projectionBindingExpression.Print(), GetType().Name));
}
}
+
+ private static Expression MatchTypes(Expression expression, Type targetType)
+ {
+ if (targetType != expression.Type
+ && targetType.TryGetSequenceType() == null)
+ {
+ Check.DebugAssert(targetType.MakeNullable() == expression.Type, "expression.Type must be nullable of targetType");
+
+ expression = Expression.Convert(expression, targetType);
+ }
+
+ return expression;
+ }
+
+ [UsedImplicitly]
+ private static T GetParameterValue(QueryContext queryContext, string parameterName)
+ => (T)queryContext.ParameterValues[parameterName];
}
}
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
index 8955a05bb4e..c7b6445882e 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
@@ -385,7 +385,10 @@ protected override ShapedQueryExpression TranslateCount(ShapedQueryExpression so
selectExpression.ClearOrdering();
selectExpression.ReplaceProjectionMapping(projectionMapping);
- return source.UpdateShaperExpression(new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), typeof(int)));
+ return source.UpdateShaperExpression(
+ Expression.Convert(
+ new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), typeof(int?)),
+ typeof(int)));
}
///
@@ -631,7 +634,10 @@ protected override ShapedQueryExpression TranslateLongCount(ShapedQueryExpressio
selectExpression.ClearOrdering();
selectExpression.ReplaceProjectionMapping(projectionMapping);
- return source.UpdateShaperExpression(new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), typeof(long)));
+ return source.UpdateShaperExpression(
+ Expression.Convert(
+ new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), typeof(long?)),
+ typeof(long)));
}
///
@@ -1132,8 +1138,7 @@ private ShapedQueryExpression AggregateResultShaper(
selectExpression.ClearOrdering();
var nullableResultType = resultType.MakeNullable();
- Expression shaper = new ProjectionBindingExpression(
- source.QueryExpression, new ProjectionMember(), throwOnNullResult ? nullableResultType : projection.Type);
+ Expression shaper = new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), nullableResultType);
if (throwOnNullResult)
{
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
index 801d47c85d0..aac57f1d65f 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosShapedQueryCompilingExpressionVisitor.CosmosProjectionBindingRemovingExpressionVisitorBase.cs
@@ -640,7 +640,9 @@ private Expression CreateGetValueExpression(
return Expression.Default(clrType);
}
- return CreateGetValueExpression(jObjectExpression, storeName, clrType, property.GetTypeMapping());
+ return Expression.Convert(
+ CreateGetValueExpression(jObjectExpression, storeName, clrType.MakeNullable(), property.GetTypeMapping()),
+ clrType);
}
private Expression CreateGetValueExpression(
@@ -649,6 +651,8 @@ private Expression CreateGetValueExpression(
Type clrType,
CoreTypeMapping typeMapping = null)
{
+ Check.DebugAssert(clrType.IsNullableType(), "Must read nullable type from JObject.");
+
var innerExpression = jObjectExpression;
if (_projectionBindings.TryGetValue(jObjectExpression, out var innerVariable))
{
diff --git a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs
index 20b4f886e8d..34c41454752 100644
--- a/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/Internal/RelationalProjectionBindingExpressionVisitor.cs
@@ -30,6 +30,7 @@ private static readonly MethodInfo _getParameterValueMethodInfo
private readonly RelationalQueryableMethodTranslatingExpressionVisitor _queryableMethodTranslatingExpressionVisitor;
private readonly RelationalSqlTranslatingExpressionVisitor _sqlTranslator;
+ private readonly IncludeFindingExpressionVisitor _includeFindingExpressionVisitor;
private SelectExpression _selectExpression;
private SqlExpression[] _existingProjections;
@@ -52,6 +53,7 @@ public RelationalProjectionBindingExpressionVisitor(
{
_queryableMethodTranslatingExpressionVisitor = queryableMethodTranslatingExpressionVisitor;
_sqlTranslator = sqlTranslatingExpressionVisitor;
+ _includeFindingExpressionVisitor = new IncludeFindingExpressionVisitor();
}
///
@@ -367,7 +369,8 @@ protected override Expression VisitMember(MemberExpression memberExpression)
Expression updatedMemberExpression = memberExpression.Update(
expression != null ? MatchTypes(expression, memberExpression.Expression.Type) : expression);
- if (expression?.Type.IsNullableValueType() == true)
+ if (expression?.Type.IsNullableType() == true
+ && !_includeFindingExpressionVisitor.ContainsInclude(expression))
{
var nullableReturnType = memberExpression.Type.MakeNullable();
if (!memberExpression.Type.IsNullableType())
@@ -591,5 +594,33 @@ private static Expression MatchTypes(Expression expression, Type targetType)
private static T GetParameterValue(QueryContext queryContext, string parameterName)
#pragma warning restore IDE0052 // Remove unread private members
=> (T)queryContext.ParameterValues[parameterName];
+
+ private sealed class IncludeFindingExpressionVisitor : ExpressionVisitor
+ {
+ private bool _containsInclude;
+
+ public bool ContainsInclude(Expression expression)
+ {
+ _containsInclude = false;
+
+ Visit(expression);
+
+ return _containsInclude;
+ }
+
+ public override Expression Visit(Expression expression) => _containsInclude ? expression : base.Visit(expression);
+
+ protected override Expression VisitExtension(Expression extensionExpression)
+ {
+ if (extensionExpression is IncludeExpression)
+ {
+ _containsInclude = true;
+
+ return extensionExpression;
+ }
+
+ return base.VisitExtension(extensionExpression);
+ }
+ }
}
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/BuiltInDataTypesCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/BuiltInDataTypesCosmosTest.cs
index 7a1371feb4b..f685f4ac7de 100644
--- a/test/EFCore.Cosmos.FunctionalTests/BuiltInDataTypesCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/BuiltInDataTypesCosmosTest.cs
@@ -80,12 +80,6 @@ FROM root c
WHERE ((c[""Discriminator""] = ""BuiltInDataTypes"") AND (c[""Id""] = 13))");
}
- [ConditionalFact(Skip = "Issue#21678")]
- public override void Optional_datetime_reading_null_from_database()
- {
- base.Optional_datetime_reading_null_from_database();
- }
-
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.Cosmos.FunctionalTests/CustomConvertersCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/CustomConvertersCosmosTest.cs
index 70ca847e7db..76dae8422d4 100644
--- a/test/EFCore.Cosmos.FunctionalTests/CustomConvertersCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/CustomConvertersCosmosTest.cs
@@ -152,12 +152,6 @@ public override void Optional_owned_with_converter_reading_non_nullable_column()
base.Optional_owned_with_converter_reading_non_nullable_column();
}
- [ConditionalFact(Skip = "Issue#21678")]
- public override void Optional_datetime_reading_null_from_database()
- {
- base.Optional_datetime_reading_null_from_database();
- }
-
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs
index 451f3d548c3..a85263ae58b 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs
@@ -2308,12 +2308,10 @@ FROM root c
WHERE (c[""Discriminator""] = ""Customer"")");
}
+ [ConditionalTheory(Skip = "Issue#13168")]
public override Task Where_bitwise_or_with_logical_or(bool async)
{
- // #13168
- //await base.Where_bitwise_or_with_logical_or(async);
-
- return Task.CompletedTask;
+ return base.Where_bitwise_or_with_logical_or(async);
}
public override async Task Where_bitwise_and_with_logical_and(bool async)
@@ -2326,12 +2324,10 @@ FROM root c
WHERE ((c[""Discriminator""] = ""Customer"") AND (((c[""CustomerID""] = ""ALFKI"") & (c[""CustomerID""] = ""ANATR"")) AND (c[""CustomerID""] = ""ANTON"")))");
}
+ [ConditionalTheory(Skip = "Issue#13168")]
public override Task Where_bitwise_or_with_logical_and(bool async)
{
- // #13168
- //await base.Where_bitwise_or_with_logical_and(async);
-
- return Task.CompletedTask;
+ return base.Where_bitwise_or_with_logical_and(async);
}
public override async Task Where_bitwise_and_with_logical_or(bool async)
@@ -2409,12 +2405,10 @@ FROM root c
WHERE (c[""Discriminator""] = ""Customer"")");
}
+ [ConditionalTheory(Skip = "Issue#13159")]
public override Task Parameter_extraction_short_circuits_1(bool async)
{
- // #13159
- //await base.Parameter_extraction_short_circuits_1(async);
-
- return Task.CompletedTask;
+ return base.Parameter_extraction_short_circuits_1(async);
}
[ConditionalTheory(Skip = "Issue #17246")]
@@ -2428,12 +2422,10 @@ FROM root c
WHERE (c[""Discriminator""] = ""Order"")");
}
+ [ConditionalTheory(Skip = "Issue#13159")]
public override Task Parameter_extraction_short_circuits_3(bool async)
{
- // #13159
- //await base.Parameter_extraction_short_circuits_3(async);
-
- return Task.CompletedTask;
+ return base.Parameter_extraction_short_circuits_3(async);
}
[ConditionalTheory(Skip = "Issue #17246")]
@@ -4151,24 +4143,18 @@ public override Task Select_distinct_Select_with_client_bindings(bool async)
return base.Select_distinct_Select_with_client_bindings(async);
}
- [ConditionalTheory(Skip = "Issue#21678")]
- public override Task Non_nullable_property_through_optional_navigation(bool async)
+ [ConditionalTheory(Skip = "Non embedded collection subquery Issue#17246")]
+ public override Task Pending_selector_in_cardinality_reducing_method_is_applied_before_expanding_collection_navigation_member(bool async)
{
- return base.Non_nullable_property_through_optional_navigation(async);
+ return base.Pending_selector_in_cardinality_reducing_method_is_applied_before_expanding_collection_navigation_member(async);
}
- [ConditionalTheory(Skip = "Issue#21678")]
+ [ConditionalTheory(Skip = "Non embedded collection subquery Issue#17246")]
public override Task Max_on_empty_sequence_throws(bool async)
{
return base.Max_on_empty_sequence_throws(async);
}
- [ConditionalTheory(Skip = "Non embedded collection subquery Issue#17246")]
- public override Task Pending_selector_in_cardinality_reducing_method_is_applied_before_expanding_collection_navigation_member(bool async)
- {
- return base.Pending_selector_in_cardinality_reducing_method_is_applied_before_expanding_collection_navigation_member(async);
- }
-
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSetOperationsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSetOperationsQueryCosmosTest.cs
deleted file mode 100644
index b8fa624db28..00000000000
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindSetOperationsQueryCosmosTest.cs
+++ /dev/null
@@ -1,65 +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.Threading.Tasks;
-using Microsoft.EntityFrameworkCore.TestUtilities;
-using Xunit.Abstractions;
-
-namespace Microsoft.EntityFrameworkCore.Query
-{
- public class NorthwindSetOperationsQueryCosmosTest : NorthwindSetOperationsQueryTestBase>
- {
- public NorthwindSetOperationsQueryCosmosTest(
- NorthwindQueryCosmosFixture fixture,
- ITestOutputHelper testOutputHelper)
- : base(fixture)
- {
- ClearLog();
- //Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
- }
-
- // Set operations aren't supported on Cosmos
- public override Task Concat(bool async) => Task.CompletedTask;
- public override Task Concat_nested(bool async) => Task.CompletedTask;
- public override Task Concat_non_entity(bool async) => Task.CompletedTask;
- public override Task Except(bool async) => Task.CompletedTask;
- public override Task Except_simple_followed_by_projecting_constant(bool async) => Task.CompletedTask;
- public override Task Except_nested(bool async) => Task.CompletedTask;
- public override Task Except_non_entity(bool async) => Task.CompletedTask;
- public override Task Intersect(bool async) => Task.CompletedTask;
- public override Task Intersect_nested(bool async) => Task.CompletedTask;
- public override Task Intersect_non_entity(bool async) => Task.CompletedTask;
- public override Task Union(bool async) => Task.CompletedTask;
- public override Task Union_nested(bool async) => Task.CompletedTask;
- public override Task Union_non_entity(bool async) => Task.CompletedTask;
- public override Task Union_OrderBy_Skip_Take(bool async) => Task.CompletedTask;
- public override Task Union_Where(bool async) => Task.CompletedTask;
- public override Task Union_Skip_Take_OrderBy_ThenBy_Where(bool async) => Task.CompletedTask;
- public override Task Union_Union(bool async) => Task.CompletedTask;
- public override Task Union_Intersect(bool async) => Task.CompletedTask;
- public override Task Union_Take_Union_Take(bool async) => Task.CompletedTask;
- public override Task Select_Union(bool async) => Task.CompletedTask;
- public override Task Union_Select(bool async) => Task.CompletedTask;
- public override Task Union_Select_scalar(bool async) => Task.CompletedTask;
- public override Task Union_with_anonymous_type_projection(bool async) => Task.CompletedTask;
- public override Task Select_Union_unrelated(bool async) => Task.CompletedTask;
- public override Task Select_Union_different_fields_in_anonymous_with_subquery(bool async) => Task.CompletedTask;
- public override Task Union_Include(bool async) => Task.CompletedTask;
- public override Task Include_Union(bool async) => Task.CompletedTask;
- public override Task Select_Except_reference_projection(bool async) => Task.CompletedTask;
- public override void Include_Union_only_on_one_side_throws() { }
- public override void Include_Union_different_includes_throws() { }
- public override Task SubSelect_Union(bool async) => Task.CompletedTask;
- public override Task Client_eval_Union_FirstOrDefault(bool async) => Task.CompletedTask;
- public override Task GroupBy_Select_Union(bool async) => Task.CompletedTask;
- public override Task Union_over_columns_with_different_nullability(bool async) => Task.CompletedTask;
- public override Task Union_over_different_projection_types(bool async, string leftType, string rightType) => Task.CompletedTask;
- public override Task OrderBy_Take_Union(bool async) => Task.CompletedTask;
-
- private void AssertSql(params string[] expected)
- => Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
-
- protected override void ClearLog()
- => Fixture.TestSqlLoggerFactory.Clear();
- }
-}