From fa060341f82fdb96690308a3bf147af62ac9be48 Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Tue, 25 Mar 2025 16:46:50 -0700 Subject: [PATCH] Remove Obsolete members from EF 8 Fixes #34467 --- .../Properties/DesignStrings.Designer.cs | 8 -- .../Properties/DesignStrings.resx | 3 - .../CSharpRuntimeModelCodeGenerator.cs | 8 -- ...oryCSharpRuntimeAnnotationCodeGenerator.cs | 17 +++ src/EFCore.InMemory/EFCore.InMemory.csproj | 2 +- .../InMemoryEntityTypeBuilderExtensions.cs | 6 +- .../InMemoryEntityTypeExtensions.cs | 26 +--- .../InMemoryServiceCollectionExtensions.cs | 1 - .../InMemoryConventionSetBuilder.cs | 2 +- .../Internal/CosmosAnnotationNames.cs | 29 ++++ .../Properties/InMemoryStrings.Designer.cs | 12 +- .../Properties/InMemoryStrings.Designer.tt | 2 +- .../Properties/InMemoryStrings.resx | 3 + .../RelationalEntityTypeExtensions.cs | 6 - ...elationalQueryFilterRewritingConvention.cs | 8 -- .../ConventionEntityTypeExtensions.cs | 38 ------ src/EFCore/Extensions/EntityTypeExtensions.cs | 23 ---- .../Extensions/MutableEntityTypeExtensions.cs | 25 ---- .../Metadata/Builders/EntityTypeBuilder`.cs | 15 --- .../Builders/IConventionEntityTypeBuilder.cs | 20 --- .../Conventions/RuntimeModelConvention.cs | 11 -- src/EFCore/Metadata/IConventionModel.cs | 2 + .../Metadata/Internal/CoreAnnotationNames.cs | 12 -- src/EFCore/Metadata/Internal/EntityType.cs | 10 -- .../Internal/InternalEntityTypeBuilder.cs | 54 -------- .../NavigationExpandingExpressionVisitor.cs | 10 +- .../Design/CSharpMigrationsGeneratorTest.cs | 9 -- .../AdHocQueryFiltersQueryInMemoryTest.cs | 124 +++++++++++++++++ .../Scaffolding/CompiledModelInMemoryTest.cs | 3 +- .../Internal/MigrationsModelDifferTest.cs | 13 -- .../Query/AdHocQueryFiltersQueryTestBase.cs | 126 ------------------ .../AdHocQueryFiltersQuerySqlServerTest.cs | 25 ---- .../Query/RawSqlServerTest.cs | 79 ----------- .../Conventions/ConventionSetBuilderTests.cs | 1 - 34 files changed, 200 insertions(+), 533 deletions(-) create mode 100644 src/EFCore.InMemory/Metadata/Internal/CosmosAnnotationNames.cs delete mode 100644 src/EFCore/Extensions/ConventionEntityTypeExtensions.cs delete mode 100644 src/EFCore/Extensions/EntityTypeExtensions.cs delete mode 100644 src/EFCore/Extensions/MutableEntityTypeExtensions.cs delete mode 100644 test/EFCore.SqlServer.FunctionalTests/Query/RawSqlServerTest.cs diff --git a/src/EFCore.Design/Properties/DesignStrings.Designer.cs b/src/EFCore.Design/Properties/DesignStrings.Designer.cs index f213b989dd2..e245a42bcc8 100644 --- a/src/EFCore.Design/Properties/DesignStrings.Designer.cs +++ b/src/EFCore.Design/Properties/DesignStrings.Designer.cs @@ -129,14 +129,6 @@ public static string CompiledModelCustomCacheKeyFactory(object? factoryType) GetString("CompiledModelCustomCacheKeyFactory", nameof(factoryType)), factoryType); - /// - /// The entity type '{entityType}' has a defining query configured. Compiled model can't be generated, because defining queries are not supported. - /// - public static string CompiledModelDefiningQuery(object? entityType) - => string.Format( - GetString("CompiledModelDefiningQuery", nameof(entityType)), - entityType); - /// /// Successfully generated a compiled model, it will be discovered automatically, but you can also call '{optionsCall}'. Run this command again when the model is modified. /// diff --git a/src/EFCore.Design/Properties/DesignStrings.resx b/src/EFCore.Design/Properties/DesignStrings.resx index 1303c7e4816..d0506266612 100644 --- a/src/EFCore.Design/Properties/DesignStrings.resx +++ b/src/EFCore.Design/Properties/DesignStrings.resx @@ -162,9 +162,6 @@ The context is configured to use a custom model cache key factory '{factoryType}', this usually indicates that the produced model can change between context instances. To preserve this behavior manually modify the generated compiled model source code. - - The entity type '{entityType}' has a defining query configured. Compiled model can't be generated, because defining queries are not supported. - Successfully generated a compiled model, it will be discovered automatically, but you can also call '{optionsCall}'. Run this command again when the model is modified. diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs index ad23c6aacac..e18a43f317d 100644 --- a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs +++ b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs @@ -898,14 +898,6 @@ private void Create(IEntityType entityType, CSharpRuntimeAnnotationCodeGenerator throw new InvalidOperationException(DesignStrings.CompiledModelQueryFilter(entityType.ShortName())); } -#pragma warning disable CS0618 // Type or member is obsolete - if (entityType.GetDefiningQuery() != null) - { - // TODO: Move to InMemoryCSharpRuntimeAnnotationCodeGenerator, see #21624 - throw new InvalidOperationException(DesignStrings.CompiledModelDefiningQuery(entityType.ShortName())); - } -#pragma warning restore CS0618 // Type or member is obsolete - AddNamespace(entityType.ClrType, parameters.Namespaces); var mainBuilder = parameters.MainBuilder; diff --git a/src/EFCore.InMemory/Design/Internal/InMemoryCSharpRuntimeAnnotationCodeGenerator.cs b/src/EFCore.InMemory/Design/Internal/InMemoryCSharpRuntimeAnnotationCodeGenerator.cs index a9c7e56e256..0ea2c29c5ff 100644 --- a/src/EFCore.InMemory/Design/Internal/InMemoryCSharpRuntimeAnnotationCodeGenerator.cs +++ b/src/EFCore.InMemory/Design/Internal/InMemoryCSharpRuntimeAnnotationCodeGenerator.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.EntityFrameworkCore.Design.Internal; +using Microsoft.EntityFrameworkCore.InMemory.Internal; namespace Microsoft.EntityFrameworkCore.InMemory.Design.Internal; @@ -25,4 +26,20 @@ public InMemoryCSharpRuntimeAnnotationCodeGenerator( : base(dependencies) { } + + /// + /// 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 void Generate(IEntityType entityType, CSharpRuntimeAnnotationCodeGeneratorParameters parameters) + { + if (entityType.GetInMemoryQuery() != null) + { + throw new InvalidOperationException(InMemoryStrings.CompiledModelDefiningQuery(entityType.DisplayName())); + } + + base.Generate(entityType, parameters); + } } diff --git a/src/EFCore.InMemory/EFCore.InMemory.csproj b/src/EFCore.InMemory/EFCore.InMemory.csproj index bf88275bc8f..17c05bf18d1 100644 --- a/src/EFCore.InMemory/EFCore.InMemory.csproj +++ b/src/EFCore.InMemory/EFCore.InMemory.csproj @@ -5,7 +5,7 @@ $(DefaultNetCoreTargetFramework) 3.6 Microsoft.EntityFrameworkCore.InMemory - Microsoft.EntityFrameworkCore.InMemory + Microsoft.EntityFrameworkCore true $(PackageTags);In-Memory true diff --git a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs index e0bc4d28569..9d7c8d9a93b 100644 --- a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs +++ b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeBuilderExtensions.cs @@ -101,9 +101,5 @@ public static bool CanSetInMemoryQuery( this IConventionEntityTypeBuilder entityTypeBuilder, LambdaExpression? query, bool fromDataAnnotation = false) -#pragma warning disable EF1001 // Internal EF Core API usage. -#pragma warning disable CS0612 // Type or member is obsolete - => entityTypeBuilder.CanSetAnnotation(CoreAnnotationNames.DefiningQuery, query, fromDataAnnotation); -#pragma warning restore CS0612 // Type or member is obsolete -#pragma warning restore EF1001 // Internal EF Core API usage. + => entityTypeBuilder.CanSetAnnotation(InMemoryAnnotationNames.DefiningQuery, query, fromDataAnnotation); } diff --git a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs index 5045a664b85..0c42dc1ffa2 100644 --- a/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs +++ b/src/EFCore.InMemory/Extensions/InMemoryEntityTypeExtensions.cs @@ -21,11 +21,7 @@ public static class InMemoryEntityTypeExtensions /// The entity type to get the in-memory query for. /// The LINQ query used as the default source. public static LambdaExpression? GetInMemoryQuery(this IReadOnlyEntityType entityType) -#pragma warning disable EF1001 // Internal EF Core API usage. -#pragma warning disable CS0612 // Type or member is obsolete - => (LambdaExpression?)entityType[CoreAnnotationNames.DefiningQuery]; -#pragma warning restore CS0612 // Type or member is obsolete -#pragma warning restore EF1001 // Internal EF Core API usage. + => (LambdaExpression?)entityType[InMemoryAnnotationNames.DefiningQuery]; /// /// Sets the LINQ query used as the default source for queries of this type. @@ -36,11 +32,7 @@ public static void SetInMemoryQuery( this IMutableEntityType entityType, LambdaExpression? inMemoryQuery) => entityType -#pragma warning disable EF1001 // Internal EF Core API usage. -#pragma warning disable CS0612 // Type or member is obsolete - .SetOrRemoveAnnotation(CoreAnnotationNames.DefiningQuery, inMemoryQuery); -#pragma warning restore CS0612 // Type or member is obsolete -#pragma warning restore EF1001 // Internal EF Core API usage. + .SetOrRemoveAnnotation(InMemoryAnnotationNames.DefiningQuery, inMemoryQuery); /// /// Sets the LINQ query used as the default source for queries of this type. @@ -54,11 +46,7 @@ public static void SetInMemoryQuery( LambdaExpression? inMemoryQuery, bool fromDataAnnotation = false) => (LambdaExpression?)entityType -#pragma warning disable EF1001 // Internal EF Core API usage. -#pragma warning disable CS0612 // Type or member is obsolete - .SetOrRemoveAnnotation(CoreAnnotationNames.DefiningQuery, inMemoryQuery, fromDataAnnotation) -#pragma warning restore CS0612 // Type or member is obsolete -#pragma warning restore EF1001 // Internal EF Core API usage. + .SetOrRemoveAnnotation(InMemoryAnnotationNames.DefiningQuery, inMemoryQuery, fromDataAnnotation) ?.Value; /// @@ -66,10 +54,6 @@ public static void SetInMemoryQuery( /// /// The entity type. /// The configuration source for . - public static ConfigurationSource? GetDefiningQueryConfigurationSource(this IConventionEntityType entityType) -#pragma warning disable EF1001 // Internal EF Core API usage. -#pragma warning disable CS0612 // Type or member is obsolete - => entityType.FindAnnotation(CoreAnnotationNames.DefiningQuery)?.GetConfigurationSource(); -#pragma warning restore CS0612 // Type or member is obsolete -#pragma warning restore EF1001 // Internal EF Core API usage. + public static ConfigurationSource? GetInMemoryQueryConfigurationSource(this IConventionEntityType entityType) + => entityType.FindAnnotation(InMemoryAnnotationNames.DefiningQuery)?.GetConfigurationSource(); } diff --git a/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs b/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs index 6b8d1c64e40..d3d50a93eb0 100644 --- a/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs +++ b/src/EFCore.InMemory/Extensions/InMemoryServiceCollectionExtensions.cs @@ -4,7 +4,6 @@ using System.ComponentModel; using Microsoft.EntityFrameworkCore.InMemory.Diagnostics.Internal; using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; -using Microsoft.EntityFrameworkCore.InMemory.Metadata.Conventions; using Microsoft.EntityFrameworkCore.InMemory.Query.Internal; using Microsoft.EntityFrameworkCore.InMemory.Storage.Internal; using Microsoft.EntityFrameworkCore.InMemory.ValueGeneration.Internal; diff --git a/src/EFCore.InMemory/Metadata/Conventions/InMemoryConventionSetBuilder.cs b/src/EFCore.InMemory/Metadata/Conventions/InMemoryConventionSetBuilder.cs index 0f13f38176f..7bfad928af8 100644 --- a/src/EFCore.InMemory/Metadata/Conventions/InMemoryConventionSetBuilder.cs +++ b/src/EFCore.InMemory/Metadata/Conventions/InMemoryConventionSetBuilder.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.EntityFrameworkCore.InMemory.Metadata.Conventions; +namespace Microsoft.EntityFrameworkCore.Metadata.Conventions; /// /// A builder for building conventions for th in-memory provider. diff --git a/src/EFCore.InMemory/Metadata/Internal/CosmosAnnotationNames.cs b/src/EFCore.InMemory/Metadata/Internal/CosmosAnnotationNames.cs new file mode 100644 index 00000000000..ce1a599dfdc --- /dev/null +++ b/src/EFCore.InMemory/Metadata/Internal/CosmosAnnotationNames.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.EntityFrameworkCore.Metadata.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 static class InMemoryAnnotationNames +{ + /// + /// 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 const string Prefix = "InMemory:"; + + /// + /// 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 const string DefiningQuery = Prefix + "DefiningQuery"; +} diff --git a/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs b/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs index 83f0de02464..4076751e735 100644 --- a/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs +++ b/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.cs @@ -21,7 +21,15 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Internal public static class InMemoryStrings { private static readonly ResourceManager _resourceManager - = new ResourceManager("Microsoft.EntityFrameworkCore.InMemory.Properties.InMemoryStrings", typeof(InMemoryStrings).Assembly); + = new ResourceManager("Microsoft.EntityFrameworkCore.Properties.InMemoryStrings", typeof(InMemoryStrings).Assembly); + + /// + /// The entity type '{entityType}' has a defining query configured. Compiled model can't be generated, because defining queries are not supported. + /// + public static string CompiledModelDefiningQuery(object? entityType) + => string.Format( + GetString("CompiledModelDefiningQuery", nameof(entityType)), + entityType); /// /// Cannot apply 'DefaultIfEmpty' after a client-evaluated projection. Consider applying 'DefaultIfEmpty' before last 'Select' or use 'AsEnumerable' before 'DefaultIfEmpty' to apply it on client-side. @@ -131,7 +139,7 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Internal public static class InMemoryResources { private static readonly ResourceManager _resourceManager - = new ResourceManager("Microsoft.EntityFrameworkCore.InMemory.Properties.InMemoryStrings", typeof(InMemoryResources).Assembly); + = new ResourceManager("Microsoft.EntityFrameworkCore.Properties.InMemoryStrings", typeof(InMemoryResources).Assembly); /// /// Saved {count} entities to in-memory store. diff --git a/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.tt b/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.tt index 8f9f997c866..349c4ed04e9 100644 --- a/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.tt +++ b/src/EFCore.InMemory/Properties/InMemoryStrings.Designer.tt @@ -1,7 +1,7 @@ <# Session = new System.Collections.Generic.Dictionary(); Session["ResourceFile"] = "InMemoryStrings.resx"; - Session["ResourceNamespace"] = "Microsoft.EntityFrameworkCore.InMemory.Properties"; + Session["ResourceNamespace"] = "Microsoft.EntityFrameworkCore.Properties"; Session["LoggingDefinitionsClass"] = "Diagnostics.Internal.InMemoryLoggingDefinitions"; #> <#@ include file="..\..\..\tools\Resources.tt" #> \ No newline at end of file diff --git a/src/EFCore.InMemory/Properties/InMemoryStrings.resx b/src/EFCore.InMemory/Properties/InMemoryStrings.resx index 93e3f5cfd5d..ed8494a3500 100644 --- a/src/EFCore.InMemory/Properties/InMemoryStrings.resx +++ b/src/EFCore.InMemory/Properties/InMemoryStrings.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + The entity type '{entityType}' has a defining query configured. Compiled model can't be generated, because defining queries are not supported. + Cannot apply 'DefaultIfEmpty' after a client-evaluated projection. Consider applying 'DefaultIfEmpty' before last 'Select' or use 'AsEnumerable' before 'DefaultIfEmpty' to apply it on client-side. diff --git a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs index 517205fb735..2e0a92fb071 100644 --- a/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalEntityTypeExtensions.cs @@ -39,9 +39,6 @@ public static class RelationalEntityTypeExtensions return ((entityType as IConventionEntityType)?.GetViewNameConfigurationSource() == null) && (entityType as IConventionEntityType)?.GetFunctionNameConfigurationSource() == null -#pragma warning disable CS0618 // Type or member is obsolete - && (entityType as IConventionEntityType)?.GetDefiningQueryConfigurationSource() == null -#pragma warning restore CS0618 // Type or member is obsolete && (entityType as IConventionEntityType)?.GetSqlQueryConfigurationSource() == null ? GetDefaultTableName(entityType) : null; @@ -267,9 +264,6 @@ public static void SetSchema(this IMutableEntityType entityType, string? value) } return ((entityType as IConventionEntityType)?.GetFunctionNameConfigurationSource() == null) -#pragma warning disable CS0618 // Type or member is obsolete - && ((entityType as IConventionEntityType)?.GetDefiningQueryConfigurationSource() == null) -#pragma warning restore CS0618 // Type or member is obsolete && ((entityType as IConventionEntityType)?.GetSqlQueryConfigurationSource() == null) ? GetDefaultViewName(entityType) : null; diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterRewritingConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterRewritingConvention.cs index 8453e441377..1cb87baf3c2 100644 --- a/src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterRewritingConvention.cs +++ b/src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterRewritingConvention.cs @@ -39,14 +39,6 @@ public override void ProcessModelFinalizing( { entityType.SetQueryFilter((LambdaExpression)DbSetAccessRewriter.Rewrite(modelBuilder.Metadata, queryFilter)); } - -#pragma warning disable CS0618 // Type or member is obsolete - var definingQuery = ((IEntityType)entityType).GetDefiningQuery(); - if (definingQuery != null) - { - entityType.SetDefiningQuery((LambdaExpression)DbSetAccessRewriter.Rewrite(modelBuilder.Metadata, definingQuery)); - } -#pragma warning restore CS0618 // Type or member is obsolete } } diff --git a/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs b/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs deleted file mode 100644 index e6b66ad06b9..00000000000 --- a/src/EFCore/Extensions/ConventionEntityTypeExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -// 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.Metadata.Internal; - -// ReSharper disable once CheckNamespace -namespace Microsoft.EntityFrameworkCore; - -/// -/// Extension methods for . -/// -[Obsolete("Use IConventionEntityType")] // Delete with defining query -public static class ConventionEntityTypeExtensions -{ - /// - /// Sets the LINQ query used as the default source for queries of this type. - /// - /// The entity type. - /// The LINQ query used as the default source. - /// Indicates whether the configuration was specified using a data annotation. - [Obsolete("Use InMemoryEntityTypeExtensions.SetInMemoryQuery")] - public static void SetDefiningQuery( - this IConventionEntityType entityType, - LambdaExpression? definingQuery, - bool fromDataAnnotation = false) - => ((EntityType)entityType).SetDefiningQuery( - definingQuery, - fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); - - /// - /// Returns the configuration source for . - /// - /// The entity type. - /// The configuration source for . - [Obsolete("Use InMemoryEntityTypeExtensions.GetInMemoryQueryConfigurationSource")] - public static ConfigurationSource? GetDefiningQueryConfigurationSource(this IConventionEntityType entityType) - => entityType.FindAnnotation(CoreAnnotationNames.DefiningQuery)?.GetConfigurationSource(); -} diff --git a/src/EFCore/Extensions/EntityTypeExtensions.cs b/src/EFCore/Extensions/EntityTypeExtensions.cs deleted file mode 100644 index b99fbb127ce..00000000000 --- a/src/EFCore/Extensions/EntityTypeExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// 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.Metadata.Internal; - -// ReSharper disable once CheckNamespace -namespace Microsoft.EntityFrameworkCore; - -/// -/// Entity type extension methods for . -/// -[Obsolete("Use IReadOnlyEntityType")] // Delete with defining query -public static class EntityTypeExtensions -{ - /// - /// Gets the LINQ query used as the default source for queries of this type. - /// - /// The entity type to get the defining query for. - /// The LINQ query used as the default source. - [Obsolete("Use InMemoryEntityTypeExtensions.GetInMemoryQuery")] - public static LambdaExpression? GetDefiningQuery(this IEntityType entityType) - => (LambdaExpression?)entityType[CoreAnnotationNames.DefiningQuery]; -} diff --git a/src/EFCore/Extensions/MutableEntityTypeExtensions.cs b/src/EFCore/Extensions/MutableEntityTypeExtensions.cs deleted file mode 100644 index 395b1aa72eb..00000000000 --- a/src/EFCore/Extensions/MutableEntityTypeExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -// 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.Metadata.Internal; - -// ReSharper disable once CheckNamespace -namespace Microsoft.EntityFrameworkCore; - -/// -/// Extension methods for . -/// -[Obsolete("Use IMutableEntityType")] // Delete with defining query -public static class MutableEntityTypeExtensions -{ - /// - /// Sets the LINQ query used as the default source for queries of this type. - /// - /// The entity type. - /// The LINQ query used as the default source. - [Obsolete("Use InMemoryEntityTypeExtensions.SetInMemoryQuery")] - public static void SetDefiningQuery( - this IMutableEntityType entityType, - LambdaExpression? definingQuery) - => ((EntityType)entityType).SetDefiningQuery(definingQuery, ConfigurationSource.Explicit); -} diff --git a/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs b/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs index 3cee16b13e4..b84fb98a562 100644 --- a/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs +++ b/src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs @@ -425,21 +425,6 @@ public virtual EntityTypeBuilder Ignore(Expression HasQueryFilter(Expression>? filter) => (EntityTypeBuilder)base.HasQueryFilter(filter); - /// - /// Configures a query used to provide data for a keyless entity type. - /// - /// The query that will provide the underlying data for the keyless entity type. - /// The same builder instance so that multiple calls can be chained. - [Obsolete("Use InMemoryEntityTypeBuilderExtensions.ToInMemoryQuery")] - public virtual EntityTypeBuilder ToQuery(Expression>> query) - { - Check.NotNull(query, nameof(query)); - - Builder.HasDefiningQuery(query, ConfigurationSource.Explicit); - - return this; - } - /// /// Configures an unnamed index on the specified properties. /// If there is an existing index on the given list of properties, diff --git a/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs b/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs index 3ae88c32f91..e7014919485 100644 --- a/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Builders/IConventionEntityTypeBuilder.cs @@ -878,26 +878,6 @@ bool CanHaveTrigger( /// if the given query filter can be set. bool CanSetQueryFilter(LambdaExpression? filter, bool fromDataAnnotation = false); - /// - /// Configures a query used to provide data for a keyless entity type. - /// - /// The query that will provide the underlying data for the keyless entity type. - /// Indicates whether the configuration was specified using a data annotation. - /// - /// The same builder instance if the query was set, otherwise. - /// - [Obsolete("Use InMemoryEntityTypeBuilderExtensions.ToInMemoryQuery")] - IConventionEntityTypeBuilder? HasDefiningQuery(LambdaExpression? query, bool fromDataAnnotation = false); - - /// - /// Returns a value indicating whether the given defining query can be set from the current configuration source. - /// - /// The query that will provide the underlying data for the keyless entity type. - /// Indicates whether the configuration was specified using a data annotation. - /// if the given defining query can be set. - [Obsolete("Use InMemoryEntityTypeBuilderExtensions.CanSetInMemoryQuery")] - bool CanSetDefiningQuery(LambdaExpression? query, bool fromDataAnnotation = false); - /// /// Configures the to be used for this entity type. /// This strategy indicates how the context detects changes to properties for an instance of the entity type. diff --git a/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs b/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs index ff39b266bf0..e3684e0b68f 100644 --- a/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs +++ b/src/EFCore/Metadata/Conventions/RuntimeModelConvention.cs @@ -293,9 +293,6 @@ protected virtual void ProcessEntityTypeAnnotations( { if (CoreAnnotationNames.AllNames.Contains(key) && key != CoreAnnotationNames.QueryFilter -#pragma warning disable CS0612 // Type or member is obsolete - && key != CoreAnnotationNames.DefiningQuery -#pragma warning restore CS0612 // Type or member is obsolete && key != CoreAnnotationNames.DiscriminatorMappingComplete) { annotations.Remove(key); @@ -307,14 +304,6 @@ protected virtual void ProcessEntityTypeAnnotations( annotations[CoreAnnotationNames.QueryFilter] = new QueryRootRewritingExpressionVisitor(runtimeEntityType.Model).Rewrite((Expression)queryFilter!); } - -#pragma warning disable CS0612 // Type or member is obsolete - if (annotations.TryGetValue(CoreAnnotationNames.DefiningQuery, out var definingQuery)) - { - annotations[CoreAnnotationNames.DefiningQuery] = -#pragma warning restore CS0612 // Type or member is obsolete - new QueryRootRewritingExpressionVisitor(runtimeEntityType.Model).Rewrite((Expression)definingQuery!); - } } } diff --git a/src/EFCore/Metadata/IConventionModel.cs b/src/EFCore/Metadata/IConventionModel.cs index c31023226d8..6a149ee47c3 100644 --- a/src/EFCore/Metadata/IConventionModel.cs +++ b/src/EFCore/Metadata/IConventionModel.cs @@ -133,6 +133,7 @@ public interface IConventionModel : IReadOnlyModel, IConventionAnnotatable /// The defining entity type. /// Indicates whether the configuration was specified using a data annotation. /// The new entity type. + [Obsolete] IConventionEntityType? AddEntityType( string name, string definingNavigationName, @@ -147,6 +148,7 @@ public interface IConventionModel : IReadOnlyModel, IConventionAnnotatable /// The defining entity type. /// Indicates whether the configuration was specified using a data annotation. /// The new entity type. + [Obsolete] IConventionEntityType? AddEntityType( [DynamicallyAccessedMembers(IEntityType.DynamicallyAccessedMemberTypes)] Type type, string definingNavigationName, diff --git a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs index cb6e7a32c61..1246a351809 100644 --- a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs +++ b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs @@ -195,15 +195,6 @@ public static class CoreAnnotationNames /// public const string QueryFilter = "QueryFilter"; - /// - /// 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. - /// - [Obsolete] // Remove with defining query - public const string DefiningQuery = "DefiningQuery"; - /// /// 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 @@ -411,9 +402,6 @@ public static class CoreAnnotationNames AfterSaveBehavior, BeforeSaveBehavior, QueryFilter, -#pragma warning disable CS0612 // Type or member is obsolete - DefiningQuery, -#pragma warning restore CS0612 // Type or member is obsolete EagerLoaded, LazyLoadingEnabled, ProviderClrType, diff --git a/src/EFCore/Metadata/Internal/EntityType.cs b/src/EFCore/Metadata/Internal/EntityType.cs index a0133ed02b4..79cf4767af7 100644 --- a/src/EFCore/Metadata/Internal/EntityType.cs +++ b/src/EFCore/Metadata/Internal/EntityType.cs @@ -2918,16 +2918,6 @@ public virtual PropertyAccessMode GetNavigationAccessMode() public virtual ConfigurationSource? GetQueryFilterConfigurationSource() => FindAnnotation(CoreAnnotationNames.QueryFilter)?.GetConfigurationSource(); - /// - /// 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. - /// - [Obsolete] - public virtual LambdaExpression? SetDefiningQuery(LambdaExpression? definingQuery, ConfigurationSource configurationSource) - => (LambdaExpression?)SetOrRemoveAnnotation(CoreAnnotationNames.DefiningQuery, definingQuery, configurationSource)?.Value; - /// /// 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/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs index 7f04bcd9f9e..39f503cab8e 100644 --- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs @@ -1322,38 +1322,6 @@ public virtual bool CanSetQueryFilter(LambdaExpression? filter, ConfigurationSou => configurationSource.Overrides(Metadata.GetQueryFilterConfigurationSource()) || Metadata.GetQueryFilter() == filter; - /// - /// 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. - /// - [Obsolete] - public virtual InternalEntityTypeBuilder? HasDefiningQuery( - LambdaExpression? query, - ConfigurationSource configurationSource) - { - if (CanSetDefiningQuery(query, configurationSource)) - { - Metadata.SetDefiningQuery(query, configurationSource); - - return this; - } - - return null; - } - - /// - /// 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. - /// - [Obsolete] - public virtual bool CanSetDefiningQuery(LambdaExpression? query, ConfigurationSource configurationSource) - => configurationSource.Overrides(Metadata.GetDefiningQueryConfigurationSource()) - || Metadata.GetDefiningQuery() == query; - /// /// 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 @@ -5318,28 +5286,6 @@ bool IConventionEntityTypeBuilder.CanHaveTrigger(string modelName, bool fromData bool IConventionEntityTypeBuilder.CanSetQueryFilter(LambdaExpression? filter, bool fromDataAnnotation) => CanSetQueryFilter(filter, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); - /// - /// 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. - /// - [DebuggerStepThrough] - [Obsolete] - IConventionEntityTypeBuilder? IConventionEntityTypeBuilder.HasDefiningQuery(LambdaExpression? query, bool fromDataAnnotation) - => HasDefiningQuery(query, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); - - /// - /// 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. - /// - [DebuggerStepThrough] - [Obsolete] - bool IConventionEntityTypeBuilder.CanSetDefiningQuery(LambdaExpression? query, bool fromDataAnnotation) - => CanSetDefiningQuery(query, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention); - /// /// 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/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 0c4d3874bfe..5752032680e 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -200,9 +200,8 @@ protected override Expression VisitExtension(Expression extensionExpression) { case EntityQueryRootExpression entityQueryRootExpression: var entityType = entityQueryRootExpression.EntityType; -#pragma warning disable CS0618 // Type or member is obsolete - var definingQuery = entityType.GetDefiningQuery(); -#pragma warning restore CS0618 // Type or member is obsolete + // TODO: Move to InMemory #21624 + var definingQuery = (LambdaExpression?)entityType["InMemory:DefiningQuery"]; NavigationExpansionExpression navigationExpansionExpression; if (definingQuery != null // Apply defining query only when it is not custom query root @@ -1120,15 +1119,14 @@ private NavigationExpansionExpression ProcessInclude( { if (source.PendingSelector is NavigationTreeExpression { Value: EntityReference entityReference }) { -#pragma warning disable CS0618 // Type or member is obsolete - if (entityReference.EntityType.GetDefiningQuery() != null) + // TODO: Move to InMemory #21624 + if (entityReference.EntityType["InMemory:DefiningQuery"] != null) { throw new InvalidOperationException( #pragma warning disable CS0612 // Type or member is obsolete CoreStrings.IncludeOnEntityWithDefiningQueryNotSupported(expression, entityReference.EntityType.DisplayName())); #pragma warning restore CS0612 // Type or member is obsolete } -#pragma warning restore CS0618 // Type or member is obsolete if (expression is ConstantExpression { Value: string navigationChain }) { diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs index 033af3a37ff..501d53b2301 100644 --- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs @@ -150,12 +150,6 @@ public void Test_new_annotations_handled_for_entity_types() + " })") }, { -#pragma warning disable CS0612 // Type or member is obsolete - CoreAnnotationNames.DefiningQuery, -#pragma warning restore CS0612 // Type or member is obsolete - (Expression.Lambda(Expression.Constant(null)), _toNullTable) - }, - { RelationalAnnotationNames.ViewName, ("MyView", _toNullTable + ";" + _nl @@ -202,9 +196,6 @@ public void Test_new_annotations_handled_for_properties() CoreAnnotationNames.EagerLoaded, CoreAnnotationNames.LazyLoadingEnabled, CoreAnnotationNames.QueryFilter, -#pragma warning disable CS0612 // Type or member is obsolete - CoreAnnotationNames.DefiningQuery, -#pragma warning restore CS0612 // Type or member is obsolete CoreAnnotationNames.DiscriminatorProperty, CoreAnnotationNames.DiscriminatorValue, CoreAnnotationNames.InverseNavigations, diff --git a/test/EFCore.InMemory.FunctionalTests/Query/AdHocQueryFiltersQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/AdHocQueryFiltersQueryInMemoryTest.cs index 92f3fd6e22a..538d96349b8 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/AdHocQueryFiltersQueryInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/AdHocQueryFiltersQueryInMemoryTest.cs @@ -5,6 +5,130 @@ namespace Microsoft.EntityFrameworkCore.Query; public class AdHocQueryFiltersQueryInMemoryTest(NonSharedFixture fixture) : AdHocQueryFiltersQueryTestBase(fixture) { + #region 19708 + + [ConditionalFact] + public virtual async Task GroupJoin_SelectMany_gets_flattened() + { + var contextFactory = await InitializeAsync(seed: c => c.SeedAsync()); + using (var context = contextFactory.CreateContext()) + { + var query = context.CustomerFilters.ToList(); + } + + using (var context = contextFactory.CreateContext()) + { + var query = context.Set().ToList(); + + Assert.Collection( + query, + t => AssertCustomerView(t, 1, "First", 1, "FirstChild"), + t => AssertCustomerView(t, 2, "Second", 2, "SecondChild1"), + t => AssertCustomerView(t, 2, "Second", 3, "SecondChild2"), + t => AssertCustomerView(t, 3, "Third", null, "")); + + static void AssertCustomerView( + Context19708.CustomerView19708 actual, + int id, + string name, + int? customerMembershipId, + string customerMembershipName) + { + Assert.Equal(id, actual.Id); + Assert.Equal(name, actual.Name); + Assert.Equal(customerMembershipId, actual.CustomerMembershipId); + Assert.Equal(customerMembershipName, actual.CustomerMembershipName); + } + } + } + + protected class Context19708(DbContextOptions options) : DbContext(options) + { + public DbSet Customers { get; set; } + public DbSet CustomerMemberships { get; set; } + public DbSet CustomerFilters { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .HasQueryFilter( + e => (from a in (from c in Customers + join cm in CustomerMemberships on c.Id equals cm.CustomerId into g + from cm in g.DefaultIfEmpty() + select new { c.Id, CustomerMembershipId = (int?)cm.Id }) + where a.CustomerMembershipId != null && a.Id == e.CustomerId + select a).Count() + > 0) + .HasKey(e => e.CustomerId); + + modelBuilder.Entity().HasNoKey().ToInMemoryQuery(Build_Customers_Sql_View_InMemory()); + } + + public Task SeedAsync() + { + var customer1 = new Customer19708 { Name = "First" }; + var customer2 = new Customer19708 { Name = "Second" }; + var customer3 = new Customer19708 { Name = "Third" }; + + var customerMembership1 = new CustomerMembership19708 { Name = "FirstChild", Customer = customer1 }; + var customerMembership2 = new CustomerMembership19708 { Name = "SecondChild1", Customer = customer2 }; + var customerMembership3 = new CustomerMembership19708 { Name = "SecondChild2", Customer = customer2 }; + + AddRange(customer1, customer2, customer3); + AddRange(customerMembership1, customerMembership2, customerMembership3); + + return SaveChangesAsync(); + } + + private Expression>> Build_Customers_Sql_View_InMemory() + { + Expression>> query = () => + from customer in Customers + join customerMembership in CustomerMemberships on customer.Id equals customerMembership.CustomerId into + nullableCustomerMemberships + from customerMembership in nullableCustomerMemberships.DefaultIfEmpty() + select new CustomerView19708 + { + Id = customer.Id, + Name = customer.Name, + CustomerMembershipId = customerMembership != null ? customerMembership.Id : default(int?), + CustomerMembershipName = customerMembership != null ? customerMembership.Name : "" + }; + return query; + } + + public class Customer19708 + { + public int Id { get; set; } + public string Name { get; set; } + } + + public class CustomerMembership19708 + { + public int Id { get; set; } + public string Name { get; set; } + + public int CustomerId { get; set; } + public Customer19708 Customer { get; set; } + } + + public class CustomerFilter19708 + { + public int CustomerId { get; set; } + public int CustomerMembershipId { get; set; } + } + + public class CustomerView19708 + { + public int Id { get; set; } + public string Name { get; set; } + public int? CustomerMembershipId { get; set; } + public string CustomerMembershipName { get; set; } + } + } + + #endregion + protected override ITestStoreFactory TestStoreFactory => InMemoryTestStoreFactory.Instance; } diff --git a/test/EFCore.InMemory.FunctionalTests/Scaffolding/CompiledModelInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Scaffolding/CompiledModelInMemoryTest.cs index dbf56c02464..4c0e39444e7 100644 --- a/test/EFCore.InMemory.FunctionalTests/Scaffolding/CompiledModelInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Scaffolding/CompiledModelInMemoryTest.cs @@ -8,6 +8,7 @@ using System.Runtime.CompilerServices; using System.Text.Json; using Microsoft.EntityFrameworkCore.Design.Internal; +using Microsoft.EntityFrameworkCore.InMemory.Internal; using Microsoft.EntityFrameworkCore.InMemory.Storage.Internal; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata.Internal; @@ -307,7 +308,7 @@ public virtual Task Throws_for_query_filter() [ConditionalFact] public virtual Task Throws_for_defining_query() => Test( - expectedExceptionMessage: DesignStrings.CompiledModelDefiningQuery("object")); + expectedExceptionMessage: InMemoryStrings.CompiledModelDefiningQuery("object")); public class DefiningQueryContext(DbContextOptions options) : DbContext(options) { diff --git a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs index 7936924eaa6..1747d5dbca4 100644 --- a/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs +++ b/test/EFCore.Relational.Tests/Migrations/Internal/MigrationsModelDifferTest.cs @@ -43,19 +43,6 @@ public void Model_differ_does_not_detect_views_with_weak_types() }), upOperations => Assert.Equal(0, upOperations.Count)); - [ConditionalFact] - public void Model_differ_does_not_detect_defining_queries() - { - DbContext context = null; - Execute( - _ => { }, -#pragma warning disable CS0618 // Type or member is obsolete - modelBuilder => modelBuilder.Entity().HasNoKey().ToQuery( - () => context.Set().FromSqlRaw("SELECT * FROM Vista")), -#pragma warning restore CS0618 // Type or member is obsolete - result => Assert.Empty(result)); - } - [ConditionalFact] public void Model_differ_does_not_detect_queries() => Execute( diff --git a/test/EFCore.Specification.Tests/Query/AdHocQueryFiltersQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/AdHocQueryFiltersQueryTestBase.cs index 2b544ce67cd..24b1bcc0b35 100644 --- a/test/EFCore.Specification.Tests/Query/AdHocQueryFiltersQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/AdHocQueryFiltersQueryTestBase.cs @@ -417,132 +417,6 @@ public class User18759 #endregion - #region 19708 - - [ConditionalFact] - public virtual async Task GroupJoin_SelectMany_gets_flattened() - { - var contextFactory = await InitializeAsync(seed: c => c.SeedAsync()); - using (var context = contextFactory.CreateContext()) - { - var query = context.CustomerFilters.ToList(); - } - - using (var context = contextFactory.CreateContext()) - { - var query = context.Set().ToList(); - - Assert.Collection( - query, - t => AssertCustomerView(t, 1, "First", 1, "FirstChild"), - t => AssertCustomerView(t, 2, "Second", 2, "SecondChild1"), - t => AssertCustomerView(t, 2, "Second", 3, "SecondChild2"), - t => AssertCustomerView(t, 3, "Third", null, "")); - - static void AssertCustomerView( - Context19708.CustomerView19708 actual, - int id, - string name, - int? customerMembershipId, - string customerMembershipName) - { - Assert.Equal(id, actual.Id); - Assert.Equal(name, actual.Name); - Assert.Equal(customerMembershipId, actual.CustomerMembershipId); - Assert.Equal(customerMembershipName, actual.CustomerMembershipName); - } - } - } - - protected class Context19708(DbContextOptions options) : DbContext(options) - { - public DbSet Customers { get; set; } - public DbSet CustomerMemberships { get; set; } - public DbSet CustomerFilters { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity() - .HasQueryFilter( - e => (from a in (from c in Customers - join cm in CustomerMemberships on c.Id equals cm.CustomerId into g - from cm in g.DefaultIfEmpty() - select new { c.Id, CustomerMembershipId = (int?)cm.Id }) - where a.CustomerMembershipId != null && a.Id == e.CustomerId - select a).Count() - > 0) - .HasKey(e => e.CustomerId); - -#pragma warning disable CS0618 // Type or member is obsolete - modelBuilder.Entity().HasNoKey().ToQuery(Build_Customers_Sql_View_InMemory()); -#pragma warning restore CS0618 // Type or member is obsolete - } - - public Task SeedAsync() - { - var customer1 = new Customer19708 { Name = "First" }; - var customer2 = new Customer19708 { Name = "Second" }; - var customer3 = new Customer19708 { Name = "Third" }; - - var customerMembership1 = new CustomerMembership19708 { Name = "FirstChild", Customer = customer1 }; - var customerMembership2 = new CustomerMembership19708 { Name = "SecondChild1", Customer = customer2 }; - var customerMembership3 = new CustomerMembership19708 { Name = "SecondChild2", Customer = customer2 }; - - AddRange(customer1, customer2, customer3); - AddRange(customerMembership1, customerMembership2, customerMembership3); - - return SaveChangesAsync(); - } - - private Expression>> Build_Customers_Sql_View_InMemory() - { - Expression>> query = () => - from customer in Customers - join customerMembership in CustomerMemberships on customer.Id equals customerMembership.CustomerId into - nullableCustomerMemberships - from customerMembership in nullableCustomerMemberships.DefaultIfEmpty() - select new CustomerView19708 - { - Id = customer.Id, - Name = customer.Name, - CustomerMembershipId = customerMembership != null ? customerMembership.Id : default(int?), - CustomerMembershipName = customerMembership != null ? customerMembership.Name : "" - }; - return query; - } - - public class Customer19708 - { - public int Id { get; set; } - public string Name { get; set; } - } - - public class CustomerMembership19708 - { - public int Id { get; set; } - public string Name { get; set; } - - public int CustomerId { get; set; } - public Customer19708 Customer { get; set; } - } - - public class CustomerFilter19708 - { - public int CustomerId { get; set; } - public int CustomerMembershipId { get; set; } - } - - public class CustomerView19708 - { - public int Id { get; set; } - public string Name { get; set; } - public int? CustomerMembershipId { get; set; } - public string CustomerMembershipName { get; set; } - } - } - - #endregion - #region 26428 #nullable enable diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocQueryFiltersQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocQueryFiltersQuerySqlServerTest.cs index 48eec0c80e9..286113b1b0a 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocQueryFiltersQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocQueryFiltersQuerySqlServerTest.cs @@ -264,31 +264,6 @@ WHERE [u].[Id] IS NOT NULL """); } - public override async Task GroupJoin_SelectMany_gets_flattened() - { - await base.GroupJoin_SelectMany_gets_flattened(); - - AssertSql( - """ -SELECT [c].[CustomerId], [c].[CustomerMembershipId] -FROM [CustomerFilters] AS [c] -WHERE ( - SELECT COUNT(*) - FROM [Customers] AS [c0] - LEFT JOIN [CustomerMemberships] AS [c1] ON [c0].[Id] = [c1].[CustomerId] - WHERE [c1].[Id] IS NOT NULL AND [c0].[Id] = [c].[CustomerId]) > 0 -""", - // - """ -SELECT [c].[Id], [c].[Name], [c0].[Id] AS [CustomerMembershipId], CASE - WHEN [c0].[Id] IS NOT NULL THEN [c0].[Name] - ELSE N'' -END AS [CustomerMembershipName] -FROM [Customers] AS [c] -LEFT JOIN [CustomerMemberships] AS [c0] ON [c].[Id] = [c0].[CustomerId] -"""); - } - public override async Task Group_by_multiple_aggregate_joining_different_tables(bool async) { await base.Group_by_multiple_aggregate_joining_different_tables(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/RawSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/RawSqlServerTest.cs deleted file mode 100644 index d984363098a..00000000000 --- a/test/EFCore.SqlServer.FunctionalTests/Query/RawSqlServerTest.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -// ReSharper disable InconsistentNaming - -namespace Microsoft.EntityFrameworkCore.Query; - -#nullable disable - -public class RawSqlServerTest(NonSharedFixture fixture) : NonSharedModelTestBase(fixture), IClassFixture -{ - // Issue #13346, #24623 - [ConditionalFact] - public virtual async Task ToQuery_can_use_FromSqlRaw() - { - var contextFactory = await InitializeAsync(seed: c => c.SeedAsync()); - using var context = contextFactory.CreateContext(); - var query = context.Set().ToList(); - - Assert.Equal(4, query.Count); - - AssertSql( - """ -SELECT o.Amount From Orders AS o -- RAW -"""); - } - - public class Context13346(DbContextOptions options) : DbContext(options) - { - public virtual DbSet Orders { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { -#pragma warning disable CS0618 // Type or member is obsolete - modelBuilder.Entity() - .HasNoKey() - .ToQuery(() => Set().FromSqlRaw("SELECT o.Amount From Orders AS o -- RAW")); -#pragma warning restore CS0618 // Type or member is obsolete - } - - public Task SeedAsync() - { - AddRange( - new Order { Amount = 1 }, - new Order { Amount = 2 }, - new Order { Amount = 3 }, - new Order { Amount = 4 } - ); - - return SaveChangesAsync(); - } - - public class Order - { - public int Id { get; set; } - public int Amount { get; set; } - } - - public class OrderSummary - { - public int Amount { get; set; } - } - } - - protected override string StoreName - => "RawSqlServerTest"; - - protected TestSqlLoggerFactory TestSqlLoggerFactory - => (TestSqlLoggerFactory)ListLoggerFactory; - - protected override ITestStoreFactory TestStoreFactory - => SqlServerTestStoreFactory.Instance; - - protected void AssertSql(params string[] expected) - => TestSqlLoggerFactory.AssertBaseline(expected); - - protected void ClearLog() - => TestSqlLoggerFactory.Clear(); -} diff --git a/test/EFCore.Tests/Metadata/Conventions/ConventionSetBuilderTests.cs b/test/EFCore.Tests/Metadata/Conventions/ConventionSetBuilderTests.cs index 5eb545a8495..c959423f25f 100644 --- a/test/EFCore.Tests/Metadata/Conventions/ConventionSetBuilderTests.cs +++ b/test/EFCore.Tests/Metadata/Conventions/ConventionSetBuilderTests.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel.DataAnnotations.Schema; -using Microsoft.EntityFrameworkCore.InMemory.Metadata.Conventions; // ReSharper disable InconsistentNaming namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;