diff --git a/src/EFCore.Cosmos/Query/Internal/FromSqlQueryRootExpression.cs b/src/EFCore.Cosmos/Query/Internal/FromSqlQueryRootExpression.cs
index 2ac394e4c4a..2d8a289516b 100644
--- a/src/EFCore.Cosmos/Query/Internal/FromSqlQueryRootExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/FromSqlQueryRootExpression.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -81,6 +82,18 @@ public FromSqlQueryRootExpression(
public override Expression DetachQueryProvider()
=> new FromSqlQueryRootExpression(EntityType, Sql, 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 override QueryRootExpression UpdateEntityType(IEntityType entityType)
+ => entityType.ClrType != EntityType.ClrType
+ || entityType.Name != EntityType.Name
+ ? throw new InvalidOperationException(CoreStrings.QueryRootDifferentEntityType(entityType.DisplayName()))
+ : new FromSqlQueryRootExpression(entityType, Sql, 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
diff --git a/src/EFCore.Relational/Query/Internal/FromSqlQueryRootExpression.cs b/src/EFCore.Relational/Query/Internal/FromSqlQueryRootExpression.cs
index ca7dc4cae04..0e55b6e88bf 100644
--- a/src/EFCore.Relational/Query/Internal/FromSqlQueryRootExpression.cs
+++ b/src/EFCore.Relational/Query/Internal/FromSqlQueryRootExpression.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -80,6 +81,18 @@ public FromSqlQueryRootExpression(
public override Expression DetachQueryProvider()
=> new FromSqlQueryRootExpression(EntityType, Sql, 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 override QueryRootExpression UpdateEntityType(IEntityType entityType)
+ => entityType.ClrType != EntityType.ClrType
+ || entityType.Name != EntityType.Name
+ ? throw new InvalidOperationException(CoreStrings.QueryRootDifferentEntityType(entityType.DisplayName()))
+ : new FromSqlQueryRootExpression(entityType, Sql, 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
diff --git a/src/EFCore.Relational/Query/Internal/TableValuedFunctionQueryRootExpression.cs b/src/EFCore.Relational/Query/Internal/TableValuedFunctionQueryRootExpression.cs
index cbda431c941..f4db217eae5 100644
--- a/src/EFCore.Relational/Query/Internal/TableValuedFunctionQueryRootExpression.cs
+++ b/src/EFCore.Relational/Query/Internal/TableValuedFunctionQueryRootExpression.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -76,6 +77,18 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
: this;
}
+ ///
+ /// 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 QueryRootExpression UpdateEntityType(IEntityType entityType)
+ => entityType.ClrType != EntityType.ClrType
+ || entityType.Name != EntityType.Name
+ ? throw new InvalidOperationException(CoreStrings.QueryRootDifferentEntityType(entityType.DisplayName()))
+ : new TableValuedFunctionQueryRootExpression(entityType, Function, Arguments);
+
///
/// 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
diff --git a/src/EFCore.SqlServer/Query/Internal/TemporalAllQueryRootExpression.cs b/src/EFCore.SqlServer/Query/Internal/TemporalAllQueryRootExpression.cs
index 55b9577cc0b..065567e33ae 100644
--- a/src/EFCore.SqlServer/Query/Internal/TemporalAllQueryRootExpression.cs
+++ b/src/EFCore.SqlServer/Query/Internal/TemporalAllQueryRootExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
@@ -46,6 +47,18 @@ public TemporalAllQueryRootExpression(IAsyncQueryProvider queryProvider, IEntity
public override Expression DetachQueryProvider()
=> new TemporalAllQueryRootExpression(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 QueryRootExpression UpdateEntityType(IEntityType entityType)
+ => entityType.ClrType != EntityType.ClrType
+ || entityType.Name != EntityType.Name
+ ? throw new InvalidOperationException(CoreStrings.QueryRootDifferentEntityType(entityType.DisplayName()))
+ : new TemporalAllQueryRootExpression(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
diff --git a/src/EFCore.SqlServer/Query/Internal/TemporalAsOfQueryRootExpression.cs b/src/EFCore.SqlServer/Query/Internal/TemporalAsOfQueryRootExpression.cs
index 3bb596cf117..1c9224de48d 100644
--- a/src/EFCore.SqlServer/Query/Internal/TemporalAsOfQueryRootExpression.cs
+++ b/src/EFCore.SqlServer/Query/Internal/TemporalAsOfQueryRootExpression.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
@@ -58,6 +59,18 @@ public TemporalAsOfQueryRootExpression(
public override Expression DetachQueryProvider()
=> new TemporalAsOfQueryRootExpression(EntityType, PointInTime);
+ ///
+ /// 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 QueryRootExpression UpdateEntityType(IEntityType entityType)
+ => entityType.ClrType != EntityType.ClrType
+ || entityType.Name != EntityType.Name
+ ? throw new InvalidOperationException(CoreStrings.QueryRootDifferentEntityType(entityType.DisplayName()))
+ : new TemporalAsOfQueryRootExpression(entityType, PointInTime);
+
///
/// 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
diff --git a/src/EFCore.SqlServer/Query/Internal/TemporalBetweenQueryRootExpression.cs b/src/EFCore.SqlServer/Query/Internal/TemporalBetweenQueryRootExpression.cs
index f31147e58eb..7d133461c5c 100644
--- a/src/EFCore.SqlServer/Query/Internal/TemporalBetweenQueryRootExpression.cs
+++ b/src/EFCore.SqlServer/Query/Internal/TemporalBetweenQueryRootExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
@@ -53,6 +54,18 @@ public TemporalBetweenQueryRootExpression(
public override Expression DetachQueryProvider()
=> new TemporalBetweenQueryRootExpression(EntityType, From, To);
+ ///
+ /// 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 QueryRootExpression UpdateEntityType(IEntityType entityType)
+ => entityType.ClrType != EntityType.ClrType
+ || entityType.Name != EntityType.Name
+ ? throw new InvalidOperationException(CoreStrings.QueryRootDifferentEntityType(entityType.DisplayName()))
+ : new TemporalBetweenQueryRootExpression(entityType, From, To);
+
///
/// 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
diff --git a/src/EFCore.SqlServer/Query/Internal/TemporalContainedInQueryRootExpression.cs b/src/EFCore.SqlServer/Query/Internal/TemporalContainedInQueryRootExpression.cs
index e10e9562f1a..fd2e049fcec 100644
--- a/src/EFCore.SqlServer/Query/Internal/TemporalContainedInQueryRootExpression.cs
+++ b/src/EFCore.SqlServer/Query/Internal/TemporalContainedInQueryRootExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
@@ -53,6 +54,18 @@ public TemporalContainedInQueryRootExpression(
public override Expression DetachQueryProvider()
=> new TemporalContainedInQueryRootExpression(EntityType, From, To);
+ ///
+ /// 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 QueryRootExpression UpdateEntityType(IEntityType entityType)
+ => entityType.ClrType != EntityType.ClrType
+ || entityType.Name != EntityType.Name
+ ? throw new InvalidOperationException(CoreStrings.QueryRootDifferentEntityType(entityType.DisplayName()))
+ : new TemporalContainedInQueryRootExpression(entityType, From, To);
+
///
/// 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
diff --git a/src/EFCore.SqlServer/Query/Internal/TemporalFromToQueryRootExpression.cs b/src/EFCore.SqlServer/Query/Internal/TemporalFromToQueryRootExpression.cs
index 6a53fb9c50b..e04cd5698ca 100644
--- a/src/EFCore.SqlServer/Query/Internal/TemporalFromToQueryRootExpression.cs
+++ b/src/EFCore.SqlServer/Query/Internal/TemporalFromToQueryRootExpression.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
@@ -53,6 +54,18 @@ public TemporalFromToQueryRootExpression(
public override Expression DetachQueryProvider()
=> new TemporalFromToQueryRootExpression(EntityType, From, To);
+ ///
+ /// 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 QueryRootExpression UpdateEntityType(IEntityType entityType)
+ => entityType.ClrType != EntityType.ClrType
+ || entityType.Name != EntityType.Name
+ ? throw new InvalidOperationException(CoreStrings.QueryRootDifferentEntityType(entityType.DisplayName()))
+ : new TemporalFromToQueryRootExpression(entityType, From, To);
+
///
/// 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
diff --git a/src/EFCore/Metadata/Conventions/QueryFilterRewritingConvention.cs b/src/EFCore/Metadata/Conventions/QueryFilterRewritingConvention.cs
index 2bf808df950..4164b28e229 100644
--- a/src/EFCore/Metadata/Conventions/QueryFilterRewritingConvention.cs
+++ b/src/EFCore/Metadata/Conventions/QueryFilterRewritingConvention.cs
@@ -3,8 +3,11 @@
using System;
using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -111,7 +114,55 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
&& methodCallExpression.Type.GetGenericTypeDefinition() == typeof(DbSet<>)
&& _model != null)
{
- return new QueryRootExpression(FindEntityType(methodCallExpression.Type)!);
+ IEntityType? entityType;
+ var returnType = methodCallExpression.Type.GetGenericArguments()[0];
+ if (methodCallExpression.Arguments.Count == 1)
+ {
+ // STET Set method
+ var entityTypeName = methodCallExpression.Arguments[0].GetConstantValue();
+ entityType = (IEntityType?)_model.FindEntityType(entityTypeName);
+ }
+ else
+ {
+ entityType = FindEntityType(returnType);
+ }
+
+ if (entityType == null)
+ {
+ if (_model.IsShared(returnType))
+ {
+ throw new InvalidOperationException(CoreStrings.InvalidSetSharedType(returnType.ShortDisplayName()));
+ }
+
+ var findSameTypeName = ((IModel)_model).FindSameTypeNameWithDifferentNamespace(returnType);
+ //if the same name exists in your entity types we will show you the full namespace of the type
+ if (!string.IsNullOrEmpty(findSameTypeName))
+ {
+ throw new InvalidOperationException(CoreStrings.InvalidSetSameTypeWithDifferentNamespace(returnType.DisplayName(), findSameTypeName));
+ }
+ else
+ {
+ throw new InvalidOperationException(CoreStrings.InvalidSetType(returnType.ShortDisplayName()));
+ }
+ }
+
+ if (entityType.IsOwned())
+ {
+ var message = CoreStrings.InvalidSetTypeOwned(
+ entityType.DisplayName(), entityType.FindOwnership()!.PrincipalEntityType.DisplayName());
+
+ throw new InvalidOperationException(message);
+ }
+
+ if (entityType.ClrType != returnType)
+ {
+ var message = CoreStrings.DbSetIncorrectGenericType(
+ entityType.ShortName(), entityType.ClrType.ShortDisplayName(), returnType.ShortDisplayName());
+
+ throw new InvalidOperationException(message);
+ }
+
+ return new QueryRootExpression(entityType);
}
return base.VisitMethodCall(methodCallExpression);
diff --git a/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs b/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs
index 263ef038889..8cfd6310e95 100644
--- a/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs
+++ b/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs
@@ -610,7 +610,7 @@ public Expression Rewrite(Expression expression)
///
protected override Expression VisitExtension(Expression extensionExpression)
=> extensionExpression is QueryRootExpression queryRootExpression
- ? new QueryRootExpression(_model.FindEntityType(queryRootExpression.EntityType.Name)!)
+ ? queryRootExpression.UpdateEntityType(_model.FindEntityType(queryRootExpression.EntityType.Name)!)
: base.VisitExtension(extensionExpression);
}
}
diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs
index 4ea9c8c5d18..4ca089e9d27 100644
--- a/src/EFCore/Properties/CoreStrings.Designer.cs
+++ b/src/EFCore/Properties/CoreStrings.Designer.cs
@@ -2289,6 +2289,14 @@ public static string QueryInvalidMaterializationType(object? projection, object?
GetString("QueryInvalidMaterializationType", nameof(projection), nameof(queryableType)),
projection, queryableType);
+ ///
+ /// The replacement entity type: {entityType} does not have same name and CLR type as entity type this query root represents.
+ ///
+ public static string QueryRootDifferentEntityType(object? entityType)
+ => string.Format(
+ GetString("QueryRootDifferentEntityType", nameof(entityType)),
+ entityType);
+
///
/// Translation of '{expression}' failed. Either the query source is not an entity type, or the specified property does not exist on the entity type.
///
@@ -2847,7 +2855,7 @@ public static string ValueGenWithConversion(object? entityType, object? property
=> string.Format(
GetString("ValueGenWithConversion", nameof(entityType), nameof(property), nameof(converter)),
entityType, property, converter);
-
+
///
/// Calling '{visitMethodName}' is not allowed. Visit the expression manually for the relevant part in the visitor.
///
diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx
index d12f4d5378f..4511a3a4dc0 100644
--- a/src/EFCore/Properties/CoreStrings.resx
+++ b/src/EFCore/Properties/CoreStrings.resx
@@ -972,7 +972,7 @@
The foreign key property '{entityType}.{property}' was created in shadow state because a conflicting property with the simple name '{baseName}' exists in the entity type, but is either not mapped, is already used for another relationship, or is incompatible with the associated primary key type. See https://aka.ms/efcore-relationships for information on mapping relationships in EF Core.
- Warning CoreEventId.ShadowForeignKeyPropertyCreated string string
+ Warning CoreEventId.ShadowForeignKeyPropertyCreated string string stringThe property '{entityType}.{property}' was created in shadow state because there are no eligible CLR members with a matching name.
@@ -1310,6 +1310,9 @@
The query contains a projection '{projection}' of type '{queryableType}'. Collections in the final projection must be an 'IEnumerable<T>' type such as 'List<T>'. Consider using 'ToList' or some other mechanism to convert the 'IQueryable<T>' or 'IOrderedEnumerable<T>' into an 'IEnumerable<T>'.
+
+ The replacement entity type: {entityType} does not have same name and CLR type as entity type this query root represents.
+
Translation of '{expression}' failed. Either the query source is not an entity type, or the specified property does not exist on the entity type.
diff --git a/src/EFCore/Query/QueryRootExpression.cs b/src/EFCore/Query/QueryRootExpression.cs
index 09a497c0615..43b1edf6ac7 100644
--- a/src/EFCore/Query/QueryRootExpression.cs
+++ b/src/EFCore/Query/QueryRootExpression.cs
@@ -4,6 +4,7 @@
using System;
using System.Linq;
using System.Linq.Expressions;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -66,6 +67,17 @@ public QueryRootExpression(IEntityType entityType)
public virtual Expression DetachQueryProvider()
=> new QueryRootExpression(EntityType);
+ ///
+ /// Updates entity type associated with this query root with equivalent optimized version.
+ ///
+ /// The entity type to replace with.
+ /// New query root containing given entity type.
+ public virtual QueryRootExpression UpdateEntityType(IEntityType entityType)
+ => entityType.ClrType != EntityType.ClrType
+ || entityType.Name != EntityType.Name
+ ? throw new InvalidOperationException(CoreStrings.QueryRootDifferentEntityType(entityType.DisplayName()))
+ : new QueryRootExpression(entityType);
+
///
public override ExpressionType NodeType
=> ExpressionType.Extension;
diff --git a/test/EFCore.InMemory.FunctionalTests/Query/SharedTypeQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/SharedTypeQueryInMemoryTest.cs
new file mode 100644
index 00000000000..7a0204edbd0
--- /dev/null
+++ b/test/EFCore.InMemory.FunctionalTests/Query/SharedTypeQueryInMemoryTest.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.TestUtilities;
+using Xunit;
+
+namespace Microsoft.EntityFrameworkCore.Query
+{
+ public class SharedTypeQueryInMemoryTest : SharedTypeQueryTestBase
+ {
+ protected override ITestStoreFactory TestStoreFactory => InMemoryTestStoreFactory.Instance;
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Can_use_shared_type_entity_type_in_ToInMemoryQuery(bool async)
+ {
+ var contextFactory = await InitializeAsync(
+ seed: c => c.Seed());
+
+ using var context = contextFactory.CreateContext();
+
+ var data = context.Set();
+
+ Assert.Equal("Maumar", Assert.Single(data).Value);
+ }
+
+ private class MyContextInMemory24601 : MyContext24601
+ {
+ public MyContextInMemory24601(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ modelBuilder.SharedTypeEntity>("STET",
+ b =>
+ {
+ b.IndexerProperty("Id");
+ b.IndexerProperty("Value");
+ });
+
+ modelBuilder.Entity().HasNoKey()
+ .ToInMemoryQuery(() => Set>("STET").Select(e => new ViewQuery24601 { Value = (string)e["Value"] }));
+ }
+ }
+ }
+}
diff --git a/test/EFCore.Relational.Specification.Tests/Query/SharedTypeQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/SharedTypeQueryRelationalTestBase.cs
new file mode 100644
index 00000000000..cdf9a09433e
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/Query/SharedTypeQueryRelationalTestBase.cs
@@ -0,0 +1,50 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.EntityFrameworkCore.TestUtilities;
+using Xunit;
+
+namespace Microsoft.EntityFrameworkCore.Query
+{
+ public abstract class SharedTypeQueryRelationalTestBase : SharedTypeQueryTestBase
+ {
+ protected TestSqlLoggerFactory TestSqlLoggerFactory
+ => (TestSqlLoggerFactory)ListLoggerFactory;
+
+ protected void ClearLog() => TestSqlLoggerFactory.Clear();
+
+ protected void AssertSql(params string[] expected) => TestSqlLoggerFactory.AssertBaseline(expected);
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Can_use_shared_type_entity_type_in_query_filter_with_from_sql(bool async)
+ {
+ var contextFactory = await InitializeAsync(
+ seed: c => c.Seed());
+
+ using var context = contextFactory.CreateContext();
+ var query = context.Set();
+ var result = async
+ ? await query.ToListAsync()
+ : query.ToList();
+
+ Assert.Empty(result);
+ }
+
+ protected class MyContextRelational24601 : MyContext24601
+ {
+ public MyContextRelational24601(DbContextOptions options)
+ : base(options)
+ {
+ }
+
+ protected override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ base.OnModelCreating(modelBuilder);
+ modelBuilder.Entity()
+ .HasQueryFilter(e => Set>("STET")
+ .FromSqlRaw("Select * from STET").Select(i => (string)i["Value"]).Contains(e.Value));
+ }
+ }
+ }
+}
diff --git a/test/EFCore.Specification.Tests/Query/SharedTypeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/SharedTypeQueryTestBase.cs
new file mode 100644
index 00000000000..d07bac3a8f0
--- /dev/null
+++ b/test/EFCore.Specification.Tests/Query/SharedTypeQueryTestBase.cs
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace Microsoft.EntityFrameworkCore
+{
+ public abstract class SharedTypeQueryTestBase : NonSharedModelTestBase
+ {
+ public static IEnumerable