diff --git a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs index 638ce909358..f9bf7bd9356 100644 --- a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs +++ b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using Microsoft.EntityFrameworkCore.Diagnostics.Internal; +using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Migrations.Internal; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Storage.Internal; @@ -185,6 +186,7 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices() TryAdd(); TryAdd(); TryAdd(); + TryAdd(); ServiceCollectionMap.GetInfrastructure() .AddDependencySingleton() diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalAdHocMapper.cs b/src/EFCore.Relational/Metadata/Internal/RelationalAdHocMapper.cs new file mode 100644 index 00000000000..1be9cb26363 --- /dev/null +++ b/src/EFCore.Relational/Metadata/Internal/RelationalAdHocMapper.cs @@ -0,0 +1,51 @@ +// 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. +/// +#pragma warning disable EF1001 // AdHocMapper should be made public +public class RelationalAdHocMapper : AdHocMapper +{ + private static readonly bool UseOldBehavior32680 = + AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32680", out var enabled32680) && enabled32680; + + /// + /// 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 RelationalAdHocMapper( + IModel model, + ModelCreationDependencies modelCreationDependencies) + : base(model, modelCreationDependencies) + { + } + + /// + /// 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 ConventionSet BuildConventionSet() + { + if (UseOldBehavior32680) + { + return base.BuildConventionSet(); + } + + var conventionSet = base.BuildConventionSet(); + conventionSet.Remove(typeof(RelationalDbFunctionAttributeConvention)); + conventionSet.Remove(typeof(TableNameFromDbSetConvention)); + conventionSet.Remove(typeof(TableValuedDbFunctionConvention)); + return conventionSet; + } +} +#pragma warning restore EF1001 diff --git a/src/EFCore/Metadata/Internal/AdHocMapper.cs b/src/EFCore/Metadata/Internal/AdHocMapper.cs index 539f7f9078f..d926b40c4c1 100644 --- a/src/EFCore/Metadata/Internal/AdHocMapper.cs +++ b/src/EFCore/Metadata/Internal/AdHocMapper.cs @@ -29,44 +29,47 @@ public AdHocMapper( _modelCreationDependencies = modelCreationDependencies; } - private ConventionSet ConventionSet + /// + /// 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 ConventionSet BuildConventionSet() { - get - { - if (_conventionSet == null) - { - _conventionSet = _modelCreationDependencies.ConventionSetBuilder.CreateConventionSet(); - _conventionSet.Remove(typeof(DbSetFindingConvention)); - _conventionSet.Remove(typeof(RelationshipDiscoveryConvention)); - _conventionSet.Remove(typeof(KeyDiscoveryConvention)); - _conventionSet.Remove(typeof(CascadeDeleteConvention)); - _conventionSet.Remove(typeof(ChangeTrackingStrategyConvention)); - _conventionSet.Remove(typeof(DeleteBehaviorAttributeConvention)); - _conventionSet.Remove(typeof(ForeignKeyAttributeConvention)); - _conventionSet.Remove(typeof(ForeignKeyIndexConvention)); - _conventionSet.Remove(typeof(ForeignKeyPropertyDiscoveryConvention)); - _conventionSet.Remove(typeof(IndexAttributeConvention)); - _conventionSet.Remove(typeof(KeyAttributeConvention)); - _conventionSet.Remove(typeof(KeylessAttributeConvention)); - _conventionSet.Remove(typeof(ManyToManyJoinEntityTypeConvention)); - _conventionSet.Remove(typeof(RequiredNavigationAttributeConvention)); - _conventionSet.Remove(typeof(NavigationBackingFieldAttributeConvention)); - _conventionSet.Remove(typeof(InversePropertyAttributeConvention)); - _conventionSet.Remove(typeof(NavigationEagerLoadingConvention)); - _conventionSet.Remove(typeof(NonNullableNavigationConvention)); - _conventionSet.Remove(typeof(NotMappedTypeAttributeConvention)); - _conventionSet.Remove(typeof(OwnedAttributeConvention)); - _conventionSet.Remove(typeof(QueryFilterRewritingConvention)); - _conventionSet.Remove(typeof(ServicePropertyDiscoveryConvention)); - _conventionSet.Remove(typeof(ValueGenerationConvention)); - _conventionSet.Remove(typeof(BaseTypeDiscoveryConvention)); - _conventionSet.Remove(typeof(DiscriminatorConvention)); - } + var conventionSet = _modelCreationDependencies.ConventionSetBuilder.CreateConventionSet(); + conventionSet.Remove(typeof(DbSetFindingConvention)); + conventionSet.Remove(typeof(RelationshipDiscoveryConvention)); + conventionSet.Remove(typeof(KeyDiscoveryConvention)); + conventionSet.Remove(typeof(CascadeDeleteConvention)); + conventionSet.Remove(typeof(ChangeTrackingStrategyConvention)); + conventionSet.Remove(typeof(DeleteBehaviorAttributeConvention)); + conventionSet.Remove(typeof(ForeignKeyAttributeConvention)); + conventionSet.Remove(typeof(ForeignKeyIndexConvention)); + conventionSet.Remove(typeof(ForeignKeyPropertyDiscoveryConvention)); + conventionSet.Remove(typeof(IndexAttributeConvention)); + conventionSet.Remove(typeof(KeyAttributeConvention)); + conventionSet.Remove(typeof(KeylessAttributeConvention)); + conventionSet.Remove(typeof(ManyToManyJoinEntityTypeConvention)); + conventionSet.Remove(typeof(RequiredNavigationAttributeConvention)); + conventionSet.Remove(typeof(NavigationBackingFieldAttributeConvention)); + conventionSet.Remove(typeof(InversePropertyAttributeConvention)); + conventionSet.Remove(typeof(NavigationEagerLoadingConvention)); + conventionSet.Remove(typeof(NonNullableNavigationConvention)); + conventionSet.Remove(typeof(NotMappedTypeAttributeConvention)); + conventionSet.Remove(typeof(OwnedAttributeConvention)); + conventionSet.Remove(typeof(QueryFilterRewritingConvention)); + conventionSet.Remove(typeof(ServicePropertyDiscoveryConvention)); + conventionSet.Remove(typeof(ValueGenerationConvention)); + conventionSet.Remove(typeof(BaseTypeDiscoveryConvention)); + conventionSet.Remove(typeof(DiscriminatorConvention)); - return _conventionSet; - } + return conventionSet; } + private ConventionSet ConventionSet + => (_conventionSet ??= BuildConventionSet()); + /// /// 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/test/EFCore.SqlServer.FunctionalTests/SqlServerEndToEndTest.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerEndToEndTest.cs index 41eff5ac533..69eb9fd3681 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerEndToEndTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerEndToEndTest.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Runtime.CompilerServices; @@ -216,6 +217,46 @@ private class ByteAdNum public string Lucy { get; set; } } + [ConditionalFact] // Issue #29931 + public void Can_use_SqlQuery_when_context_has_DbFunction() + { + using var testDatabase = SqlServerTestStore.CreateInitialized(DatabaseName); + var options = Fixture.CreateOptions(testDatabase); + using (var context = new DbFunctionContext(options)) + { + var result = context.Database + .SqlQueryRaw("SELECT Name from sys.databases") + .OrderBy(d => d.Name) + .ToList(); + } + } + + private class DbFunctionContext(DbContextOptions options) : DbContext(options) + { + [DbFunction("tvp", "dbo")] + public IQueryable Tvp(int? storeid) + => FromExpression(() => Tvp(storeid)); + + protected override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity().HasNoKey(); + } + + private class TvpResult + { + public int Id { get; set; } + + [Required] + public string Name { get; set; } + + [Column(TypeName = "decimal(18,2)")] + public decimal Total { get; set; } + } + + private class RawResult + { + public string Name { get; set; } + } + [ConditionalFact] public void Can_use_string_enum_or_byte_array_as_key() {