From 9a1b7fc0189132840c4434ad9f161905dafa9e7c Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Tue, 16 Jan 2024 22:35:54 +0000 Subject: [PATCH] Make AdHocMapper and RelationalAdHocMapper public (#32839) Part of #32680 --- ...ntityFrameworkRelationalServicesBuilder.cs | 3 +- .../Internal/RelationalAdHocMapper.cs | 43 -------- .../Metadata/RelationalAdHocMapper.cs | 51 +++++++++ .../RelationalAdHocMapperDependencies.cs | 41 +++++++ .../EntityFrameworkServicesBuilder.cs | 3 +- src/EFCore/Metadata/AdHocMapper.cs | 104 ++++++++++++++++++ .../Metadata/AdHocMapperDependencies.cs | 55 +++++++++ src/EFCore/Metadata/IAdHocMapper.cs | 12 ++ src/EFCore/Metadata/Internal/AdHocMapper.cs | 97 ---------------- 9 files changed, 267 insertions(+), 142 deletions(-) delete mode 100644 src/EFCore.Relational/Metadata/Internal/RelationalAdHocMapper.cs create mode 100644 src/EFCore.Relational/Metadata/RelationalAdHocMapper.cs create mode 100644 src/EFCore.Relational/Metadata/RelationalAdHocMapperDependencies.cs create mode 100644 src/EFCore/Metadata/AdHocMapper.cs create mode 100644 src/EFCore/Metadata/AdHocMapperDependencies.cs delete mode 100644 src/EFCore/Metadata/Internal/AdHocMapper.cs diff --git a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs index b0dbf2b978e..688f805326d 100644 --- a/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs +++ b/src/EFCore.Relational/Infrastructure/EntityFrameworkRelationalServicesBuilder.cs @@ -224,7 +224,8 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices() .AddDependencyScoped() .AddDependencyScoped() .AddDependencyScoped() - .AddDependencyScoped(); + .AddDependencyScoped() + .AddDependencyScoped(); return base.TryAddCoreServices(); } diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalAdHocMapper.cs b/src/EFCore.Relational/Metadata/Internal/RelationalAdHocMapper.cs deleted file mode 100644 index 9f01ea4239a..00000000000 --- a/src/EFCore.Relational/Metadata/Internal/RelationalAdHocMapper.cs +++ /dev/null @@ -1,43 +0,0 @@ -// 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 -{ - /// - /// 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() - { - 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.Relational/Metadata/RelationalAdHocMapper.cs b/src/EFCore.Relational/Metadata/RelationalAdHocMapper.cs new file mode 100644 index 00000000000..e59550c82b0 --- /dev/null +++ b/src/EFCore.Relational/Metadata/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; + +/// +/// Creates ad-hoc mappings of CLR types to entity types after the model has been built. +/// +/// +/// +/// The service lifetime is . This means that each +/// instance will use its own instance of this service. +/// The implementation may depend on other services registered with any lifetime. +/// The implementation does not need to be thread-safe. +/// +/// +/// See Implementation of database providers and extensions +/// for more information and examples. +/// +/// +public class RelationalAdHocMapper : AdHocMapper +{ + /// + /// Do not call this constructor directly from either provider or application code as it may change + /// as new dependencies are added. Instead, use this type in your constructor so that an instance + /// will be created and injected automatically by the dependency injection container. To create + /// an instance with some dependent services replaced, first resolve the object from the dependency + /// injection container, then replace selected services using the C# 'with' operator. Do not call + /// the constructor at any point in this process. + /// + public RelationalAdHocMapper(AdHocMapperDependencies dependencies, RelationalAdHocMapperDependencies relationalDependencies) + : base(dependencies) + { + RelationalDependencies = relationalDependencies; + } + + /// + /// Relational-specific dependencies for this service. + /// + protected virtual RelationalAdHocMapperDependencies RelationalDependencies { get; } + + /// + public override ConventionSet BuildConventionSet() + { + var conventionSet = base.BuildConventionSet(); + conventionSet.Remove(typeof(RelationalDbFunctionAttributeConvention)); + conventionSet.Remove(typeof(TableNameFromDbSetConvention)); + conventionSet.Remove(typeof(TableValuedDbFunctionConvention)); + return conventionSet; + } +} diff --git a/src/EFCore.Relational/Metadata/RelationalAdHocMapperDependencies.cs b/src/EFCore.Relational/Metadata/RelationalAdHocMapperDependencies.cs new file mode 100644 index 00000000000..cf1e44b4029 --- /dev/null +++ b/src/EFCore.Relational/Metadata/RelationalAdHocMapperDependencies.cs @@ -0,0 +1,41 @@ +// 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; + +/// +/// +/// Service dependencies parameter class for +/// +/// +/// This type is typically used by database providers (and other extensions). It is generally +/// not used in application code. +/// +/// +/// +/// +/// 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. +/// +/// +/// The service lifetime is . This means that each +/// instance will use its own instance of this service. +/// The implementation may depend on other services registered with any lifetime. +/// The implementation does not need to be thread-safe. +/// +/// +public sealed record RelationalAdHocMapperDependencies +{ + /// + /// 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. + /// + [EntityFrameworkInternal] + public RelationalAdHocMapperDependencies() + { + } +} diff --git a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs index 3311f7e1fbd..60b90236db4 100644 --- a/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs +++ b/src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs @@ -347,7 +347,8 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices() .AddDependencyScoped() .AddDependencyScoped() .AddDependencyScoped() - .AddDependencyScoped(); + .AddDependencyScoped() + .AddDependencyScoped(); ServiceCollectionMap.TryAddSingleton( new RegisteredServices(ServiceCollectionMap.ServiceCollection.Select(s => s.ServiceType))); diff --git a/src/EFCore/Metadata/AdHocMapper.cs b/src/EFCore/Metadata/AdHocMapper.cs new file mode 100644 index 00000000000..bfefdce8136 --- /dev/null +++ b/src/EFCore/Metadata/AdHocMapper.cs @@ -0,0 +1,104 @@ +// 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; + +namespace Microsoft.EntityFrameworkCore.Metadata; + +/// +/// Creates ad-hoc mappings of CLR types to entity types after the model has been built. +/// +/// +/// +/// The service lifetime is . This means that each +/// instance will use its own instance of this service. +/// The implementation may depend on other services registered with any lifetime. +/// The implementation does not need to be thread-safe. +/// +/// +/// See Implementation of database providers and extensions +/// for more information and examples. +/// +/// +public class AdHocMapper : IAdHocMapper +{ + private ConventionSet? _conventionSet; + + /// + /// Do not call this constructor directly from either provider or application code as it may change + /// as new dependencies are added. Instead, use this type in your constructor so that an instance + /// will be created and injected automatically by the dependency injection container. To create + /// an instance with some dependent services replaced, first resolve the object from the dependency + /// injection container, then replace selected services using the C# 'with' operator. Do not call + /// the constructor at any point in this process. + /// + public AdHocMapper(AdHocMapperDependencies dependencies) + { + Dependencies = dependencies; + } + + /// + /// Dependencies for this service. + /// + protected virtual AdHocMapperDependencies Dependencies { get; } + + /// + /// Builds the convention set to be used by the ad-hoc mapper. + /// + /// The convention set. + public virtual ConventionSet BuildConventionSet() + { + var conventionSet = Dependencies.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; + } + + private ConventionSet ConventionSet + => (_conventionSet ??= BuildConventionSet()); + + /// + public virtual RuntimeEntityType GetOrAddEntityType(Type clrType) + { + Check.DebugAssert(Dependencies.Model is RuntimeModel, "Ad-hoc entity types can only be used at runtime."); + + return ((RuntimeModel)Dependencies.Model).FindAdHocEntityType(clrType) ?? AddEntityType(clrType); + } + + private RuntimeEntityType AddEntityType(Type clrType) + { + var modelCreationDependencies = Dependencies.ModelCreationDependencies; + var modelBuilder = new ModelBuilder(ConventionSet, modelCreationDependencies.ModelDependencies); + modelBuilder.HasAnnotation(CoreAnnotationNames.AdHocModel, true); + modelBuilder.Entity(clrType).HasNoKey(); + var finalizedModel = modelBuilder.FinalizeModel(); + var runtimeModel = modelCreationDependencies.ModelRuntimeInitializer.Initialize( + finalizedModel, designTime: false, modelCreationDependencies.ValidationLogger); + + return ((RuntimeModel)Dependencies.Model).GetOrAddAdHocEntityType((RuntimeEntityType)runtimeModel.FindEntityType(clrType)!); + } +} diff --git a/src/EFCore/Metadata/AdHocMapperDependencies.cs b/src/EFCore/Metadata/AdHocMapperDependencies.cs new file mode 100644 index 00000000000..3f924a23e9e --- /dev/null +++ b/src/EFCore/Metadata/AdHocMapperDependencies.cs @@ -0,0 +1,55 @@ +// 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; + +/// +/// +/// Service dependencies parameter class for +/// +/// +/// This type is typically used by database providers (and other extensions). It is generally +/// not used in application code. +/// +/// +/// +/// +/// 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. +/// +/// +/// The service lifetime is . This means that each +/// instance will use its own instance of this service. +/// The implementation may depend on other services registered with any lifetime. +/// The implementation does not need to be thread-safe. +/// +/// +public sealed record AdHocMapperDependencies +{ + /// + /// 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. + /// + [EntityFrameworkInternal] + public AdHocMapperDependencies( + IModel model, + ModelCreationDependencies modelCreationDependencies) + { + Model = model; + ModelCreationDependencies = modelCreationDependencies; + } + + /// + /// The model source. + /// + public IModel Model { get; init; } + + /// + /// The convention set to use when creating the model. + /// + public ModelCreationDependencies ModelCreationDependencies { get; init; } +} diff --git a/src/EFCore/Metadata/IAdHocMapper.cs b/src/EFCore/Metadata/IAdHocMapper.cs index 3f8256368b7..3859b7ca8ba 100644 --- a/src/EFCore/Metadata/IAdHocMapper.cs +++ b/src/EFCore/Metadata/IAdHocMapper.cs @@ -6,6 +6,18 @@ namespace Microsoft.EntityFrameworkCore.Metadata; /// /// Creates ad-hoc mappings of CLR types to entity types after the model has been built. /// +/// +/// +/// The service lifetime is . This means that each +/// instance will use its own instance of this service. +/// The implementation may depend on other services registered with any lifetime. +/// The implementation does not need to be thread-safe. +/// +/// +/// See Implementation of database providers and extensions +/// for more information and examples. +/// +/// public interface IAdHocMapper { /// diff --git a/src/EFCore/Metadata/Internal/AdHocMapper.cs b/src/EFCore/Metadata/Internal/AdHocMapper.cs deleted file mode 100644 index d926b40c4c1..00000000000 --- a/src/EFCore/Metadata/Internal/AdHocMapper.cs +++ /dev/null @@ -1,97 +0,0 @@ -// 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 class AdHocMapper : IAdHocMapper -{ - private readonly IModel _model; - private readonly 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 AdHocMapper( - IModel model, - ModelCreationDependencies modelCreationDependencies) - { - _model = model; - _modelCreationDependencies = 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 virtual ConventionSet BuildConventionSet() - { - 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; - } - - 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 - /// 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 RuntimeEntityType GetOrAddEntityType(Type clrType) - { - Check.DebugAssert(_model is RuntimeModel, "Ad-hoc entity types can only be used at runtime."); - - return ((RuntimeModel)_model).FindAdHocEntityType(clrType) ?? AddEntityType(clrType); - } - - private RuntimeEntityType AddEntityType(Type clrType) - { - var modelBuilder = new ModelBuilder(ConventionSet, _modelCreationDependencies.ModelDependencies); - modelBuilder.HasAnnotation(CoreAnnotationNames.AdHocModel, true); - modelBuilder.Entity(clrType).HasNoKey(); - var finalizedModel = modelBuilder.FinalizeModel(); - var runtimeModel = _modelCreationDependencies.ModelRuntimeInitializer.Initialize( - finalizedModel, designTime: false, _modelCreationDependencies.ValidationLogger); - - return ((RuntimeModel)_model).GetOrAddAdHocEntityType((RuntimeEntityType)runtimeModel.FindEntityType(clrType)!); - } -}