diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
index 1f1a984b226..55c748b0031 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
@@ -27,6 +27,8 @@ public class CosmosQueryableMethodTranslatingExpressionVisitor : QueryableMethod
{
private readonly IModel _model;
private readonly ISqlExpressionFactory _sqlExpressionFactory;
+ private readonly IMemberTranslatorProvider _memberTranslatorProvider;
+ private readonly IMethodCallTranslatorProvider _methodCallTranslatorProvider;
private readonly CosmosSqlTranslatingExpressionVisitor _sqlTranslator;
private readonly CosmosProjectionBindingExpressionVisitor _projectionBindingExpressionVisitor;
@@ -46,11 +48,13 @@ public CosmosQueryableMethodTranslatingExpressionVisitor(
{
_model = queryCompilationContext.Model;
_sqlExpressionFactory = sqlExpressionFactory;
+ _memberTranslatorProvider = memberTranslatorProvider;
+ _methodCallTranslatorProvider = methodCallTranslatorProvider;
_sqlTranslator = new CosmosSqlTranslatingExpressionVisitor(
queryCompilationContext,
- sqlExpressionFactory,
- memberTranslatorProvider,
- methodCallTranslatorProvider);
+ _sqlExpressionFactory,
+ _memberTranslatorProvider,
+ _methodCallTranslatorProvider);
_projectionBindingExpressionVisitor = new CosmosProjectionBindingExpressionVisitor(_model, _sqlTranslator);
}
@@ -66,7 +70,11 @@ protected CosmosQueryableMethodTranslatingExpressionVisitor(
{
_model = parentVisitor._model;
_sqlExpressionFactory = parentVisitor._sqlExpressionFactory;
- _sqlTranslator = parentVisitor._sqlTranslator;
+ _sqlTranslator = new CosmosSqlTranslatingExpressionVisitor(
+ QueryCompilationContext,
+ _sqlExpressionFactory,
+ _memberTranslatorProvider,
+ _methodCallTranslatorProvider);
_projectionBindingExpressionVisitor = new CosmosProjectionBindingExpressionVisitor(_model, _sqlTranslator);
}
@@ -1091,7 +1099,15 @@ when methodCallExpression.TryGetIndexerArguments(_model, out _, out var property
}
private SqlExpression TranslateExpression(Expression expression)
- => _sqlTranslator.Translate(expression);
+ {
+ var translation = _sqlTranslator.Translate(expression);
+ if (translation == null && _sqlTranslator.TranslationErrorDetails != null)
+ {
+ ProvideTranslationErrorDetails(_sqlTranslator.TranslationErrorDetails);
+ }
+
+ return translation;
+ }
private SqlExpression TranslateLambdaExpression(
ShapedQueryExpression shapedQueryExpression, LambdaExpression lambdaExpression)
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs
index cf7fd5fb10b..8f13512ec78 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosSqlTranslatingExpressionVisitor.cs
@@ -35,6 +35,10 @@ public class CosmosSqlTranslatingExpressionVisitor : ExpressionVisitor
private static readonly MethodInfo _concatMethodInfo
= typeof(string).GetRuntimeMethod(nameof(string.Concat), new[] { typeof(object), typeof(object) });
+ private static readonly MethodInfo _stringEqualsWithStringComparison
+ = typeof(string).GetRuntimeMethod(nameof(string.Equals), new[] { typeof(string), typeof(StringComparison) });
+ private static readonly MethodInfo _stringEqualsWithStringComparisonStatic
+ = typeof(string).GetRuntimeMethod(nameof(string.Equals), new[] { typeof(string), typeof(string), typeof(StringComparison) });
private readonly QueryCompilationContext _queryCompilationContext;
private readonly IModel _model;
@@ -63,6 +67,34 @@ public CosmosSqlTranslatingExpressionVisitor(
_sqlVerifyingExpressionVisitor = new SqlTypeMappingVerifyingExpressionVisitor();
}
+ ///
+ /// 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 string TranslationErrorDetails { get; private 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.
+ ///
+ protected virtual void ProvideTranslationErrorDetails([NotNull] string details)
+ {
+ Check.NotNull(details, nameof(details));
+
+ if (TranslationErrorDetails == null)
+ {
+ TranslationErrorDetails = details;
+ }
+ else
+ {
+ TranslationErrorDetails += Environment.NewLine + details;
+ }
+ }
+
///
/// 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
@@ -70,6 +102,15 @@ public CosmosSqlTranslatingExpressionVisitor(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual SqlExpression Translate([NotNull] Expression expression)
+ {
+ Check.NotNull(expression, nameof(expression));
+
+ TranslationErrorDetails = null;
+
+ return TranslateInternal(expression);
+ }
+
+ private SqlExpression TranslateInternal(Expression expression)
{
var result = Visit(expression);
@@ -416,7 +457,16 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
}
}
- return _methodCallTranslatorProvider.Translate(_model, sqlObject, methodCallExpression.Method, arguments);
+ var translation = _methodCallTranslatorProvider.Translate(_model, sqlObject, methodCallExpression.Method, arguments);
+
+ if (translation == null
+ && (methodCallExpression.Method == _stringEqualsWithStringComparison
+ || methodCallExpression.Method == _stringEqualsWithStringComparisonStatic))
+ {
+ ProvideTranslationErrorDetails(CoreStrings.QueryUnableToTranslateStringEqualsWithStringComparison);
+ }
+
+ return translation;
static Expression RemoveObjectConvert(Expression expression)
=> expression is UnaryExpression unaryExpression
@@ -518,6 +568,14 @@ private Expression TryBindMember(Expression source, MemberIdentity member)
? entityReferenceExpression.ParameterEntity.BindMember(member.MemberInfo, entityReferenceExpression.Type, clientEval: false, out _)
: entityReferenceExpression.ParameterEntity.BindMember(member.Name, entityReferenceExpression.Type, clientEval: false, out _);
+ if (result == null)
+ {
+ ProvideTranslationErrorDetails(
+ CoreStrings.QueryUnableToTranslateMember(
+ member.Name,
+ entityReferenceExpression.EntityType.DisplayName()));
+ }
+
return result switch
{
EntityProjectionExpression entityProjectionExpression => new EntityReferenceExpression(entityProjectionExpression),
diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs
index 58edc24780c..34d0c93a646 100644
--- a/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs
+++ b/src/EFCore.InMemory/Query/Internal/InMemoryExpressionTranslatingExpressionVisitor.cs
@@ -79,6 +79,34 @@ public InMemoryExpressionTranslatingExpressionVisitor(
_model = queryCompilationContext.Model;
}
+ ///
+ /// 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 string TranslationErrorDetails { get; private 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.
+ ///
+ protected virtual void ProvideTranslationErrorDetails([NotNull] string details)
+ {
+ Check.NotNull(details, nameof(details));
+
+ if (TranslationErrorDetails == null)
+ {
+ TranslationErrorDetails = details;
+ }
+ else
+ {
+ TranslationErrorDetails += Environment.NewLine + details;
+ }
+ }
+
///
/// 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
@@ -86,6 +114,15 @@ public InMemoryExpressionTranslatingExpressionVisitor(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual Expression Translate([NotNull] Expression expression)
+ {
+ Check.NotNull(expression, nameof(expression));
+
+ TranslationErrorDetails = null;
+
+ return TranslateInternal(expression);
+ }
+
+ private Expression TranslateInternal(Expression expression)
{
var result = Visit(expression);
@@ -366,7 +403,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
case nameof(Enumerable.Min):
case nameof(Enumerable.Sum):
{
- var translation = Translate(GetSelectorOnGrouping(methodCallExpression, groupByShaperExpression));
+ var translation = TranslateInternal(GetSelectorOnGrouping(methodCallExpression, groupByShaperExpression));
if (translation == null)
{
return null;
@@ -409,7 +446,7 @@ MethodInfo GetMethod()
groupByShaperExpression.GroupingParameter);
}
- var translation = Translate(predicate);
+ var translation = TranslateInternal(predicate);
if (translation == null)
{
return null;
@@ -829,7 +866,18 @@ private Expression TryBindMember(Expression source, MemberIdentity member, Type
? entityType.FindProperty(member.MemberInfo)
: entityType.FindProperty(member.Name);
- return property != null ? BindProperty(entityReferenceExpression, property, type) : null;
+ if (property != null)
+ {
+ return BindProperty(entityReferenceExpression, property, type);
+
+ }
+
+ ProvideTranslationErrorDetails(
+ CoreStrings.QueryUnableToTranslateMember(
+ member.Name,
+ entityReferenceExpression.EntityType.DisplayName()));
+
+ return null;
}
private Expression BindProperty(EntityReferenceExpression entityReferenceExpression, IProperty property, Type type)
diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs
index f2aa685c340..694de799264 100644
--- a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs
@@ -56,8 +56,8 @@ protected InMemoryQueryableMethodTranslatingExpressionVisitor(
[NotNull] InMemoryQueryableMethodTranslatingExpressionVisitor parentVisitor)
: base(parentVisitor.Dependencies, parentVisitor.QueryCompilationContext, subquery: true)
{
- _expressionTranslator = parentVisitor._expressionTranslator;
- _weakEntityExpandingExpressionVisitor = parentVisitor._weakEntityExpandingExpressionVisitor;
+ _expressionTranslator = new InMemoryExpressionTranslatingExpressionVisitor(QueryCompilationContext, parentVisitor);
+ _weakEntityExpandingExpressionVisitor = new WeakEntityExpandingExpressionVisitor(_expressionTranslator);
_projectionBindingExpressionVisitor = new InMemoryProjectionBindingExpressionVisitor(this, _expressionTranslator);
_model = parentVisitor._model;
}
@@ -465,7 +465,7 @@ private Expression TranslateGroupingKey(Expression expression)
return memberInitExpression.Update(updatedNewExpression, newBindings);
default:
- var translation = _expressionTranslator.Translate(expression);
+ var translation = TranslateExpression(expression);
if (translation == null)
{
return null;
@@ -1204,19 +1204,23 @@ protected override ShapedQueryExpression TranslateWhere(ShapedQueryExpression so
private Expression TranslateExpression(Expression expression, bool preserveType = false)
{
- var result = _expressionTranslator.Translate(expression);
+ var translation = _expressionTranslator.Translate(expression);
+ if (translation == null && _expressionTranslator.TranslationErrorDetails != null)
+ {
+ ProvideTranslationErrorDetails(_expressionTranslator.TranslationErrorDetails);
+ }
if (expression != null
- && result != null
+ && translation != null
&& preserveType
- && expression.Type != result.Type)
+ && expression.Type != translation.Type)
{
- result = expression.Type == typeof(bool)
- ? Expression.Equal(result, Expression.Constant(true, result.Type))
- : (Expression)Expression.Convert(result, expression.Type);
+ translation = expression.Type == typeof(bool)
+ ? Expression.Equal(translation, Expression.Constant(true, translation.Type))
+ : (Expression)Expression.Convert(translation, expression.Type);
}
- return result;
+ return translation;
}
private LambdaExpression TranslateLambdaExpression(
@@ -1254,6 +1258,8 @@ public WeakEntityExpandingExpressionVisitor(InMemoryExpressionTranslatingExpress
_expressionTranslator = expressionTranslator;
}
+ public string TranslationErrorDetails => _expressionTranslator.TranslationErrorDetails;
+
public Expression Expand(InMemoryQueryExpression queryExpression, Expression lambdaBody)
{
_queryExpression = queryExpression;
diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
index e71c2dd7adf..d13df00bb16 100644
--- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
@@ -47,6 +47,7 @@ public RelationalQueryableMethodTranslatingExpressionVisitor(
RelationalDependencies = relationalDependencies;
var sqlExpressionFactory = relationalDependencies.SqlExpressionFactory;
+ _queryCompilationContext = queryCompilationContext;
_model = queryCompilationContext.Model;
_sqlTranslator = relationalDependencies.RelationalSqlTranslatingExpressionVisitorFactory.Create(queryCompilationContext, this);
_weakEntityExpandingExpressionVisitor = new WeakEntityExpandingExpressionVisitor(_sqlTranslator, sqlExpressionFactory);
@@ -70,8 +71,8 @@ protected RelationalQueryableMethodTranslatingExpressionVisitor(
{
RelationalDependencies = parentVisitor.RelationalDependencies;
_queryCompilationContext = parentVisitor._queryCompilationContext;
- _sqlTranslator = parentVisitor._sqlTranslator;
- _weakEntityExpandingExpressionVisitor = parentVisitor._weakEntityExpandingExpressionVisitor;
+ _sqlTranslator = RelationalDependencies.RelationalSqlTranslatingExpressionVisitorFactory.Create(parentVisitor._queryCompilationContext, parentVisitor);
+ _weakEntityExpandingExpressionVisitor = new WeakEntityExpandingExpressionVisitor(_sqlTranslator, parentVisitor._sqlExpressionFactory);
_projectionBindingExpressionVisitor = new RelationalProjectionBindingExpressionVisitor(this, _sqlTranslator);
_sqlExpressionFactory = parentVisitor._sqlExpressionFactory;
_subquery = true;
@@ -98,7 +99,7 @@ protected override Expression VisitExtension(Expression extensionExpression)
var arguments = new List();
foreach (var arg in tableValuedFunctionQueryRootExpression.Arguments)
{
- var sqlArgument = _sqlTranslator.Translate(arg);
+ var sqlArgument = TranslateExpression(arg);
if (sqlArgument == null)
{
var methodCall = Expression.Call(
@@ -106,7 +107,10 @@ protected override Expression VisitExtension(Expression extensionExpression)
function.MethodInfo,
tableValuedFunctionQueryRootExpression.Arguments);
- throw new InvalidOperationException(CoreStrings.TranslationFailed(methodCall.Print()));
+ throw new InvalidOperationException(
+ TranslationErrorDetails == null
+ ? CoreStrings.TranslationFailed(methodCall.Print())
+ : CoreStrings.TranslationFailedWithDetails(methodCall.Print(), TranslationErrorDetails));
}
arguments.Add(sqlArgument);
@@ -476,7 +480,7 @@ private Expression TranslateGroupingKey(Expression expression)
return memberInitExpression.Update(updatedNewExpression, newBindings);
default:
- var translation = _sqlTranslator.Translate(expression);
+ var translation = TranslateExpression(expression);
if (translation == null)
{
return null;
@@ -1139,7 +1143,16 @@ protected override ShapedQueryExpression TranslateWhere(ShapedQueryExpression so
return source;
}
- private SqlExpression TranslateExpression(Expression expression) => _sqlTranslator.Translate(expression);
+ private SqlExpression TranslateExpression(Expression expression)
+ {
+ var translation = _sqlTranslator.Translate(expression);
+ if (translation == null && _sqlTranslator.TranslationErrorDetails != null)
+ {
+ ProvideTranslationErrorDetails(_sqlTranslator.TranslationErrorDetails);
+ }
+
+ return translation;
+ }
private SqlExpression TranslateLambdaExpression(
ShapedQueryExpression shapedQueryExpression, LambdaExpression lambdaExpression)
@@ -1170,6 +1183,8 @@ public WeakEntityExpandingExpressionVisitor(
_sqlExpressionFactory = sqlExpressionFactory;
}
+ public string TranslationErrorDetails => _sqlTranslator.TranslationErrorDetails;
+
public Expression Expand(SelectExpression selectExpression, Expression lambdaBody)
{
_selectExpression = selectExpression;
diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
index 314339ecd7a..bd676b2c124 100644
--- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs
@@ -35,6 +35,11 @@ public class RelationalSqlTranslatingExpressionVisitor : ExpressionVisitor
private static readonly MethodInfo _parameterListValueExtractor =
typeof(RelationalSqlTranslatingExpressionVisitor).GetTypeInfo().GetDeclaredMethod(nameof(ParameterListValueExtractor));
+ private static readonly MethodInfo _stringEqualsWithStringComparison
+ = typeof(string).GetRuntimeMethod(nameof(string.Equals), new[] { typeof(string), typeof(StringComparison) });
+ private static readonly MethodInfo _stringEqualsWithStringComparisonStatic
+ = typeof(string).GetRuntimeMethod(nameof(string.Equals), new[] { typeof(string), typeof(string), typeof(StringComparison) });
+
private readonly QueryCompilationContext _queryCompilationContext;
private readonly IModel _model;
private readonly ISqlExpressionFactory _sqlExpressionFactory;
@@ -64,6 +69,29 @@ public RelationalSqlTranslatingExpressionVisitor(
_sqlTypeMappingVerifyingExpressionVisitor = new SqlTypeMappingVerifyingExpressionVisitor();
}
+ ///
+ /// Detailed information about errors encountered during translation.
+ ///
+ public virtual string TranslationErrorDetails { get; private set; }
+
+ ///
+ /// Provides detailed information about error encountered during translation.
+ ///
+ /// Detailed information about error encountered during translation.
+ protected virtual void ProvideTranslationErrorDetails([NotNull] string details)
+ {
+ Check.NotNull(details, nameof(details));
+
+ if (TranslationErrorDetails == null)
+ {
+ TranslationErrorDetails = details;
+ }
+ else
+ {
+ TranslationErrorDetails += Environment.NewLine + details;
+ }
+ }
+
///
/// Parameter object containing service dependencies.
///
@@ -78,6 +106,13 @@ public virtual SqlExpression Translate([NotNull] Expression expression)
{
Check.NotNull(expression, nameof(expression));
+ TranslationErrorDetails = null;
+
+ return TranslateInternal(expression);
+ }
+
+ private SqlExpression TranslateInternal(Expression expression)
+ {
var result = Visit(expression);
if (result is SqlExpression translation)
@@ -116,12 +151,15 @@ public virtual SqlExpression TranslateAverage([NotNull] Expression expression)
if (!(expression is SqlExpression sqlExpression))
{
- sqlExpression = Translate(expression);
+ sqlExpression = TranslateInternal(expression);
}
if (sqlExpression == null)
{
- throw new InvalidOperationException(CoreStrings.TranslationFailed(expression.Print()));
+ throw new InvalidOperationException(
+ TranslationErrorDetails == null
+ ? CoreStrings.TranslationFailed(expression.Print())
+ : CoreStrings.TranslationFailedWithDetails(expression.Print(), TranslationErrorDetails));
}
var inputType = sqlExpression.Type.UnwrapNullableType();
@@ -206,7 +244,7 @@ public virtual SqlExpression TranslateMax([NotNull] Expression expression)
if (!(expression is SqlExpression sqlExpression))
{
- sqlExpression = Translate(expression);
+ sqlExpression = TranslateInternal(expression);
}
return sqlExpression != null
@@ -231,7 +269,7 @@ public virtual SqlExpression TranslateMin([NotNull] Expression expression)
if (!(expression is SqlExpression sqlExpression))
{
- sqlExpression = Translate(expression);
+ sqlExpression = TranslateInternal(expression);
}
return sqlExpression != null
@@ -256,12 +294,15 @@ public virtual SqlExpression TranslateSum([NotNull] Expression expression)
if (!(expression is SqlExpression sqlExpression))
{
- sqlExpression = Translate(expression);
+ sqlExpression = TranslateInternal(expression);
}
if (sqlExpression == null)
{
- throw new InvalidOperationException(CoreStrings.TranslationFailed(expression.Print()));
+ throw new InvalidOperationException(
+ TranslationErrorDetails == null
+ ? CoreStrings.TranslationFailed(expression.Print())
+ : CoreStrings.TranslationFailedWithDetails(expression.Print(), TranslationErrorDetails));
}
var inputType = sqlExpression.Type.UnwrapNullableType();
@@ -438,7 +479,10 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
if (translatedAggregate == null)
{
- throw new InvalidOperationException(CoreStrings.TranslationFailed(methodCallExpression.Print()));
+ throw new InvalidOperationException(
+ TranslationErrorDetails == null
+ ? CoreStrings.TranslationFailed(methodCallExpression.Print())
+ : CoreStrings.TranslationFailedWithDetails(methodCallExpression.Print(), TranslationErrorDetails));
}
return translatedAggregate;
@@ -618,7 +662,16 @@ static bool IsAggregateResultWithCustomShaper(MethodInfo method)
}
}
- return Dependencies.MethodCallTranslatorProvider.Translate(_model, sqlObject, methodCallExpression.Method, arguments);
+ var translation = Dependencies.MethodCallTranslatorProvider.Translate(_model, sqlObject, methodCallExpression.Method, arguments);
+
+ if (translation == null
+ && (methodCallExpression.Method == _stringEqualsWithStringComparison
+ || methodCallExpression.Method == _stringEqualsWithStringComparisonStatic))
+ {
+ ProvideTranslationErrorDetails(CoreStrings.QueryUnableToTranslateStringEqualsWithStringComparison);
+ }
+
+ return translation;
static Expression RemoveObjectConvert(Expression expression)
=> expression is UnaryExpression unaryExpression
@@ -750,7 +803,17 @@ private Expression TryBindMember(Expression source, MemberIdentity member)
? entityType.FindProperty(member.MemberInfo)
: entityType.FindProperty(member.Name);
- return property != null ? BindProperty(entityReferenceExpression, property) : null;
+ if (property != null)
+ {
+ return BindProperty(entityReferenceExpression, property);
+ }
+
+ ProvideTranslationErrorDetails(
+ CoreStrings.QueryUnableToTranslateMember(
+ member.Name,
+ entityReferenceExpression.EntityType.DisplayName()));
+
+ return null;
}
private SqlExpression BindProperty(EntityReferenceExpression entityReferenceExpression, IProperty property)
diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs
index 95935186f11..a2e9c58c9b8 100644
--- a/src/EFCore/Properties/CoreStrings.Designer.cs
+++ b/src/EFCore/Properties/CoreStrings.Designer.cs
@@ -40,6 +40,14 @@ public static string TranslationFailed([CanBeNull] object expression)
GetString("TranslationFailed", nameof(expression)),
expression);
+ ///
+ /// The LINQ expression '{expression}' could not be translated. Additional information: {details} Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
+ ///
+ public static string TranslationFailedWithDetails([CanBeNull] object expression, [CanBeNull] object details)
+ => string.Format(
+ GetString("TranslationFailedWithDetails", nameof(expression), nameof(details)),
+ expression, details);
+
///
/// Processing of the LINQ expression '{expression}' by '{visitor}' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
///
@@ -2560,6 +2568,20 @@ public static string QueryUnableToTranslateEFProperty([CanBeNull] object express
GetString("QueryUnableToTranslateEFProperty", nameof(expression)),
expression);
+ ///
+ /// Translation of member '{member}' on entity type '{entityType}' failed. Possibly the specified member is not mapped.
+ ///
+ public static string QueryUnableToTranslateMember([CanBeNull] object member, [CanBeNull] object entityType)
+ => string.Format(
+ GetString("QueryUnableToTranslateMember", nameof(member), nameof(entityType)),
+ member, entityType);
+
+ ///
+ /// Translation of 'string.Equals' method which takes 'StringComparison' argument is not supported. See https://go.microsoft.com/fwlink/?linkid=2129535 for more information.
+ ///
+ public static string QueryUnableToTranslateStringEqualsWithStringComparison
+ => GetString("QueryUnableToTranslateStringEqualsWithStringComparison");
+
///
/// Invalid {state} encountered.
///
diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx
index e32292d21df..0072be98d56 100644
--- a/src/EFCore/Properties/CoreStrings.resx
+++ b/src/EFCore/Properties/CoreStrings.resx
@@ -123,6 +123,9 @@
The LINQ expression '{expression}' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
+
+ The LINQ expression '{expression}' could not be translated. Additional information: {details} Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
+
Processing of the LINQ expression '{expression}' by '{visitor}' failed. This may indicate either a bug or a limitation in EF Core. See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.
@@ -1356,6 +1359,12 @@
Translation of '{expression}' failed. Either source is not an entity type or the specified property does not exist on the entity type.
+
+ Translation of member '{member}' on entity type '{entityType}' failed. Possibly the specified member is not mapped.
+
+
+ Translation of 'string.Equals' method which takes 'StringComparison' argument is not supported. See https://go.microsoft.com/fwlink/?linkid=2129535 for more information.
+
Invalid {state} encountered.
diff --git a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs
index e0ecbf9844d..d7c4a640d40 100644
--- a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs
@@ -54,6 +54,29 @@ protected QueryableMethodTranslatingExpressionVisitor(
///
protected virtual QueryableMethodTranslatingExpressionVisitorDependencies Dependencies { get; }
+ ///
+ /// Additional information about errors encountered during translation.
+ ///
+ public virtual string TranslationErrorDetails { get; private set; }
+
+ ///
+ /// Provides additional information about errors encountered during translation.
+ ///
+ /// Error encountered during translation
+ protected virtual void ProvideTranslationErrorDetails([NotNull] string details)
+ {
+ Check.NotNull(details, nameof(details));
+
+ if (TranslationErrorDetails == null)
+ {
+ TranslationErrorDetails = details;
+ }
+ else
+ {
+ TranslationErrorDetails += Environment.NewLine + details;
+ }
+ }
+
///
/// The query compilation context object for current compilation.
///
@@ -79,7 +102,12 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
ShapedQueryExpression CheckTranslated(ShapedQueryExpression translated)
{
- return translated ?? throw new InvalidOperationException(CoreStrings.TranslationFailed(methodCallExpression.Print()));
+ return translated ?? throw new InvalidOperationException(
+ TranslationErrorDetails == null
+ ? CoreStrings.TranslationFailed(methodCallExpression.Print())
+ : CoreStrings.TranslationFailedWithDetails(
+ methodCallExpression.Print(),
+ TranslationErrorDetails));
}
var method = methodCallExpression.Method;
@@ -596,7 +624,14 @@ public virtual ShapedQueryExpression TranslateSubquery([NotNull] Expression expr
{
Check.NotNull(expression, nameof(expression));
- return (ShapedQueryExpression)CreateSubqueryVisitor().Visit(expression);
+ var subqueryVisitor = CreateSubqueryVisitor();
+ var translation = (ShapedQueryExpression)subqueryVisitor.Visit(expression);
+ if (translation == null && subqueryVisitor.TranslationErrorDetails != null)
+ {
+ ProvideTranslationErrorDetails(subqueryVisitor.TranslationErrorDetails);
+ }
+
+ return translation;
}
///
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs
index 361517cf452..110f7ec28b7 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs
@@ -1600,6 +1600,12 @@ public override Task Min_after_default_if_empty_does_not_throw(bool isAsync)
return base.Min_after_default_if_empty_does_not_throw(isAsync);
}
+ [ConditionalTheory(Skip = "Issue#20677")]
+ public override Task Average_with_unmapped_property_access_throws_meaningful_exception(bool async)
+ {
+ return base.Average_with_unmapped_property_access_throws_meaningful_exception(async);
+ }
+
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 8d3059fd816..b4bc8bcec64 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindMiscellaneousQueryCosmosTest.cs
@@ -4125,6 +4125,14 @@ public override Task Perform_identity_resolution_reuses_same_instances_across_jo
return base.Perform_identity_resolution_reuses_same_instances_across_joins(async);
}
+ [ConditionalTheory(Skip = "Issue #17246")]
+ public override Task All_client_and_server_top_level(bool async)
+ => base.All_client_and_server_top_level(async);
+
+ [ConditionalTheory(Skip = "Issue #17246")]
+ public override Task All_client_or_server_top_level(bool async)
+ => base.All_client_or_server_top_level(async);
+
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
diff --git a/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs
index cdca991be52..19e1110272c 100644
--- a/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs
+++ b/test/EFCore.InMemory.FunctionalTests/Query/GearsOfWarQueryInMemoryTest.cs
@@ -17,82 +17,58 @@ public GearsOfWarQueryInMemoryTest(GearsOfWarQueryInMemoryFixture fixture, ITest
[ConditionalTheory(Skip = "issue #17386")]
public override Task Correlated_collection_order_by_constant_null_of_non_mapped_type(bool async)
- {
- return base.Correlated_collection_order_by_constant_null_of_non_mapped_type(async);
- }
+ => base.Correlated_collection_order_by_constant_null_of_non_mapped_type(async);
[ConditionalTheory(Skip = "issue #17386")]
public override Task Client_side_equality_with_parameter_works_with_optional_navigations(bool async)
- {
- return base.Client_side_equality_with_parameter_works_with_optional_navigations(async);
- }
+ => base.Client_side_equality_with_parameter_works_with_optional_navigations(async);
[ConditionalTheory(Skip = "issue #17386")]
public override Task Where_coalesce_with_anonymous_types(bool async)
- {
- return base.Where_coalesce_with_anonymous_types(async);
- }
+ => base.Where_coalesce_with_anonymous_types(async);
[ConditionalTheory(Skip = "issue #17386")]
public override Task Where_conditional_with_anonymous_type(bool async)
- {
- return base.Where_conditional_with_anonymous_type(async);
- }
+ => base.Where_conditional_with_anonymous_type(async);
[ConditionalTheory(Skip = "issue #17386")]
public override Task GetValueOrDefault_on_DateTimeOffset(bool async)
- {
- return base.GetValueOrDefault_on_DateTimeOffset(async);
- }
+ => base.GetValueOrDefault_on_DateTimeOffset(async);
[ConditionalFact(Skip = "issue #17537")]
public override void Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_coalesce_result1()
- {
- base.Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_coalesce_result1();
- }
+ => base.Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_coalesce_result1();
[ConditionalFact(Skip = "issue #17537")]
public override void Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_coalesce_result2()
- {
- base.Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_coalesce_result2();
- }
+ => base.Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_coalesce_result2();
[ConditionalTheory(Skip = "issue #17540")]
- public override Task
- Null_semantics_is_correctly_applied_for_function_comparisons_that_take_arguments_from_optional_navigation_complex(bool async)
- {
- return base.Null_semantics_is_correctly_applied_for_function_comparisons_that_take_arguments_from_optional_navigation_complex(
- async);
- }
+ public override Task Null_semantics_is_correctly_applied_for_function_comparisons_that_take_arguments_from_optional_navigation_complex(bool async)
+ => base.Null_semantics_is_correctly_applied_for_function_comparisons_that_take_arguments_from_optional_navigation_complex(async);
[ConditionalTheory(Skip = "issue #18284")]
public override Task GroupBy_with_boolean_groupin_key_thru_navigation_access(bool async)
- {
- return GroupBy_with_boolean_groupin_key_thru_navigation_access(async);
- }
+ => GroupBy_with_boolean_groupin_key_thru_navigation_access(async);
[ConditionalTheory(Skip = "issue #17620")]
public override Task Select_subquery_projecting_single_constant_inside_anonymous(bool async)
- {
- return base.Select_subquery_projecting_single_constant_inside_anonymous(async);
- }
+ => base.Select_subquery_projecting_single_constant_inside_anonymous(async);
[ConditionalTheory(Skip = "issue #19683")]
public override Task Group_by_on_StartsWith_with_null_parameter_as_argument(bool async)
- {
- return base.Group_by_on_StartsWith_with_null_parameter_as_argument(async);
- }
+ => base.Group_by_on_StartsWith_with_null_parameter_as_argument(async);
[ConditionalTheory(Skip = "issue #18284")]
public override Task Enum_closure_typed_as_underlying_type_generates_correct_parameter_type(bool async)
- {
- return base.Enum_closure_typed_as_underlying_type_generates_correct_parameter_type(async);
- }
+ => base.Enum_closure_typed_as_underlying_type_generates_correct_parameter_type(async);
+
+ [ConditionalTheory(Skip = "issue #17386")]
+ public override Task Client_member_and_unsupported_string_Equals_in_the_same_query(bool async)
+ => base.Client_member_and_unsupported_string_Equals_in_the_same_query(async);
[ConditionalTheory(Skip = "issue #17386")]
public override Task Client_eval_followed_by_set_operation_throws_meaningful_exception(bool async)
- {
- return base.Client_eval_followed_by_set_operation_throws_meaningful_exception(async);
- }
+ => base.Client_eval_followed_by_set_operation_throws_meaningful_exception(async);
}
}
diff --git a/test/EFCore.InMemory.FunctionalTests/Query/NorthwindMiscellaneousQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/NorthwindMiscellaneousQueryInMemoryTest.cs
index 38dc9462e88..fb05af7aae8 100644
--- a/test/EFCore.InMemory.FunctionalTests/Query/NorthwindMiscellaneousQueryInMemoryTest.cs
+++ b/test/EFCore.InMemory.FunctionalTests/Query/NorthwindMiscellaneousQueryInMemoryTest.cs
@@ -22,106 +22,84 @@ public NorthwindMiscellaneousQueryInMemoryTest(
}
public override Task Where_query_composition_entity_equality_one_element_Single(bool async)
- {
- return Assert.ThrowsAsync(
+ => Assert.ThrowsAsync(
() => base.Where_query_composition_entity_equality_one_element_Single(async));
- }
public override Task Where_query_composition_entity_equality_one_element_First(bool async)
- {
- return Assert.ThrowsAsync(
+ => Assert.ThrowsAsync(
() => base.Where_query_composition_entity_equality_one_element_First(async));
- }
public override Task Where_query_composition_entity_equality_no_elements_Single(bool async)
- {
- return Assert.ThrowsAsync(
+ => Assert.ThrowsAsync(
() => base.Where_query_composition_entity_equality_no_elements_Single(async));
- }
public override Task Where_query_composition_entity_equality_no_elements_First(bool async)
- {
- return Assert.ThrowsAsync(
+ => Assert.ThrowsAsync(
() => base.Where_query_composition_entity_equality_no_elements_First(async));
- }
public override Task Where_query_composition_entity_equality_multiple_elements_SingleOrDefault(bool async)
- {
- return Assert.ThrowsAsync(
+ => Assert.ThrowsAsync(
() => base.Where_query_composition_entity_equality_multiple_elements_SingleOrDefault(async));
- }
public override Task Where_query_composition_entity_equality_multiple_elements_Single(bool async)
- {
- return Assert.ThrowsAsync(
+ => Assert.ThrowsAsync(
() => base.Where_query_composition_entity_equality_multiple_elements_Single(async));
- }
// Sending client code to server
[ConditionalFact(Skip = "Issue#17050")]
public override void Client_code_using_instance_in_anonymous_type()
- {
- base.Client_code_using_instance_in_anonymous_type();
- }
+ => base.Client_code_using_instance_in_anonymous_type();
[ConditionalFact(Skip = "Issue#17050")]
public override void Client_code_using_instance_in_static_method()
- {
- base.Client_code_using_instance_in_static_method();
- }
+ => base.Client_code_using_instance_in_static_method();
[ConditionalFact(Skip = "Issue#17050")]
public override void Client_code_using_instance_method_throws()
- {
- base.Client_code_using_instance_method_throws();
- }
+ => base.Client_code_using_instance_method_throws();
[ConditionalTheory(Skip = "Issue#17386")]
public override Task OrderBy_multiple_queries(bool async)
- {
- return base.OrderBy_multiple_queries(async);
- }
+ => base.OrderBy_multiple_queries(async);
[ConditionalTheory(Skip = "Issue#17386")]
public override Task Random_next_is_not_funcletized_1(bool async)
- {
- return base.Random_next_is_not_funcletized_1(async);
- }
+ => base.Random_next_is_not_funcletized_1(async);
[ConditionalTheory(Skip = "Issue#17386")]
public override Task Random_next_is_not_funcletized_2(bool async)
- {
- return base.Random_next_is_not_funcletized_2(async);
- }
+ => base.Random_next_is_not_funcletized_2(async);
[ConditionalTheory(Skip = "Issue#17386")]
public override Task Random_next_is_not_funcletized_3(bool async)
- {
- return base.Random_next_is_not_funcletized_3(async);
- }
+ => base.Random_next_is_not_funcletized_3(async);
[ConditionalTheory(Skip = "Issue#17386")]
public override Task Random_next_is_not_funcletized_4(bool async)
- {
- return base.Random_next_is_not_funcletized_4(async);
- }
+ => base.Random_next_is_not_funcletized_4(async);
[ConditionalTheory(Skip = "Issue#17386")]
public override Task Random_next_is_not_funcletized_5(bool async)
- {
- return base.Random_next_is_not_funcletized_5(async);
- }
+ => base.Random_next_is_not_funcletized_5(async);
[ConditionalTheory(Skip = "Issue#17386")]
public override Task Random_next_is_not_funcletized_6(bool async)
- {
- return base.Random_next_is_not_funcletized_6(async);
- }
+ => base.Random_next_is_not_funcletized_6(async);
- [ConditionalTheory]
- public override Task DefaultIfEmpty_in_subquery_nested(bool async)
- {
- return base.DefaultIfEmpty_in_subquery_nested(async);
- }
+ [ConditionalTheory(Skip = "issue#17386")]
+ public override Task Where_query_composition5(bool async)
+ => base.Where_query_composition5(async);
+
+ [ConditionalTheory(Skip = "issue#17386")]
+ public override Task Where_query_composition6(bool async)
+ => base.Where_query_composition6(async);
+
+ [ConditionalTheory(Skip = "issue #17386")]
+ public override Task Using_string_Equals_with_StringComparison_throws_informative_error(bool async)
+ => base.Using_string_Equals_with_StringComparison_throws_informative_error(async);
+
+ [ConditionalTheory(Skip = "issue #17386")]
+ public override Task Using_static_string_Equals_with_StringComparison_throws_informative_error(bool async)
+ => base.Using_static_string_Equals_with_StringComparison_throws_informative_error(async);
}
}
diff --git a/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs
index 6c10a8500b6..8d7cbfe2a2d 100644
--- a/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/Query/QueryNoClientEvalTestBase.cs
@@ -23,25 +23,30 @@ public abstract class QueryNoClientEvalTestBase : IClassFixture context.Customers.Where(c => c.IsLondon).ToList());
+ AssertTranslationFailedWithDetails(
+ () => context.Customers.Where(c => c.IsLondon).ToList(),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalFact]
public virtual void Throws_when_orderby()
{
using var context = CreateContext();
- AssertTranslationFailed(() => context.Customers.OrderBy(c => c.IsLondon).ToList());
+ AssertTranslationFailedWithDetails(
+ () => context.Customers.OrderBy(c => c.IsLondon).ToList(),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalFact]
public virtual void Throws_when_orderby_multiple()
{
using var context = CreateContext();
- AssertTranslationFailed(
+ AssertTranslationFailedWithDetails(
() => context.Customers
.OrderBy(c => c.IsLondon)
.ThenBy(c => ClientMethod(c))
- .ToList());
+ .ToList(),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
private static object ClientMethod(object o) => o.GetHashCode();
@@ -50,28 +55,32 @@ public virtual void Throws_when_orderby_multiple()
public virtual void Throws_when_where_subquery_correlated()
{
using var context = CreateContext();
- AssertTranslationFailed(
+ AssertTranslationFailedWithDetails(
() => context.Customers
.Where(c1 => context.Customers.Any(c2 => c1.CustomerID == c2.CustomerID && c2.IsLondon))
- .ToList());
+ .ToList(),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalFact]
public virtual void Throws_when_all()
{
using var context = CreateContext();
- AssertTranslationFailed(() => context.Customers.All(c => c.IsLondon));
+ AssertTranslationFailedWithDetails(
+ () => context.Customers.All(c => c.IsLondon),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalFact]
public virtual void Throws_when_from_sql_composed()
{
using var context = CreateContext();
- AssertTranslationFailed(
+ AssertTranslationFailedWithDetails(
() => context.Customers
.FromSqlRaw(NormalizeDelimitersInRawString("select * from [Customers]"))
.Where(c => c.IsLondon)
- .ToList());
+ .ToList(),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalFact]
@@ -90,13 +99,14 @@ var customers
public virtual void Throws_when_subquery_main_from_clause()
{
using var context = CreateContext();
- AssertTranslationFailed(
+ AssertTranslationFailedWithDetails(
() => (from c1 in context.Customers
.Where(c => c.IsLondon)
.OrderBy(c => c.CustomerID)
.Take(5)
select c1)
- .ToList());
+ .ToList(),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalFact]
@@ -151,28 +161,36 @@ public virtual void Throws_when_group_by()
public virtual void Throws_when_first()
{
using var context = CreateContext();
- AssertTranslationFailed(() => context.Customers.First(c => c.IsLondon));
+ AssertTranslationFailedWithDetails(
+ () => context.Customers.First(c => c.IsLondon),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalFact]
public virtual void Throws_when_single()
{
using var context = CreateContext();
- AssertTranslationFailed(() => context.Customers.Single(c => c.IsLondon));
+ AssertTranslationFailedWithDetails(
+ () => context.Customers.Single(c => c.IsLondon),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalFact]
public virtual void Throws_when_first_or_default()
{
using var context = CreateContext();
- AssertTranslationFailed(() => context.Customers.FirstOrDefault(c => c.IsLondon));
+ AssertTranslationFailedWithDetails(
+ () => context.Customers.FirstOrDefault(c => c.IsLondon),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalFact]
public virtual void Throws_when_single_or_default()
{
using var context = CreateContext();
- AssertTranslationFailed(() => context.Customers.SingleOrDefault(c => c.IsLondon));
+ AssertTranslationFailedWithDetails(
+ () => context.Customers.SingleOrDefault(c => c.IsLondon),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
private string NormalizeDelimitersInRawString(string sql)
@@ -183,6 +201,11 @@ private void AssertTranslationFailed(Action testCode)
CoreStrings.TranslationFailed("").Substring(21),
Assert.Throws(testCode).Message);
+ private void AssertTranslationFailedWithDetails(Action testCode, string details)
+ => Assert.Contains(
+ CoreStrings.TranslationFailedWithDetails("", details).Substring(21),
+ Assert.Throws(testCode).Message);
+
protected NorthwindContext CreateContext() => Fixture.CreateContext();
}
}
diff --git a/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs b/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs
index e99e7d226bc..b9fa3db8fb6 100644
--- a/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs
+++ b/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs
@@ -6,6 +6,7 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.ChangeTracking;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Xunit;
@@ -581,7 +582,8 @@ public virtual void Collection_property_as_scalar_Count_member()
{
using var context = CreateContext();
Assert.Equal(
- @"The LINQ expression 'DbSet() .Where(c => c.Tags.Count == 2)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.",
+ CoreStrings.TranslationFailed(
+ @"DbSet() .Where(c => c.Tags.Count == 2)"),
Assert.Throws(
() => context.Set().Where(e => e.Tags.Count == 2).ToList())
.Message.Replace("\r", "").Replace("\n", ""));
diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
index 6e68b173a31..331c19634ed 100644
--- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
@@ -1563,12 +1563,13 @@ public virtual Task Select_subquery_distinct_firstordefault(bool async)
[MemberData(nameof(IsAsyncData))]
public virtual Task Select_Where_Navigation_Client(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => from t in ss.Set()
where t.Gear != null && t.Gear.IsMarcus
- select t));
+ select t),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Gear.IsMarcus), nameof(Gear)));
}
[ConditionalTheory]
@@ -7506,6 +7507,85 @@ await AssertTranslationFailed(
ss => ss.Set().Select(m => m.Duration.Ticks)));
}
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Trying_to_access_unmapped_property_throws_informative_error(bool async)
+ {
+ return AssertTranslationFailedWithDetails(
+ () => AssertQuery(
+ async,
+ ss => ss.Set().Where(g => g.IsMarcus)),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Gear.IsMarcus), nameof(Gear)));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Trying_to_access_unmapped_property_in_projection(bool async)
+ {
+ return AssertQueryScalar(
+ async,
+ ss => ss.Set().Select(g => g.IsMarcus));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Trying_to_access_unmapped_property_inside_aggregate(bool async)
+ {
+ return AssertTranslationFailedWithDetails(
+ () => AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.BornGears.Count(g => g.IsMarcus) > 0)),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Gear.IsMarcus), nameof(Gear)));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Trying_to_access_unmapped_property_inside_subquery(bool async)
+ {
+ return AssertTranslationFailedWithDetails(
+ () => AssertQuery(
+ async,
+ ss => ss.Set().Where(c => ss.Set().Where(g => g.IsMarcus).Select(g => g.Nickname).FirstOrDefault() == "Marcus")),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Gear.IsMarcus), nameof(Gear)));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Trying_to_access_unmapped_property_inside_join_key_selector(bool async)
+ {
+ return AssertTranslationFailedWithDetails(
+ () => AssertQuery(
+ async,
+ ss => from w in ss.Set()
+ join g in ss.Set() on w.IsAutomatic equals g.IsMarcus into grouping
+ from g in grouping.DefaultIfEmpty()
+ select new { w, g }),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Gear.IsMarcus), nameof(Gear)));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Client_projection_with_nested_unmapped_property_bubbles_up_translation_failure_info(bool async)
+ {
+ return AssertTranslationFailedWithDetails(
+ () => AssertQuery(
+ async,
+ ss => ss.Set().Select(g => new { nested = ss.Set().Where(gg => gg.IsMarcus).ToList() })),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Gear.IsMarcus), nameof(Gear)));
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Client_member_and_unsupported_string_Equals_in_the_same_query(bool async)
+ {
+ return AssertTranslationFailedWithDetails(
+ () => AssertQuery(
+ async,
+ ss => ss.Set().Where(g => g.FullName.Equals(g.Nickname, StringComparison.InvariantCulture) || g.IsMarcus)),
+ CoreStrings.QueryUnableToTranslateStringEqualsWithStringComparison
+ + Environment.NewLine + CoreStrings.QueryUnableToTranslateMember(nameof(Gear.IsMarcus), nameof(Gear)));
+ }
+
protected GearsOfWarContext CreateContext() => Fixture.CreateContext();
protected virtual void ClearLog()
diff --git a/test/EFCore.Specification.Tests/Query/NorthwindAggregateOperatorsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindAggregateOperatorsQueryTestBase.cs
index 646edc41142..a157786cad4 100644
--- a/test/EFCore.Specification.Tests/Query/NorthwindAggregateOperatorsQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/NorthwindAggregateOperatorsQueryTestBase.cs
@@ -6,6 +6,7 @@
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;
@@ -1889,6 +1890,18 @@ await AssertCount(
ss => ss.Set().Select(o => new { Id = CodeFormat(o.OrderID) }));
}
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Average_with_unmapped_property_access_throws_meaningful_exception(bool async)
+ {
+ return AssertTranslationFailedWithDetails(
+ () => AssertAverage(
+ async,
+ ss => ss.Set(),
+ selector: c => c.ShipVia),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Order.ShipVia), nameof(Order)));
+ }
+
private static string CodeFormat(int str) => str.ToString();
[ConditionalTheory]
diff --git a/test/EFCore.Specification.Tests/Query/NorthwindIncludeAsyncQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindIncludeAsyncQueryTestBase.cs
index 605dd6d0be1..12fde7e2aa4 100644
--- a/test/EFCore.Specification.Tests/Query/NorthwindIncludeAsyncQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/NorthwindIncludeAsyncQueryTestBase.cs
@@ -567,7 +567,9 @@ public virtual async Task Include_collection_with_client_filter()
{
using var context = CreateContext();
Assert.Contains(
- CoreStrings.TranslationFailed("").Substring(21),
+ CoreStrings.TranslationFailedWithDetails(
+ "",
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer))).Substring(21),
(await Assert.ThrowsAsync(
() => context.Set()
.Include(c => c.Orders)
diff --git a/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs
index 01f12db1bbb..312de559ef9 100644
--- a/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/NorthwindIncludeQueryTestBase.cs
@@ -1812,7 +1812,9 @@ public virtual void Include_collection_with_client_filter(bool useString)
{
using var context = CreateContext();
Assert.Contains(
- CoreStrings.TranslationFailed("").Substring(21),
+ CoreStrings.TranslationFailedWithDetails(
+ "",
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer))).Substring(21),
Assert.Throws(
() => useString
? context.Set()
diff --git a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs
index a93cd8a45a3..bb2ed89ab58 100644
--- a/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/NorthwindMiscellaneousQueryTestBase.cs
@@ -559,11 +559,12 @@ public virtual Task Queryable_simple_anonymous_subquery(bool async)
[MemberData(nameof(IsAsyncData))]
public virtual Task Queryable_reprojection(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => ss.Set().Where(c => c.IsLondon)
- .Select(c => new Customer { CustomerID = "Foo", City = c.City })));
+ .Select(c => new Customer { CustomerID = "Foo", City = c.City })),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
@@ -1140,33 +1141,36 @@ public virtual Task All_top_level_subquery_ef_property(bool async)
[MemberData(nameof(IsAsyncData))]
public virtual Task All_client(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertAll(
async,
ss => ss.Set(),
- predicate: c => c.IsLondon));
+ predicate: c => c.IsLondon),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task All_client_and_server_top_level(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertAll(
async,
ss => ss.Set(),
- predicate: c => c.CustomerID != "Foo" && c.IsLondon));
+ predicate: c => c.CustomerID != "Foo" && c.IsLondon),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task All_client_or_server_top_level(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertAll(
async,
ss => ss.Set(),
- predicate: c => c.CustomerID != "Foo" || c.IsLondon));
+ predicate: c => c.CustomerID != "Foo" || c.IsLondon),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
@@ -1207,12 +1211,13 @@ public virtual Task Cast_results_to_object(bool async)
[MemberData(nameof(IsAsyncData))]
public virtual Task First_client_predicate(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertFirst(
async,
ss => ss.Set().OrderBy(c => c.CustomerID),
predicate: c => c.IsLondon,
- entryCount: 1));
+ entryCount: 1),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
@@ -1935,20 +1940,21 @@ where e1.FirstName
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_query_composition3(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => from c1 in ss.Set()
where c1.City == ss.Set().OrderBy(c => c.CustomerID).First(c => c.IsLondon).City
select c1,
- entryCount: 6));
+ entryCount: 6),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_query_composition4(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => from c1 in ss.Set().OrderBy(c => c.CustomerID).Take(2)
@@ -1957,27 +1963,29 @@ where c1.City
from c3 in ss.Set().OrderBy(c => c.IsLondon).ThenBy(c => c.CustomerID)
select new { c3 }).First().c3.City
select c1,
- entryCount: 1));
+ entryCount: 1),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_query_composition5(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => from c1 in ss.Set()
where c1.IsLondon == ss.Set().OrderBy(c => c.CustomerID).First().IsLondon
select c1,
- entryCount: 85));
+ entryCount: 85),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_query_composition6(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => from c1 in ss.Set()
@@ -1986,7 +1994,8 @@ where c1.IsLondon
.Select(c => new { Foo = c })
.First().Foo.IsLondon
select c1,
- entryCount: 85));
+ entryCount: 85),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
@@ -2614,12 +2623,13 @@ public virtual Task OrderBy_anon2(bool async)
[MemberData(nameof(IsAsyncData))]
public virtual Task OrderBy_client_mixed(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => ss.Set().OrderBy(c => c.IsLondon).ThenBy(c => c.CompanyName),
assertOrder: true,
- entryCount: 91));
+ entryCount: 91),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
@@ -5919,5 +5929,27 @@ on c.CustomerID equals o.CustomerID
Assert.All(arouts, t => Assert.Same(firstArout, t));
Assert.Empty(context.ChangeTracker.Entries());
}
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Using_string_Equals_with_StringComparison_throws_informative_error(bool async)
+ {
+ return AssertTranslationFailedWithDetails(
+ () => AssertQuery(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.Equals("ALFKI", StringComparison.InvariantCulture))),
+ CoreStrings.QueryUnableToTranslateStringEqualsWithStringComparison);
+ }
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Using_static_string_Equals_with_StringComparison_throws_informative_error(bool async)
+ {
+ return AssertTranslationFailedWithDetails(
+ () => AssertQuery(
+ async,
+ ss => ss.Set().Where(c => string.Equals(c.CustomerID, "ALFKI", StringComparison.InvariantCulture))),
+ CoreStrings.QueryUnableToTranslateStringEqualsWithStringComparison);
+ }
}
}
diff --git a/test/EFCore.Specification.Tests/Query/NorthwindNavigationsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindNavigationsQueryTestBase.cs
index 238270169ff..4ff9e169cf6 100644
--- a/test/EFCore.Specification.Tests/Query/NorthwindNavigationsQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/NorthwindNavigationsQueryTestBase.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;
@@ -146,13 +147,14 @@ from o2 in ss.Set().Where(o => o.OrderID < 10400)
[MemberData(nameof(IsAsyncData))]
public virtual Task Select_Where_Navigation_Client(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => from o in ss.Set()
where o.Customer.IsLondon
select o,
- entryCount: 46));
+ entryCount: 46),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
@@ -603,7 +605,7 @@ public virtual Task Collection_select_nav_prop_all(bool async)
[MemberData(nameof(IsAsyncData))]
public virtual Task Collection_select_nav_prop_all_client(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => from c in ss.Set()
@@ -612,7 +614,8 @@ orderby c.CustomerID
ss => from c in ss.Set()
orderby c.CustomerID
select new { All = (c.Orders ?? new List()).All(o => false) },
- assertOrder: true));
+ assertOrder: true),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Order.ShipCity), nameof(Order)));
}
[ConditionalTheory]
@@ -634,13 +637,14 @@ where c.Orders.All(o => o.CustomerID == "ALFKI")
[MemberData(nameof(IsAsyncData))]
public virtual Task Collection_where_nav_prop_all_client(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => from c in ss.Set()
orderby c.CustomerID
where c.Orders.All(o => o.ShipCity == "London")
- select c));
+ select c),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Order.ShipCity), nameof(Order)));
}
[ConditionalTheory]
diff --git a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs
index 7029721de67..3b33eb28537 100644
--- a/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/NorthwindWhereQueryTestBase.cs
@@ -6,6 +6,7 @@
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;
@@ -591,11 +592,12 @@ where EF.Property(e, "Title")
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_client(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => ss.Set().Where(c => c.IsLondon),
- entryCount: 6));
+ entryCount: 6),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
@@ -612,59 +614,64 @@ public virtual Task Where_subquery_correlated(bool async)
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_subquery_correlated_client_eval(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => ss.Set()
.OrderBy(c1 => c1.CustomerID)
.Take(5)
.Where(c1 => ss.Set().Any(c2 => c1.CustomerID == c2.CustomerID && c2.IsLondon)),
- entryCount: 1));
+ entryCount: 1),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_client_and_server_top_level(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => ss.Set().Where(c => c.IsLondon && c.CustomerID != "AROUT"),
- entryCount: 5));
+ entryCount: 5),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_client_or_server_top_level(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => ss.Set().Where(c => c.IsLondon || c.CustomerID == "ALFKI"),
- entryCount: 7));
+ entryCount: 7),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_client_and_server_non_top_level(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => ss.Set().Where(c => c.CustomerID != "ALFKI" == (c.IsLondon && c.CustomerID != "AROUT")),
- entryCount: 6));
+ entryCount: 6),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_client_deep_inside_predicate_and_server_top_level(bool async)
{
- return AssertTranslationFailed(
+ return AssertTranslationFailedWithDetails(
() => AssertQuery(
async,
ss => ss.Set()
.Where(c => c.CustomerID != "ALFKI" && (c.CustomerID == "MAUMAR" || (c.CustomerID != "AROUT" && c.IsLondon))),
- entryCount: 5));
+ entryCount: 5),
+ CoreStrings.QueryUnableToTranslateMember(nameof(Customer.IsLondon), nameof(Customer)));
}
[ConditionalTheory]
diff --git a/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs
index f117d106de6..a53e4d1cce6 100644
--- a/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/OwnedQueryTestBase.cs
@@ -785,6 +785,17 @@ public virtual async Task NoTracking_Include_with_cycles_does_not_throw_when_per
}
}
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Trying_to_access_non_existent_indexer_property_throws_meaningful_exception(bool async)
+ {
+ return AssertTranslationFailedWithDetails(
+ () => AssertQuery(
+ async,
+ ss => ss.Set().Where(op => (bool)op["Foo"])),
+ CoreStrings.QueryUnableToTranslateMember("Foo", nameof(OwnedPerson)));
+ }
+
protected virtual DbContext CreateContext() => Fixture.CreateContext();
public abstract class OwnedQueryFixtureBase : SharedStoreFixtureBase, IQueryFixtureBase
diff --git a/test/EFCore.Specification.Tests/Query/QueryTestBase.cs b/test/EFCore.Specification.Tests/Query/QueryTestBase.cs
index 5fa2f444eee..fe62972594d 100644
--- a/test/EFCore.Specification.Tests/Query/QueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/QueryTestBase.cs
@@ -1153,6 +1153,12 @@ protected static async Task AssertTranslationFailed(Func query)
(await Assert.ThrowsAsync(query))
.Message);
+ protected static async Task AssertTranslationFailedWithDetails(Func query, string details)
+ => Assert.Contains(
+ CoreStrings.TranslationFailedWithDetails("", details).Substring(21),
+ (await Assert.ThrowsAsync(query))
+ .Message);
+
#endregion
}
}
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs
index 42dd81259b2..22e17cfd533 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs
@@ -6723,8 +6723,9 @@ public void Cast_to_non_implemented_interface_is_not_removed_from_expression_tre
() => queryBase.Cast().FirstOrDefault(x => x.Id == id)).Message;
Assert.Equal(
- CoreStrings.TranslationFailed(@"DbSet() .Cast() .Where(e => e.Id == __id_0)"),
- message.Replace("\r", "").Replace("\n", ""));
+ CoreStrings.TranslationFailed(
+ @"DbSet() .Cast() .Where(e => e.Id == __id_0)"),
+ message.Replace("\r", "").Replace("\n", ""));
}
private SqlServerTestStore CreateDatabase18087()