diff --git a/src/EFCore.Relational/Query/IFromSqlQueryable.cs b/src/EFCore.Relational/Query/IFromSqlQueryable.cs new file mode 100644 index 00000000000..f26b266a735 --- /dev/null +++ b/src/EFCore.Relational/Query/IFromSqlQueryable.cs @@ -0,0 +1,23 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq.Expressions; + +namespace Microsoft.EntityFrameworkCore.Query +{ + /// + /// An interface to identify FromSql query roots in LINQ. + /// + public interface IFromSqlQueryable : IEntityQueryable + { + /// + /// Return Sql used to get data for this query root. + /// + string Sql { get; } + + /// + /// Return arguments for the Sql. + /// + Expression Argument { get; } + } +} diff --git a/src/EFCore.Relational/Query/Internal/CustomQueryableInjectingExpressionVisitor.cs b/src/EFCore.Relational/Query/Internal/CustomQueryableInjectingExpressionVisitor.cs new file mode 100644 index 00000000000..0750ff80aca --- /dev/null +++ b/src/EFCore.Relational/Query/Internal/CustomQueryableInjectingExpressionVisitor.cs @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq.Expressions; +using System.Reflection; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Metadata; + +namespace Microsoft.EntityFrameworkCore.Query.Internal +{ + public class CustomQueryableInjectingExpressionVisitor : ExpressionVisitor + { + protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) + { + if (methodCallExpression.Method.DeclaringType == typeof(RelationalQueryableExtensions) + && methodCallExpression.Method.Name == nameof(RelationalQueryableExtensions.FromSqlOnQueryable)) + { + var sql = (string)((ConstantExpression)methodCallExpression.Arguments[1]).Value; + var entityType = ((IEntityQueryable)((ConstantExpression)methodCallExpression.Arguments[0]).Value).EntityType; + + return CreateFromSqlQueryableExpression(entityType, sql, methodCallExpression.Arguments[2]); + } + + return base.VisitMethodCall(methodCallExpression); + } + + private static ConstantExpression CreateFromSqlQueryableExpression(IEntityType entityType, string sql, Expression argument) + { + return Expression.Constant( + _createFromSqlQueryableMethod + .MakeGenericMethod(entityType.ClrType) + .Invoke( + null, new object[] { NullAsyncQueryProvider.Instance, entityType, sql, argument })); + } + + private static readonly MethodInfo _createFromSqlQueryableMethod + = typeof(CustomQueryableInjectingExpressionVisitor) + .GetTypeInfo().GetDeclaredMethod(nameof(CreateFromSqlQueryable)); + + [UsedImplicitly] + private static FromSqlQueryable CreateFromSqlQueryable( + IAsyncQueryProvider entityQueryProvider, IEntityType entityType, string sql, Expression argument) + => new FromSqlQueryable(entityQueryProvider, entityType, sql, argument); + } +} diff --git a/src/EFCore.Relational/Query/Internal/FromSqlQueryable`.cs b/src/EFCore.Relational/Query/Internal/FromSqlQueryable`.cs new file mode 100644 index 00000000000..2c47e25430c --- /dev/null +++ b/src/EFCore.Relational/Query/Internal/FromSqlQueryable`.cs @@ -0,0 +1,63 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Utilities; + +namespace Microsoft.EntityFrameworkCore.Query.Internal +{ + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public class FromSqlQueryable : EntityQueryable, IFromSqlQueryable + { + /// + /// 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 FromSqlQueryable( + [NotNull] IAsyncQueryProvider queryProvider, [NotNull] IEntityType entityType, + [NotNull] string sql, [NotNull] Expression argument) + : base(queryProvider, entityType) + { + Check.NotEmpty(sql, nameof(sql)); + Check.NotNull(argument, nameof(argument)); + + Sql = sql; + Argument = argument; + } + + /// + /// 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 Sql { get; } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual Expression Argument { get; } + + /// + /// 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 override string ToString() => $"{base.ToString()}.FromSql(\"{Sql}\", {Argument.Print()}"; + } +} diff --git a/src/EFCore.Relational/Query/RelationalQueryTranslationPreprocessor.cs b/src/EFCore.Relational/Query/RelationalQueryTranslationPreprocessor.cs index f0f441f5fcf..1130314bbe0 100644 --- a/src/EFCore.Relational/Query/RelationalQueryTranslationPreprocessor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryTranslationPreprocessor.cs @@ -1,7 +1,9 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Linq.Expressions; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query @@ -21,5 +23,12 @@ public RelationalQueryTranslationPreprocessor( } protected virtual RelationalQueryTranslationPreprocessorDependencies RelationalDependencies { get; } + + public override Expression NormalizeQueryableMethodCall(Expression expression) + { + expression = new CustomQueryableInjectingExpressionVisitor().Visit(expression); + + return base.NormalizeQueryableMethodCall(expression); + } } } diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index a28a30726db..6d9b90c03ef 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -63,21 +63,18 @@ protected RelationalQueryableMethodTranslatingExpressionVisitor( _subquery = true; } + protected override Expression VisitConstant(ConstantExpression constantExpression) + { + return constantExpression.Value is IFromSqlQueryable fromSqlQueryable + ? CreateShapedQueryExpression(fromSqlQueryable) + : base.VisitConstant(constantExpression); + } + protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { Check.NotNull(methodCallExpression, nameof(methodCallExpression)); - if (methodCallExpression.Method.DeclaringType == typeof(RelationalQueryableExtensions) - && methodCallExpression.Method.Name == nameof(RelationalQueryableExtensions.FromSqlOnQueryable)) - { - var sql = (string)((ConstantExpression)methodCallExpression.Arguments[1]).Value; - var queryable = (IEntityQueryable)((ConstantExpression)methodCallExpression.Arguments[0]).Value; - - return CreateShapedQueryExpression( - queryable.EntityType, _sqlExpressionFactory.Select(queryable.EntityType, sql, methodCallExpression.Arguments[2])); - } - - var dbFunction = this._model.FindDbFunction(methodCallExpression.Method); + var dbFunction = _model.FindDbFunction(methodCallExpression.Method); if (dbFunction != null && dbFunction.IsIQueryable) { return CreateShapedQueryExpression(methodCallExpression); @@ -94,13 +91,13 @@ protected virtual ShapedQueryExpression CreateShapedQueryExpression([NotNull] Me var sqlFuncExpression = _sqlTranslator.TranslateMethodCall(methodCallExpression) as SqlFunctionExpression; var elementType = methodCallExpression.Method.ReturnType.GetGenericArguments()[0]; - var entityType =_model.FindEntityType(elementType); + var entityType = _model.FindEntityType(elementType); var queryExpression = _sqlExpressionFactory.Select(entityType, sqlFuncExpression); return CreateShapedQueryExpression(entityType, queryExpression); } - [Obsolete("Use overload which takes IEntityType.")] + [Obsolete("Use overload which takes IEntityType.")] protected override ShapedQueryExpression CreateShapedQueryExpression(Type elementType) { Check.NotNull(elementType, nameof(elementType)); @@ -118,6 +115,15 @@ protected override ShapedQueryExpression CreateShapedQueryExpression(IEntityType return CreateShapedQueryExpression(entityType, _sqlExpressionFactory.Select(entityType)); } + private ShapedQueryExpression CreateShapedQueryExpression(IFromSqlQueryable fromSqlQueryable) + { + Check.NotNull(fromSqlQueryable, nameof(fromSqlQueryable)); + + return CreateShapedQueryExpression( + fromSqlQueryable.EntityType, + _sqlExpressionFactory.Select(fromSqlQueryable.EntityType, fromSqlQueryable.Sql, fromSqlQueryable.Argument)); + } + private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType, SelectExpression selectExpression) => new ShapedQueryExpression( selectExpression, diff --git a/src/EFCore/Extensions/Internal/ExpressionExtensions.cs b/src/EFCore/Extensions/Internal/ExpressionExtensions.cs index ce20e8cbeee..59541c2e9cf 100644 --- a/src/EFCore/Extensions/Internal/ExpressionExtensions.cs +++ b/src/EFCore/Extensions/Internal/ExpressionExtensions.cs @@ -8,6 +8,7 @@ using System.Linq.Expressions; using System.Reflection; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Query; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Utilities; @@ -138,9 +139,9 @@ public static bool IsLogicalOperation([NotNull] this Expression expression) /// 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. /// + [Obsolete("Use constantExpression.Value is IEntityQueryable instead.")] public static bool IsEntityQueryable([NotNull] this ConstantExpression constantExpression) - => constantExpression.Type.IsGenericType - && constantExpression.Type.GetGenericTypeDefinition() == typeof(EntityQueryable<>); + => constantExpression.Value is IEntityQueryable; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Metadata/Internal/TypeBase.cs b/src/EFCore/Metadata/Internal/TypeBase.cs index bda279cc8f3..b9d35666415 100644 --- a/src/EFCore/Metadata/Internal/TypeBase.cs +++ b/src/EFCore/Metadata/Internal/TypeBase.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Reflection; using System.Threading; using JetBrains.Annotations; diff --git a/src/EFCore/Query/ExpressionPrinter.cs b/src/EFCore/Query/ExpressionPrinter.cs index ed0110f1aca..0a755c64e7d 100644 --- a/src/EFCore/Query/ExpressionPrinter.cs +++ b/src/EFCore/Query/ExpressionPrinter.cs @@ -376,9 +376,9 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio { printable.Print(this); } - else if (constantExpression.IsEntityQueryable()) + else if (constantExpression.Value is IEntityQueryable entityQueryable) { - _stringBuilder.Append($"DbSet<{constantExpression.Type.GenericTypeArguments.First().ShortDisplayName()}>"); + _stringBuilder.Append(entityQueryable.ToString()); } else { diff --git a/src/EFCore/Query/Internal/EntityEqualityRewritingExpressionVisitor.cs b/src/EFCore/Query/Internal/EntityEqualityRewritingExpressionVisitor.cs index a423fb93351..42a52abce6c 100644 --- a/src/EFCore/Query/Internal/EntityEqualityRewritingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/EntityEqualityRewritingExpressionVisitor.cs @@ -55,10 +55,8 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio { Check.NotNull(constantExpression, nameof(constantExpression)); - return constantExpression.IsEntityQueryable() - ? new EntityReferenceExpression( - constantExpression, - ((IEntityQueryable)constantExpression.Value).EntityType) + return constantExpression.Value is IEntityQueryable entityQueryable + ? new EntityReferenceExpression(constantExpression, entityQueryable.EntityType) : (Expression)constantExpression; } diff --git a/src/EFCore/Query/Internal/EntityQueryable`.cs b/src/EFCore/Query/Internal/EntityQueryable`.cs index dab658ae9a5..02d1fb36df2 100644 --- a/src/EFCore/Query/Internal/EntityQueryable`.cs +++ b/src/EFCore/Query/Internal/EntityQueryable`.cs @@ -156,5 +156,38 @@ IList IListSource.GetList() /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual QueryDebugView DebugView => new QueryDebugView(() => Expression.Print(), this.ToQueryString); + + /// + /// 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 override bool Equals(object obj) + => obj != null + && (ReferenceEquals(this, obj) + || obj is IEntityQueryable entityQueryable + && entityQueryable.EntityType == _entityType); + + /// + /// 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 override int GetHashCode() => _entityType?.GetHashCode() ?? 0; + + /// + /// 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 override string ToString() + => _entityType != null + ? (_entityType.IsSharedType + ? $"DbSet<{_entityType.ClrType.ShortDisplayName()}>(\"{_entityType.Name}\")" + : $"DbSet<{_entityType.ClrType.ShortDisplayName()}>()") + : base.ToString(); } } diff --git a/src/EFCore/Query/Internal/ExpressionEqualityComparer.cs b/src/EFCore/Query/Internal/ExpressionEqualityComparer.cs index b12e20879c7..4d419bebe18 100644 --- a/src/EFCore/Query/Internal/ExpressionEqualityComparer.cs +++ b/src/EFCore/Query/Internal/ExpressionEqualityComparer.cs @@ -421,8 +421,7 @@ private static bool CompareConstant(ConstantExpression a, ConstantExpression b) => a.Value == b.Value || (a.Value != null && b.Value != null - && (a.IsEntityQueryable() && b.IsEntityQueryable() && a.Value.GetType() == b.Value.GetType() - || Equals(a.Value, b.Value))); + && (Equals(a.Value, b.Value))); private bool CompareGoto(GotoExpression a, GotoExpression b) => a.Kind == b.Kind diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index b39934c83e6..5df7bf34a6d 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -682,12 +682,11 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio { Check.NotNull(constantExpression, nameof(constantExpression)); - if (constantExpression.IsEntityQueryable()) + if (constantExpression.Value is IEntityQueryable entityQueryable) { - var entityType = ((IEntityQueryable)constantExpression.Value).EntityType; - if (entityType == _entityType) + if (entityQueryable.EntityType == _entityType) { - return _navigationExpandingExpressionVisitor.CreateNavigationExpansionExpression(constantExpression, entityType); + return _navigationExpandingExpressionVisitor.CreateNavigationExpansionExpression(constantExpression, _entityType); } } diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index f5867dadc98..f94d7f6294a 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -108,12 +108,16 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio { Check.NotNull(constantExpression, nameof(constantExpression)); - if (constantExpression.IsEntityQueryable()) + if (constantExpression.Value is IEntityQueryable entityQueryable) { - var entityType = ((IEntityQueryable)constantExpression.Value).EntityType; - var definingQuery = entityType.GetDefiningQuery(); - NavigationExpansionExpression navigationExpansionExpression; - if (definingQuery != null) + var entityType = entityQueryable.EntityType; + NavigationExpansionExpression navigationExpansionExpression = null; + + // Only apply defining query if not a custom query root. + // This code will get removed when defining query is re-worked + if (constantExpression.Type.IsGenericType + && constantExpression.Type.GetGenericTypeDefinition() == typeof(EntityQueryable<>) + && entityType.GetDefiningQuery() is LambdaExpression definingQuery) { var processedDefiningQueryBody = _parameterExtractingExpressionVisitor.ExtractParameters(definingQuery.Body); processedDefiningQueryBody = _queryTranslationPreprocessor.NormalizeQueryableMethodCall(processedDefiningQueryBody); @@ -125,10 +129,8 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio processedDefiningQueryBody = Reduce(processedDefiningQueryBody); navigationExpansionExpression = CreateNavigationExpansionExpression(processedDefiningQueryBody, entityType); } - else - { - navigationExpansionExpression = CreateNavigationExpansionExpression(constantExpression, entityType); - } + + navigationExpansionExpression ??= CreateNavigationExpansionExpression(constantExpression, entityType); return ApplyQueryFilter(navigationExpansionExpression); } @@ -521,24 +523,6 @@ when QueryableMethods.IsSumWithSelector(method): return methodCallExpression.Update(null, new[] { argument }); } - if (method.IsGenericMethod - && method.Name == "FromSqlOnQueryable" - && methodCallExpression.Arguments.Count == 3 - && methodCallExpression.Arguments[0] is ConstantExpression constantExpression - && methodCallExpression.Arguments[1] is ConstantExpression - && (methodCallExpression.Arguments[2] is ParameterExpression || methodCallExpression.Arguments[2] is ConstantExpression) - && constantExpression.IsEntityQueryable()) - { - var entityType = ((IEntityQueryable)constantExpression.Value).EntityType; - var source = CreateNavigationExpansionExpression(constantExpression, entityType); - source.UpdateSource( - methodCallExpression.Update( - null, - new[] { source.Source, methodCallExpression.Arguments[1], methodCallExpression.Arguments[2] })); - - return ApplyQueryFilter(source); - } - return ProcessUnknownMethod(methodCallExpression); } @@ -915,7 +899,7 @@ private NavigationExpansionExpression ProcessLeftJoin( var currentTree = new NavigationTreeNode(outerSource.CurrentTree, innerSource.CurrentTree); var pendingSelector = new ReplacingExpressionVisitor( new Expression[] { resultSelector.Parameters[0], resultSelector.Parameters[1] }, - new[] { outerSource.PendingSelector, innerPendingSelector}) + new[] { outerSource.PendingSelector, innerPendingSelector }) .Visit(resultSelector.Body); var parameterName = GetParameterName("ti"); diff --git a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs index ec0f7905969..91d42422b48 100644 --- a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -37,8 +36,8 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio { Check.NotNull(constantExpression, nameof(constantExpression)); - return constantExpression.IsEntityQueryable() - ? CreateShapedQueryExpression(((IEntityQueryable)constantExpression.Value).EntityType) + return constantExpression.Value is IEntityQueryable entityQueryable + ? CreateShapedQueryExpression(entityQueryable.EntityType) : base.VisitConstant(constantExpression); } diff --git a/test/EFCore.CrossStore.FunctionalTests/ConfigurationPatternsTest.cs b/test/EFCore.CrossStore.FunctionalTests/ConfigurationPatternsTest.cs index e56ecb0c901..90da3f3efa2 100644 --- a/test/EFCore.CrossStore.FunctionalTests/ConfigurationPatternsTest.cs +++ b/test/EFCore.CrossStore.FunctionalTests/ConfigurationPatternsTest.cs @@ -227,7 +227,7 @@ public NestedContextDifferentStores(CrossStoreFixture fixture) ExistingTestStore = Fixture.CreateTestStore(SqlServerTestStoreFactory.Instance, StoreName, Seed); } - [ConditionalFact] + [ConditionalFact(Skip = "#18682")] public async Task Can_use_one_context_nested_inside_another_of_a_different_type() { var inMemoryServiceProvider = InMemoryFixture.DefaultServiceProvider; diff --git a/test/EFCore.InMemory.FunctionalTests/Query/InheritanceInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/InheritanceInMemoryTest.cs index 358fec97ad2..633ef37b7cd 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/InheritanceInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/InheritanceInMemoryTest.cs @@ -23,7 +23,7 @@ public override void Can_query_all_animal_views() Assert.Equal( CoreStrings.TranslationFailed( - @"DbSet + @"DbSet() .Select(b => InheritanceInMemoryFixture.MaterializeView(b)) .OrderBy(a => a.CountryId)"), message); diff --git a/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs b/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs index 2062e1fd1cd..c43c9efbc0f 100644 --- a/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs +++ b/test/EFCore.Specification.Tests/CustomConvertersTestBase.cs @@ -570,7 +570,7 @@ public virtual void Collection_property_as_scalar_Any() { using var context = CreateContext(); Assert.Equal( - @"The LINQ expression 'DbSet .Where(c => c.Tags .Any())' 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 'DbSet() .Where(c => c.Tags .Any())' 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.", Assert.Throws( () => context.Set().Where(e => e.Tags.Any()).ToList()) .Message.Replace("\r", "").Replace("\n", "")); @@ -581,7 +581,7 @@ 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.", + @"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.", Assert.Throws( () => context.Set().Where(e => e.Tags.Count == 2).ToList()) .Message.Replace("\r", "").Replace("\n", "")); @@ -599,7 +599,7 @@ public virtual void Collection_enum_as_string_Contains() using var context = CreateContext(); var sameRole = Roles.Seller; Assert.Equal( - @"The LINQ expression 'DbSet .Where(c => c.Roles.Contains(__sameRole_0))' 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 'DbSet() .Where(c => c.Roles.Contains(__sameRole_0))' 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.", Assert.Throws( () => context.Set().Where(e => e.Roles.Contains(sameRole)).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 c436c8db3e7..79c6144395e 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -1504,7 +1504,7 @@ public virtual async Task Select_navigation_with_concat_and_count(bool async) @"(MaterializeCollectionNavigation( navigation: Navigation: Gear.Weapons, subquery: (NavigationExpansionExpression - Source: DbSet + Source: DbSet() .Where(w => EF.Property(g, ""FullName"") != null && EF.Property(g, ""FullName"") == EF.Property(w, ""OwnerFullName"")) PendingSelector: w => (NavigationTreeExpression Value: (EntityReference: Weapon) @@ -1519,7 +1519,7 @@ public virtual async Task Select_navigation_with_concat_and_count(bool async) .Concat((MaterializeCollectionNavigation( navigation: Navigation: Gear.Weapons, subquery: (NavigationExpansionExpression - Source: DbSet + Source: DbSet() .Where(w0 => EF.Property(g, ""FullName"") != null && EF.Property(g, ""FullName"") == EF.Property(w0, ""OwnerFullName"")) PendingSelector: w0 => (NavigationTreeExpression Value: (EntityReference: Weapon) @@ -1547,7 +1547,7 @@ public virtual async Task Concat_with_collection_navigations(bool async) @"(MaterializeCollectionNavigation( navigation: Navigation: Gear.Weapons, subquery: (NavigationExpansionExpression - Source: DbSet + Source: DbSet() .Where(w => EF.Property(g, ""FullName"") != null && EF.Property(g, ""FullName"") == EF.Property(w, ""OwnerFullName"")) PendingSelector: w => (NavigationTreeExpression Value: (EntityReference: Weapon) @@ -1562,7 +1562,7 @@ public virtual async Task Concat_with_collection_navigations(bool async) .Union((MaterializeCollectionNavigation( navigation: Navigation: Gear.Weapons, subquery: (NavigationExpansionExpression - Source: DbSet + Source: DbSet() .Where(w0 => EF.Property(g, ""FullName"") != null && EF.Property(g, ""FullName"") == EF.Property(w0, ""OwnerFullName"")) PendingSelector: w0 => (NavigationTreeExpression Value: (EntityReference: Weapon) diff --git a/test/EFCore.Specification.Tests/Query/NorthwindQueryFiltersQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/NorthwindQueryFiltersQueryTestBase.cs index e5f22feb4ee..97ba3f15aab 100644 --- a/test/EFCore.Specification.Tests/Query/NorthwindQueryFiltersQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/NorthwindQueryFiltersQueryTestBase.cs @@ -51,7 +51,7 @@ public virtual void Find() public virtual void Client_eval() { Assert.Equal( - CoreStrings.TranslationFailed("DbSet .Where(p => NorthwindContext.ClientMethod(p))"), + CoreStrings.TranslationFailed("DbSet() .Where(p => NorthwindContext.ClientMethod(p))"), RemoveNewLines( Assert.Throws( () => _context.Products.ToList()).Message)); @@ -134,7 +134,7 @@ public virtual void Project_reference_that_itself_has_query_filter_with_another_ public virtual void Included_one_to_many_query_with_client_eval() { Assert.Equal( - CoreStrings.TranslationFailed("DbSet .Where(p => NorthwindContext.ClientMethod(p))"), + CoreStrings.TranslationFailed("DbSet() .Where(p => NorthwindContext.ClientMethod(p))"), RemoveNewLines( Assert.Throws( () => _context.Products.Include(p => p.OrderDetails).ToList()).Message)); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs index 52e770493d0..f3912561e88 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs @@ -6767,7 +6767,7 @@ 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)"), + CoreStrings.TranslationFailed(@"DbSet() .Cast() .Where(e => e.Id == __id_0)"), message.Replace("\r", "").Replace("\n", "")); }