Skip to content

Commit

Permalink
Make AdHocMapper and RelationalAdHocMapper public (#32839)
Browse files Browse the repository at this point in the history
Part of #32680
  • Loading branch information
ajcvickers committed Jan 16, 2024
1 parent 4211953 commit 9a1b7fc
Show file tree
Hide file tree
Showing 9 changed files with 267 additions and 142 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ public override EntityFrameworkServicesBuilder TryAddCoreServices()
.AddDependencyScoped<RelationalConnectionDependencies>()
.AddDependencyScoped<RelationalDatabaseDependencies>()
.AddDependencyScoped<RelationalQueryContextDependencies>()
.AddDependencyScoped<RelationalQueryCompilationContextDependencies>();
.AddDependencyScoped<RelationalQueryCompilationContextDependencies>()
.AddDependencyScoped<RelationalAdHocMapperDependencies>();

return base.TryAddCoreServices();
}
Expand Down
43 changes: 0 additions & 43 deletions src/EFCore.Relational/Metadata/Internal/RelationalAdHocMapper.cs

This file was deleted.

51 changes: 51 additions & 0 deletions src/EFCore.Relational/Metadata/RelationalAdHocMapper.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Creates ad-hoc mappings of CLR types to entity types after the model has been built.
/// </summary>
/// <remarks>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Scoped" />. This means that each
/// <see cref="DbContext" /> 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.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-providers">Implementation of database providers and extensions</see>
/// for more information and examples.
/// </para>
/// </remarks>
public class RelationalAdHocMapper : AdHocMapper
{
/// <summary>
/// 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.
/// </summary>
public RelationalAdHocMapper(AdHocMapperDependencies dependencies, RelationalAdHocMapperDependencies relationalDependencies)
: base(dependencies)
{
RelationalDependencies = relationalDependencies;
}

/// <summary>
/// Relational-specific dependencies for this service.
/// </summary>
protected virtual RelationalAdHocMapperDependencies RelationalDependencies { get; }

/// <inheritdoc />
public override ConventionSet BuildConventionSet()
{
var conventionSet = base.BuildConventionSet();
conventionSet.Remove(typeof(RelationalDbFunctionAttributeConvention));
conventionSet.Remove(typeof(TableNameFromDbSetConvention));
conventionSet.Remove(typeof(TableValuedDbFunctionConvention));
return conventionSet;
}
}
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// <para>
/// Service dependencies parameter class for <see cref="RelationalAdHocMapper" />
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
/// <remarks>
/// <para>
/// 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.
/// </para>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Scoped" />. This means that each
/// <see cref="DbContext" /> 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.
/// </para>
/// </remarks>
public sealed record RelationalAdHocMapperDependencies
{
/// <summary>
/// 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.
/// </summary>
[EntityFrameworkInternal]
public RelationalAdHocMapperDependencies()
{
}
}
3 changes: 2 additions & 1 deletion src/EFCore/Infrastructure/EntityFrameworkServicesBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,8 @@ public virtual EntityFrameworkServicesBuilder TryAddCoreServices()
.AddDependencyScoped<ValueGeneratorSelectorDependencies>()
.AddDependencyScoped<DatabaseDependencies>()
.AddDependencyScoped<ModelDependencies>()
.AddDependencyScoped<ModelCreationDependencies>();
.AddDependencyScoped<ModelCreationDependencies>()
.AddDependencyScoped<AdHocMapperDependencies>();

ServiceCollectionMap.TryAddSingleton<IRegisteredServices>(
new RegisteredServices(ServiceCollectionMap.ServiceCollection.Select(s => s.ServiceType)));
Expand Down
104 changes: 104 additions & 0 deletions src/EFCore/Metadata/AdHocMapper.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Creates ad-hoc mappings of CLR types to entity types after the model has been built.
/// </summary>
/// <remarks>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Scoped" />. This means that each
/// <see cref="DbContext" /> 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.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-providers">Implementation of database providers and extensions</see>
/// for more information and examples.
/// </para>
/// </remarks>
public class AdHocMapper : IAdHocMapper
{
private ConventionSet? _conventionSet;

/// <summary>
/// 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.
/// </summary>
public AdHocMapper(AdHocMapperDependencies dependencies)
{
Dependencies = dependencies;
}

/// <summary>
/// Dependencies for this service.
/// </summary>
protected virtual AdHocMapperDependencies Dependencies { get; }

/// <summary>
/// Builds the convention set to be used by the ad-hoc mapper.
/// </summary>
/// <returns>The convention set.</returns>
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());

/// <inheritdoc />
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)!);
}
}
55 changes: 55 additions & 0 deletions src/EFCore/Metadata/AdHocMapperDependencies.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// <para>
/// Service dependencies parameter class for <see cref="AdHocMapper" />
/// </para>
/// <para>
/// This type is typically used by database providers (and other extensions). It is generally
/// not used in application code.
/// </para>
/// </summary>
/// <remarks>
/// <para>
/// 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.
/// </para>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Scoped" />. This means that each
/// <see cref="DbContext" /> 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.
/// </para>
/// </remarks>
public sealed record AdHocMapperDependencies
{
/// <summary>
/// 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.
/// </summary>
[EntityFrameworkInternal]
public AdHocMapperDependencies(
IModel model,
ModelCreationDependencies modelCreationDependencies)
{
Model = model;
ModelCreationDependencies = modelCreationDependencies;
}

/// <summary>
/// The model source.
/// </summary>
public IModel Model { get; init; }

/// <summary>
/// The convention set to use when creating the model.
/// </summary>
public ModelCreationDependencies ModelCreationDependencies { get; init; }
}
12 changes: 12 additions & 0 deletions src/EFCore/Metadata/IAdHocMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ namespace Microsoft.EntityFrameworkCore.Metadata;
/// <summary>
/// Creates ad-hoc mappings of CLR types to entity types after the model has been built.
/// </summary>
/// <remarks>
/// <para>
/// The service lifetime is <see cref="ServiceLifetime.Scoped" />. This means that each
/// <see cref="DbContext" /> 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.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-providers">Implementation of database providers and extensions</see>
/// for more information and examples.
/// </para>
/// </remarks>
public interface IAdHocMapper
{
/// <summary>
Expand Down
Loading

0 comments on commit 9a1b7fc

Please sign in to comment.