diff --git a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
index 415211e072a..97fcfa28b66 100644
--- a/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
+++ b/src/EFCore.Cosmos/Infrastructure/Internal/CosmosDbOptionExtension.cs
@@ -181,8 +181,8 @@ public virtual CosmosOptionsExtension WithConnectionString([CanBeNull] string? c
/// 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 string? DatabaseName
- => _databaseName;
+ public virtual string DatabaseName
+ => _databaseName!;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Design/Design/DesignTimeServiceCollectionExtensions.cs b/src/EFCore.Design/Design/DesignTimeServiceCollectionExtensions.cs
index f752814c8ea..88842a71f18 100644
--- a/src/EFCore.Design/Design/DesignTimeServiceCollectionExtensions.cs
+++ b/src/EFCore.Design/Design/DesignTimeServiceCollectionExtensions.cs
@@ -8,9 +8,7 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Diagnostics.Internal;
using Microsoft.EntityFrameworkCore.Infrastructure;
-using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Design;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
diff --git a/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs b/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs
index 5058b5801f2..6fe6a47e8c5 100644
--- a/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs
+++ b/src/EFCore.Design/Design/Internal/DesignTimeServicesBuilder.cs
@@ -51,9 +51,16 @@ public DesignTimeServicesBuilder(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual IServiceProvider Build([NotNull] DbContext context)
- {
- Check.NotNull(context, nameof(context));
+ => CreateServiceCollection(Check.NotNull(context, nameof(context))).BuildServiceProvider();
+ ///
+ /// 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 IServiceCollection CreateServiceCollection([NotNull] DbContext context)
+ {
var services = new ServiceCollection()
.AddEntityFrameworkDesignTimeServices(_reporter)
.AddDbContextDesignTimeServices(context);
@@ -61,8 +68,7 @@ public virtual IServiceProvider Build([NotNull] DbContext context)
ConfigureProviderServices(provider, services);
ConfigureReferencedServices(services, provider);
ConfigureUserServices(services);
-
- return services.BuildServiceProvider();
+ return services;
}
///
@@ -72,16 +78,22 @@ public virtual IServiceProvider Build([NotNull] DbContext context)
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual IServiceProvider Build([NotNull] string provider)
- {
- Check.NotEmpty(provider, nameof(provider));
+ => CreateServiceCollection(Check.NotEmpty(provider, nameof(provider))).BuildServiceProvider();
+ ///
+ /// 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 IServiceCollection CreateServiceCollection([NotNull] string provider)
+ {
var services = new ServiceCollection()
.AddEntityFrameworkDesignTimeServices(_reporter, GetApplicationServices);
ConfigureProviderServices(provider, services, throwOnError: true);
ConfigureReferencedServices(services, provider);
ConfigureUserServices(services);
-
- return services.BuildServiceProvider();
+ return services;
}
private IServiceProvider GetApplicationServices()
diff --git a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs
index f8659453cae..da98df67899 100644
--- a/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/RelationalScaffoldingModelFactory.cs
@@ -9,6 +9,7 @@
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
@@ -44,6 +45,7 @@ public class RelationalScaffoldingModelFactory : IScaffoldingModelFactory
private readonly ICSharpUtilities _cSharpUtilities;
private readonly IScaffoldingTypeMapper _scaffoldingTypeMapper;
private readonly LoggingDefinitions _loggingDefinitions;
+ private readonly IModelRuntimeInitializer _modelRuntimeInitializer;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -57,7 +59,8 @@ public RelationalScaffoldingModelFactory(
[NotNull] IPluralizer pluralizer,
[NotNull] ICSharpUtilities cSharpUtilities,
[NotNull] IScaffoldingTypeMapper scaffoldingTypeMapper,
- [NotNull] LoggingDefinitions loggingDefinitions)
+ [NotNull] LoggingDefinitions loggingDefinitions,
+ [NotNull] IModelRuntimeInitializer modelRuntimeInitializer)
{
Check.NotNull(reporter, nameof(reporter));
Check.NotNull(candidateNamingService, nameof(candidateNamingService));
@@ -65,6 +68,7 @@ public RelationalScaffoldingModelFactory(
Check.NotNull(cSharpUtilities, nameof(cSharpUtilities));
Check.NotNull(scaffoldingTypeMapper, nameof(scaffoldingTypeMapper));
Check.NotNull(loggingDefinitions, nameof(loggingDefinitions));
+ Check.NotNull(modelRuntimeInitializer, nameof(modelRuntimeInitializer));
_reporter = reporter;
_candidateNamingService = candidateNamingService;
@@ -72,6 +76,7 @@ public RelationalScaffoldingModelFactory(
_cSharpUtilities = cSharpUtilities;
_scaffoldingTypeMapper = scaffoldingTypeMapper;
_loggingDefinitions = loggingDefinitions;
+ _modelRuntimeInitializer = modelRuntimeInitializer;
}
///
@@ -108,7 +113,7 @@ public virtual IModel Create(DatabaseModel databaseModel, ModelReverseEngineerOp
VisitDatabaseModel(modelBuilder, databaseModel);
- return modelBuilder.FinalizeModel();
+ return _modelRuntimeInitializer.Initialize(modelBuilder.FinalizeModel(), null);
}
///
diff --git a/src/EFCore.Proxies/Proxies/Internal/IProxyFactory.cs b/src/EFCore.Proxies/Proxies/Internal/IProxyFactory.cs
index f41aa5ba2e8..24721fedba9 100644
--- a/src/EFCore.Proxies/Proxies/Internal/IProxyFactory.cs
+++ b/src/EFCore.Proxies/Proxies/Internal/IProxyFactory.cs
@@ -49,7 +49,7 @@ object CreateProxy(
///
Type CreateProxyType(
[NotNull] ProxiesOptionsExtension options,
- [NotNull] IEntityType entityType);
+ [NotNull] IReadOnlyEntityType entityType);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs b/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs
index 4800928633b..a0ac5286dc7 100644
--- a/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs
+++ b/src/EFCore.Proxies/Proxies/Internal/ProxyBindingRewriter.cs
@@ -68,175 +68,178 @@ public virtual void ProcessModelFinalizing(
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
{
var clrType = entityType.ClrType;
- if (!clrType.IsAbstract)
+ if (clrType.IsAbstract)
{
- if (clrType.IsSealed)
- {
- throw new InvalidOperationException(ProxiesStrings.ItsASeal(entityType.DisplayName()));
- }
+ continue;
+ }
- var proxyType = _proxyFactory.CreateProxyType(_options, (IEntityType)entityType);
+ if (clrType.IsSealed)
+ {
+ throw new InvalidOperationException(ProxiesStrings.ItsASeal(entityType.DisplayName()));
+ }
- // WARNING: This code is EF internal; it should not be copied. See #10789 #14554
+ var proxyType = _proxyFactory.CreateProxyType(_options, entityType);
+
+ // WARNING: This code is EF internal; it should not be copied. See #10789 #14554
#pragma warning disable EF1001 // Internal EF Core API usage.
- var binding = (InstantiationBinding?)entityType[CoreAnnotationNames.ConstructorBinding];
- if (binding == null)
- {
- _directBindingConvention.ProcessModelFinalizing(modelBuilder, context);
- }
+ var binding = ((EntityType)entityType).ConstructorBinding;
+ if (binding == null)
+ {
+ _directBindingConvention.ProcessModelFinalizing(modelBuilder, context);
+ binding = ((EntityType)entityType).ConstructorBinding!;
+ }
+
+ ((EntityType)entityType).SetConstructorBinding(
+ UpdateConstructorBindings(entityType, proxyType, binding),
+ ConfigurationSource.Convention);
- binding = (InstantiationBinding)entityType[CoreAnnotationNames.ConstructorBinding]!;
- UpdateConstructorBindings(CoreAnnotationNames.ConstructorBinding, binding);
+ binding = ((EntityType)entityType).ServiceOnlyConstructorBinding;
+ if (binding != null)
+ {
+ ((EntityType)entityType).SetServiceOnlyConstructorBinding(
+ UpdateConstructorBindings(entityType, proxyType, binding),
+ ConfigurationSource.Convention);
+ }
+#pragma warning restore EF1001 // Internal EF Core API usage.
- binding = (InstantiationBinding?)entityType[CoreAnnotationNames.ServiceOnlyConstructorBinding];
- if (binding != null)
+ foreach (var navigationBase in entityType.GetDeclaredNavigations()
+ .Concat(entityType.GetDeclaredSkipNavigations()))
+ {
+ if (navigationBase.PropertyInfo == null)
{
- UpdateConstructorBindings(CoreAnnotationNames.ServiceOnlyConstructorBinding, binding);
+ throw new InvalidOperationException(
+ ProxiesStrings.FieldProperty(navigationBase.Name, entityType.DisplayName()));
}
-#pragma warning restore EF1001 // Internal EF Core API usage.
- foreach (var navigationBase in entityType.GetDeclaredNavigations()
- .Concat(entityType.GetDeclaredSkipNavigations()))
+ if (_options.UseChangeTrackingProxies
+ && navigationBase.PropertyInfo.SetMethod?.IsReallyVirtual() == false)
{
- if (navigationBase.PropertyInfo == null)
- {
- throw new InvalidOperationException(
- ProxiesStrings.FieldProperty(navigationBase.Name, entityType.DisplayName()));
- }
+ throw new InvalidOperationException(
+ ProxiesStrings.NonVirtualProperty(navigationBase.Name, entityType.DisplayName()));
+ }
- if (_options.UseChangeTrackingProxies
- && navigationBase.PropertyInfo.SetMethod?.IsReallyVirtual() == false)
+ if (_options.UseLazyLoadingProxies)
+ {
+ if (!navigationBase.PropertyInfo.GetMethod.IsReallyVirtual()
+ && (!(navigationBase is INavigation navigation
+ && navigation.ForeignKey.IsOwnership)))
{
throw new InvalidOperationException(
ProxiesStrings.NonVirtualProperty(navigationBase.Name, entityType.DisplayName()));
}
- if (_options.UseLazyLoadingProxies)
- {
- if (!navigationBase.PropertyInfo.GetMethod.IsReallyVirtual()
- && (!(navigationBase is INavigation navigation
- && navigation.ForeignKey.IsOwnership)))
- {
- throw new InvalidOperationException(
- ProxiesStrings.NonVirtualProperty(navigationBase.Name, entityType.DisplayName()));
- }
-
- navigationBase.SetPropertyAccessMode(PropertyAccessMode.Field);
- }
+ navigationBase.SetPropertyAccessMode(PropertyAccessMode.Field);
}
+ }
- if (_options.UseChangeTrackingProxies)
+ if (_options.UseChangeTrackingProxies)
+ {
+ var indexerChecked = false;
+ foreach (var property in entityType.GetDeclaredProperties()
+ .Where(p => !p.IsShadowProperty()))
{
- var indexerChecked = false;
- foreach (var property in entityType.GetDeclaredProperties()
- .Where(p => !p.IsShadowProperty()))
+ if (property.IsIndexerProperty())
{
- if (property.IsIndexerProperty())
+ if (!indexerChecked)
{
- if (!indexerChecked)
- {
- indexerChecked = true;
+ indexerChecked = true;
- if (!property.PropertyInfo!.SetMethod.IsReallyVirtual())
+ if (!property.PropertyInfo!.SetMethod.IsReallyVirtual())
+ {
+ if (clrType.IsGenericType
+ && clrType.GetGenericTypeDefinition() == typeof(Dictionary<,>)
+ && clrType.GenericTypeArguments[0] == typeof(string))
{
- if (clrType.IsGenericType
- && clrType.GetGenericTypeDefinition() == typeof(Dictionary<,>)
- && clrType.GenericTypeArguments[0] == typeof(string))
- {
- if (entityType.GetProperties().Any(p => !p.IsPrimaryKey()))
- {
- throw new InvalidOperationException(
- ProxiesStrings.DictionaryCannotBeProxied(
- clrType.ShortDisplayName(),
- entityType.DisplayName(),
- typeof(IDictionary<,>).MakeGenericType(clrType.GenericTypeArguments)
- .ShortDisplayName()));
- }
- }
- else
+ if (entityType.GetProperties().Any(p => !p.IsPrimaryKey()))
{
throw new InvalidOperationException(
- ProxiesStrings.NonVirtualIndexerProperty(entityType.DisplayName()));
+ ProxiesStrings.DictionaryCannotBeProxied(
+ clrType.ShortDisplayName(),
+ entityType.DisplayName(),
+ typeof(IDictionary<,>).MakeGenericType(clrType.GenericTypeArguments)
+ .ShortDisplayName()));
}
}
- }
- }
- else
- {
- if (property.PropertyInfo == null)
- {
- throw new InvalidOperationException(
- ProxiesStrings.FieldProperty(property.Name, entityType.DisplayName()));
- }
-
- if (property.PropertyInfo.SetMethod?.IsReallyVirtual() == false)
- {
- throw new InvalidOperationException(
- ProxiesStrings.NonVirtualProperty(property.Name, entityType.DisplayName()));
+ else
+ {
+ throw new InvalidOperationException(
+ ProxiesStrings.NonVirtualIndexerProperty(entityType.DisplayName()));
+ }
}
}
}
- }
-
- void UpdateConstructorBindings(string bindingAnnotationName, InstantiationBinding binding)
- {
- if (_options.UseLazyLoadingProxies)
+ else
{
- foreach (var conflictingProperty in entityType.GetDerivedTypes()
- .SelectMany(e => e.GetDeclaredServiceProperties().Where(p => p.ClrType == typeof(ILazyLoader)))
- .ToList())
+ if (property.PropertyInfo == null)
{
- conflictingProperty.DeclaringEntityType.RemoveServiceProperty(conflictingProperty.Name);
+ throw new InvalidOperationException(
+ ProxiesStrings.FieldProperty(property.Name, entityType.DisplayName()));
}
- var serviceProperty = entityType.GetServiceProperties()
- .FirstOrDefault(e => e.ClrType == typeof(ILazyLoader));
- if (serviceProperty == null)
+ if (property.PropertyInfo.SetMethod?.IsReallyVirtual() == false)
{
- serviceProperty = entityType.AddServiceProperty(_lazyLoaderProperty);
- serviceProperty.SetParameterBinding(
- (ServiceParameterBinding)new LazyLoaderParameterBindingFactory(
- _lazyLoaderParameterBindingFactoryDependencies)
- .Bind(
- entityType,
- typeof(ILazyLoader),
- nameof(IProxyLazyLoader.LazyLoader)));
+ throw new InvalidOperationException(
+ ProxiesStrings.NonVirtualProperty(property.Name, entityType.DisplayName()));
}
-
- entityType.SetAnnotation(
- bindingAnnotationName,
- new FactoryMethodBinding(
- _proxyFactory,
- _createLazyLoadingProxyMethod,
- new List
- {
- new ContextParameterBinding(typeof(DbContext)),
- new EntityTypeParameterBinding(),
- new DependencyInjectionParameterBinding(
- typeof(ILazyLoader), typeof(ILazyLoader), (IPropertyBase)serviceProperty),
- new ObjectArrayParameterBinding(binding.ParameterBindings)
- },
- proxyType));
- }
- else
- {
- entityType.SetAnnotation(
- bindingAnnotationName,
- new FactoryMethodBinding(
- _proxyFactory,
- _createProxyMethod,
- new List
- {
- new ContextParameterBinding(typeof(DbContext)),
- new EntityTypeParameterBinding(),
- new ObjectArrayParameterBinding(binding.ParameterBindings)
- },
- proxyType));
}
}
}
}
}
}
+
+ private InstantiationBinding UpdateConstructorBindings(
+ IConventionEntityType entityType, Type proxyType, InstantiationBinding binding)
+ {
+ if (_options?.UseLazyLoadingProxies == true)
+ {
+ foreach (var conflictingProperty in entityType.GetDerivedTypes()
+ .SelectMany(e => e.GetDeclaredServiceProperties().Where(p => p.ClrType == typeof(ILazyLoader)))
+ .ToList())
+ {
+ conflictingProperty.DeclaringEntityType.RemoveServiceProperty(conflictingProperty.Name);
+ }
+
+ var serviceProperty = entityType.GetServiceProperties()
+ .FirstOrDefault(e => e.ClrType == typeof(ILazyLoader));
+ if (serviceProperty == null)
+ {
+ serviceProperty = entityType.AddServiceProperty(_lazyLoaderProperty);
+ serviceProperty.SetParameterBinding(
+ (ServiceParameterBinding)new LazyLoaderParameterBindingFactory(
+ _lazyLoaderParameterBindingFactoryDependencies)
+ .Bind(
+ entityType,
+ typeof(ILazyLoader),
+ nameof(IProxyLazyLoader.LazyLoader)));
+ }
+
+ return new FactoryMethodBinding(
+ _proxyFactory,
+ _createLazyLoadingProxyMethod,
+ new List
+ {
+ new ContextParameterBinding(typeof(DbContext)),
+ new EntityTypeParameterBinding(),
+ new DependencyInjectionParameterBinding(
+ typeof(ILazyLoader), typeof(ILazyLoader), (IPropertyBase)serviceProperty),
+ new ObjectArrayParameterBinding(binding.ParameterBindings)
+ },
+ proxyType);
+ }
+ else
+ {
+ return new FactoryMethodBinding(
+ _proxyFactory,
+ _createProxyMethod,
+ new List
+ {
+ new ContextParameterBinding(typeof(DbContext)),
+ new EntityTypeParameterBinding(),
+ new ObjectArrayParameterBinding(binding.ParameterBindings)
+ },
+ proxyType);
+ }
+ }
}
}
diff --git a/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs b/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs
index 313967d4b25..c3cddb2ff14 100644
--- a/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs
+++ b/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs
@@ -62,10 +62,10 @@ public virtual object Create(
///
public virtual Type CreateProxyType(
ProxiesOptionsExtension options,
- IEntityType entityType)
+ IReadOnlyEntityType entityType)
=> _generator.ProxyBuilder.CreateClassProxyType(
entityType.ClrType,
- GetInterfacesToProxy(options, entityType),
+ GetInterfacesToProxy(options, entityType.ClrType),
ProxyGenerationOptions.Default);
///
@@ -100,7 +100,7 @@ private object CreateLazyLoadingProxy(
object[] constructorArguments)
=> _generator.CreateClassProxy(
entityType.ClrType,
- GetInterfacesToProxy(options, entityType),
+ GetInterfacesToProxy(options, entityType.ClrType),
ProxyGenerationOptions.Default,
constructorArguments,
GetNotifyChangeInterceptors(options, entityType, new LazyLoadingInterceptor(entityType, loader)));
@@ -143,14 +143,14 @@ private object CreateProxy(
object[] constructorArguments)
=> _generator.CreateClassProxy(
entityType.ClrType,
- GetInterfacesToProxy(options, entityType),
+ GetInterfacesToProxy(options, entityType.ClrType),
ProxyGenerationOptions.Default,
constructorArguments,
GetNotifyChangeInterceptors(options, entityType));
private Type[] GetInterfacesToProxy(
ProxiesOptionsExtension options,
- IEntityType entityType)
+ Type type)
{
var interfacesToProxy = new List();
@@ -161,12 +161,12 @@ private Type[] GetInterfacesToProxy(
if (options.UseChangeTrackingProxies)
{
- if (!_notifyPropertyChangedInterface.IsAssignableFrom(entityType.ClrType))
+ if (!_notifyPropertyChangedInterface.IsAssignableFrom(type))
{
interfacesToProxy.Add(_notifyPropertyChangedInterface);
}
- if (!_notifyPropertyChangingInterface.IsAssignableFrom(entityType.ClrType))
+ if (!_notifyPropertyChangingInterface.IsAssignableFrom(type))
{
interfacesToProxy.Add(_notifyPropertyChangingInterface);
}
diff --git a/src/EFCore.Relational/Metadata/Conventions/DbFunctionTypeMappingConvention.cs b/src/EFCore.Relational/Metadata/Conventions/DbFunctionTypeMappingConvention.cs
index 46b471a136f..d727652938b 100644
--- a/src/EFCore.Relational/Metadata/Conventions/DbFunctionTypeMappingConvention.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/DbFunctionTypeMappingConvention.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
@@ -14,6 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
///
/// A convention configure type mapping for instances.
///
+ [Obsolete("Use IModelRuntimeInitializer.Initialize instead.")]
public class DbFunctionTypeMappingConvention : IModelFinalizingConvention
{
private readonly IRelationalTypeMappingSource _relationalTypeMappingSource;
@@ -44,6 +46,7 @@ public virtual void ProcessModelFinalizing(
foreach (var dbFunction in modelBuilder.Metadata.GetDbFunctions())
{
// TODO: This check needs to be updated to skip over enumerable parameter of aggregate.
+ // Also in DbFunctionParameter.TypeMapping
foreach (var parameter in dbFunction.Parameters)
{
parameter.Builder!.HasTypeMapping(
diff --git a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs
index 6a14683ff0b..233691a1242 100644
--- a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs
+++ b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs
@@ -107,7 +107,6 @@ public override ConventionSet CreateConventionSet()
conventionSet.ModelFinalizingConventions.Add(new EntityTypeHierarchyMappingConvention(Dependencies, RelationalDependencies));
conventionSet.ModelFinalizingConventions.Add(new SequenceUniquificationConvention(Dependencies, RelationalDependencies));
conventionSet.ModelFinalizingConventions.Add(new SharedTableConvention(Dependencies, RelationalDependencies));
- conventionSet.ModelFinalizingConventions.Add(new DbFunctionTypeMappingConvention(Dependencies, RelationalDependencies));
ReplaceConvention(
conventionSet.ModelFinalizingConventions,
(QueryFilterRewritingConvention)new RelationalQueryFilterRewritingConvention(
diff --git a/src/EFCore.Relational/Metadata/IConventionDbFunctionParameter.cs b/src/EFCore.Relational/Metadata/IConventionDbFunctionParameter.cs
index ec0d0eb4e0e..83b93617126 100644
--- a/src/EFCore.Relational/Metadata/IConventionDbFunctionParameter.cs
+++ b/src/EFCore.Relational/Metadata/IConventionDbFunctionParameter.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using System;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage;
@@ -22,7 +23,8 @@ public interface IConventionDbFunctionParameter : IConventionAnnotatable, IReadO
///
/// The for configuring this function parameter.
///
- new IConventionDbFunctionParameterBuilder? Builder { get; }
+ /// If the function has been removed from the model.
+ new IConventionDbFunctionParameterBuilder Builder { get; }
///
/// Returns the configuration source for the parameter.
diff --git a/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs b/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs
index 5a3edbc7a85..d9e284b9a9c 100644
--- a/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs
+++ b/src/EFCore.Relational/Metadata/IDbFunctionParameter.cs
@@ -18,16 +18,6 @@ public interface IDbFunctionParameter : IReadOnlyDbFunctionParameter, IAnnotatab
///
new IDbFunction Function { get; }
- ///
- /// Gets the store type of this parameter.
- ///
- new string StoreType { get; }
-
- ///
- /// Gets the for this parameter.
- ///
- new RelationalTypeMapping TypeMapping { get; }
-
///
/// Gets the associated .
///
diff --git a/src/EFCore.Relational/Metadata/Internal/Column.cs b/src/EFCore.Relational/Metadata/Internal/Column.cs
index e2742ec5cd3..e98e8d424d0 100644
--- a/src/EFCore.Relational/Metadata/Internal/Column.cs
+++ b/src/EFCore.Relational/Metadata/Internal/Column.cs
@@ -30,9 +30,14 @@ public Column([NotNull] string name, [NotNull] string type, [NotNull] Table tabl
{
}
- ///
- public new virtual ITable Table
- => (ITable)base.Table;
+ ///
+ /// 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 new virtual Table Table
+ => (Table)base.Table;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -43,6 +48,13 @@ public Column([NotNull] string name, [NotNull] string type, [NotNull] Table tabl
public override string ToString()
=> this.ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+ ///
+ ITable IColumn.Table
+ {
+ [DebuggerStepThrough]
+ get => Table;
+ }
+
///
IEnumerable IColumn.PropertyMappings
{
diff --git a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
index a6fb4e8c597..3131109a6d4 100644
--- a/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
+++ b/src/EFCore.Relational/Metadata/Internal/DbFunction.cs
@@ -542,7 +542,16 @@ public virtual string? StoreType
///
public virtual RelationalTypeMapping? TypeMapping
{
- get => _typeMapping;
+ get => IsReadOnly && IsScalar
+ ? NonCapturingLazyInitializer.EnsureInitialized(ref _typeMapping, this, static dbFunction =>
+ {
+ var relationalTypeMappingSource =
+ (IRelationalTypeMappingSource)((IModel)dbFunction.Model).GetModelDependencies().TypeMappingSource;
+ return !string.IsNullOrEmpty(dbFunction._storeType)
+ ? relationalTypeMappingSource.FindMapping(dbFunction._storeType)!
+ : relationalTypeMappingSource.FindMapping(dbFunction.ReturnType)!;
+ })
+ : _typeMapping;
set => SetTypeMapping(value, ConfigurationSource.Explicit);
}
diff --git a/src/EFCore.Relational/Metadata/Internal/DbFunctionParameter.cs b/src/EFCore.Relational/Metadata/Internal/DbFunctionParameter.cs
index 1494e9a44b2..929ead1d371 100644
--- a/src/EFCore.Relational/Metadata/Internal/DbFunctionParameter.cs
+++ b/src/EFCore.Relational/Metadata/Internal/DbFunctionParameter.cs
@@ -6,6 +6,7 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Builders.Internal;
using Microsoft.EntityFrameworkCore.Storage;
@@ -34,6 +35,7 @@ public class DbFunctionParameter :
private ConfigurationSource? _storeTypeConfigurationSource;
private ConfigurationSource? _typeMappingConfigurationSource;
private ConfigurationSource? _propagatesNullabilityConfigurationSource;
+ private InternalDbFunctionParameterBuilder? _builder;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -53,7 +55,7 @@ public DbFunctionParameter(
Name = name;
Function = function;
ClrType = clrType;
- Builder = new InternalDbFunctionParameterBuilder(this, function.Builder.ModelBuilder);
+ _builder = new InternalDbFunctionParameterBuilder(this, function.Builder.ModelBuilder);
}
///
@@ -62,11 +64,11 @@ public DbFunctionParameter(
/// 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 InternalDbFunctionParameterBuilder? Builder { get; }
-
- ///
- IConventionDbFunctionParameterBuilder? IConventionDbFunctionParameter.Builder
- => Builder;
+ public virtual InternalDbFunctionParameterBuilder Builder
+ {
+ [DebuggerStepThrough]
+ get => _builder ?? throw new InvalidOperationException(CoreStrings.ObjectRemovedFromModel);
+ }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -74,28 +76,30 @@ public DbFunctionParameter(
/// 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 DbFunction Function { get; }
+ public virtual bool IsInModel
+ => _builder is not null;
- ///
- IConventionDbFunction IConventionDbFunctionParameter.Function
- {
- [DebuggerStepThrough]
- get => Function;
- }
+ ///
+ /// 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 void SetRemovedFromModel()
+ => _builder = null;
- ///
- IReadOnlyDbFunction IReadOnlyDbFunctionParameter.Function
- {
- [DebuggerStepThrough]
- get => Function;
- }
+ ///
+ /// Indicates whether the function parameter is read-only.
+ ///
+ public override bool IsReadOnly => ((Annotatable)Function.Model).IsReadOnly;
- ///
- IMutableDbFunction IMutableDbFunctionParameter.Function
- {
- [DebuggerStepThrough]
- get => Function;
- }
+ ///
+ /// 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 DbFunction Function { get; }
///
public virtual string Name { get; }
@@ -108,6 +112,9 @@ IMutableDbFunction IMutableDbFunctionParameter.Function
public virtual ConfigurationSource GetConfigurationSource()
=> Function.GetConfigurationSource();
+ ///
+ public virtual IStoreFunctionParameter StoreFunctionParameter { get; [param: NotNull] set; } = default!;
+
///
/// 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
@@ -144,11 +151,6 @@ public virtual string? StoreType
public virtual ConfigurationSource? GetStoreTypeConfigurationSource()
=> _storeTypeConfigurationSource;
- ///
- [DebuggerStepThrough]
- string? IConventionDbFunctionParameter.SetStoreType(string? storeType, bool fromDataAnnotation)
- => SetStoreType(storeType, 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
@@ -157,14 +159,19 @@ public virtual string? StoreType
///
public virtual RelationalTypeMapping? TypeMapping
{
- get => _typeMapping;
+ get => IsReadOnly
+ ? NonCapturingLazyInitializer.EnsureInitialized(ref _typeMapping, this, static parameter =>
+ {
+ var relationalTypeMappingSource =
+ (IRelationalTypeMappingSource)((IModel)parameter.Function.Model).GetModelDependencies().TypeMappingSource;
+ return !string.IsNullOrEmpty(parameter._storeType)
+ ? relationalTypeMappingSource.FindMapping(parameter._storeType)!
+ : relationalTypeMappingSource.FindMapping(parameter.ClrType)!;
+ })
+ : _typeMapping;
set => SetTypeMapping(value, ConfigurationSource.Explicit);
}
- // Model validation ensures all parameters have a type mapping
- RelationalTypeMapping IDbFunctionParameter.TypeMapping
- => _typeMapping!;
-
///
/// 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
@@ -231,9 +238,6 @@ public virtual bool SetPropagatesNullability(bool propagatesNullability, Configu
public virtual ConfigurationSource? GetTypeMappingConfigurationSource()
=> _typeMappingConfigurationSource;
- ///
- public virtual IStoreFunctionParameter StoreFunctionParameter { get; [param: NotNull] set; } = default!;
-
///
/// 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
@@ -244,10 +248,31 @@ public override string ToString()
=> this.ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
///
- string IDbFunctionParameter.StoreType
+ IConventionDbFunctionParameterBuilder IConventionDbFunctionParameter.Builder
+ {
+ [DebuggerStepThrough]
+ get => Builder;
+ }
+
+ ///
+ IConventionDbFunction IConventionDbFunctionParameter.Function
+ {
+ [DebuggerStepThrough]
+ get => Function;
+ }
+
+ ///
+ IReadOnlyDbFunction IReadOnlyDbFunctionParameter.Function
{
[DebuggerStepThrough]
- get => StoreType!; // Model validation ensures all parameters have a type mapping
+ get => Function;
+ }
+
+ ///
+ IMutableDbFunction IMutableDbFunctionParameter.Function
+ {
+ [DebuggerStepThrough]
+ get => Function;
}
///
@@ -261,5 +286,10 @@ IDbFunction IDbFunctionParameter.Function
[DebuggerStepThrough]
RelationalTypeMapping? IConventionDbFunctionParameter.SetTypeMapping(RelationalTypeMapping? typeMapping, bool fromDataAnnotation)
=> SetTypeMapping(typeMapping, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
+
+ ///
+ [DebuggerStepThrough]
+ string? IConventionDbFunctionParameter.SetStoreType(string? storeType, bool fromDataAnnotation)
+ => SetStoreType(storeType, fromDataAnnotation ? ConfigurationSource.DataAnnotation : ConfigurationSource.Convention);
}
}
diff --git a/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraint.cs b/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraint.cs
index c8a2979833d..0d2259ad6a9 100644
--- a/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraint.cs
+++ b/src/EFCore.Relational/Metadata/Internal/ForeignKeyConstraint.cs
@@ -83,6 +83,14 @@ public ForeignKeyConstraint(
///
public virtual IReadOnlyList PrincipalColumns { get; }
+ ///
+ /// 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 bool IsReadOnly => Table.Model.IsReadOnly;
+
///
public virtual ReferentialAction OnDeleteAction { get; set; }
diff --git a/src/EFCore.Relational/Metadata/Internal/FunctionColumn.cs b/src/EFCore.Relational/Metadata/Internal/FunctionColumn.cs
index fcbbe265bad..7ca253ce836 100644
--- a/src/EFCore.Relational/Metadata/Internal/FunctionColumn.cs
+++ b/src/EFCore.Relational/Metadata/Internal/FunctionColumn.cs
@@ -30,9 +30,14 @@ public FunctionColumn([NotNull] string name, [NotNull] string type, [NotNull] St
{
}
- ///
- public virtual IStoreFunction Function
- => (IStoreFunction)Table;
+ ///
+ /// 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 StoreFunction Function
+ => (StoreFunction)Table;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -43,6 +48,13 @@ public virtual IStoreFunction Function
public override string ToString()
=> this.ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+ ///
+ IStoreFunction IFunctionColumn.Function
+ {
+ [DebuggerStepThrough]
+ get => Function;
+ }
+
///
IEnumerable IFunctionColumn.PropertyMappings
{
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
index 968962105ee..0377d221d17 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalModel.cs
@@ -22,6 +22,8 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
///
public class RelationalModel : Annotatable, IRelationalModel
{
+ private bool _isReadOnly;
+
///
/// 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
@@ -36,6 +38,14 @@ public RelationalModel([NotNull] IModel model)
///
public virtual IModel Model { get; }
+ ///
+ /// 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 bool IsReadOnly => _isReadOnly;
+
///
/// 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
@@ -219,6 +229,7 @@ public static IModel Add(
databaseModel.AddAnnotations(relationalAnnotationProvider.For(databaseModel));
}
+ databaseModel._isReadOnly = true;
model.AddRuntimeAnnotation(RelationalAnnotationNames.RelationalModel, databaseModel);
return model;
}
@@ -235,7 +246,8 @@ private static void AddDefaultMappings(RelationalModel databaseModel, IEntityTyp
var tableMapping = new TableMappingBase(entityType, defaultTable, includesDerivedTypes: true)
{
- IsSharedTablePrincipal = true, IsSplitEntityTypePrincipal = true
+ IsSharedTablePrincipal = true,
+ IsSplitEntityTypePrincipal = true
};
foreach (var property in entityType.GetProperties())
@@ -249,7 +261,7 @@ private static void AddDefaultMappings(RelationalModel databaseModel, IEntityTyp
var column = (ColumnBase?)defaultTable.FindColumn(columnName);
if (column == null)
{
- column = new (columnName, property.GetColumnType()!, defaultTable);
+ column = new(columnName, property.GetColumnType()!, defaultTable);
column.IsNullable = property.IsColumnNullable();
defaultTable.Columns.Add(columnName, column);
}
@@ -342,7 +354,7 @@ private static void AddTables(RelationalModel databaseModel, IEntityType entityT
var column = (Column?)table.FindColumn(columnName);
if (column == null)
{
- column = new (columnName, property.GetColumnType(mappedTable)!, table);
+ column = new(columnName, property.GetColumnType(mappedTable)!, table);
column.IsNullable = property.IsColumnNullable(mappedTable);
table.Columns.Add(columnName, column);
}
@@ -433,7 +445,7 @@ private static void AddViews(RelationalModel databaseModel, IEntityType entityTy
var column = (ViewColumn?)view.FindColumn(columnName);
if (column == null)
{
- column = new (columnName, property.GetColumnType(mappedView)!, view);
+ column = new(columnName, property.GetColumnType(mappedView)!, view);
column.IsNullable = property.IsColumnNullable(mappedView);
view.Columns.Add(columnName, column);
}
@@ -538,7 +550,7 @@ private static void AddSqlQueries(RelationalModel databaseModel, IEntityType ent
var column = (SqlQueryColumn?)sqlQuery.FindColumn(columnName);
if (column == null)
{
- column = new (columnName, property.GetColumnType(mappedQuery)!, sqlQuery);
+ column = new(columnName, property.GetColumnType(mappedQuery)!, sqlQuery);
column.IsNullable = property.IsColumnNullable(mappedQuery);
sqlQuery.Columns.Add(columnName, column);
}
@@ -687,7 +699,7 @@ private static FunctionMapping CreateFunctionMapping(
var column = (FunctionColumn?)storeFunction.FindColumn(columnName);
if (column == null)
{
- column = new (columnName, property.GetColumnType(mappedFunction)!, storeFunction);
+ column = new(columnName, property.GetColumnType(mappedFunction)!, storeFunction);
column.IsNullable = property.IsColumnNullable(mappedFunction);
storeFunction.Columns.Add(columnName, column);
}
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
index 547a7ec5f58..9555b9470ab 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
@@ -21,6 +21,33 @@ public class RelationalPropertyOverrides : ConventionAnnotatable
private ConfigurationSource? _columnNameConfigurationSource;
+ ///
+ /// 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 RelationalPropertyOverrides([NotNull] IReadOnlyProperty property)
+ {
+ Property = property;
+ }
+
+ ///
+ /// 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 IReadOnlyProperty Property { get; }
+
+ ///
+ /// 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 bool IsReadOnly => ((Annotatable)Property).IsReadOnly;
+
///
/// 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
@@ -42,6 +69,8 @@ public virtual string? ColumnName
///
public virtual string? SetColumnName([CanBeNull] string? columnName, ConfigurationSource configurationSource)
{
+ EnsureMutable();
+
_columnName = columnName;
_columnNameConfigurationSource = configurationSource.Max(_columnNameConfigurationSource);
@@ -93,7 +122,7 @@ public static RelationalPropertyOverrides GetOrCreate(
if (!tableOverrides.TryGetValue(storeObject, out var overrides))
{
- overrides = new RelationalPropertyOverrides();
+ overrides = new RelationalPropertyOverrides(property);
tableOverrides.Add(storeObject, overrides);
}
diff --git a/src/EFCore.Relational/Metadata/Internal/SqlQueryColumn.cs b/src/EFCore.Relational/Metadata/Internal/SqlQueryColumn.cs
index bcedbd595c1..37a4838a916 100644
--- a/src/EFCore.Relational/Metadata/Internal/SqlQueryColumn.cs
+++ b/src/EFCore.Relational/Metadata/Internal/SqlQueryColumn.cs
@@ -30,9 +30,14 @@ public SqlQueryColumn([NotNull] string name, [NotNull] string type, [NotNull] Sq
{
}
- ///
- public virtual ISqlQuery SqlQuery
- => (ISqlQuery)Table;
+ ///
+ /// 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 SqlQuery SqlQuery
+ => (SqlQuery)Table;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -43,6 +48,13 @@ public virtual ISqlQuery SqlQuery
public override string ToString()
=> this.ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+ ///
+ ISqlQuery ISqlQueryColumn.SqlQuery
+ {
+ [DebuggerStepThrough]
+ get => SqlQuery;
+ }
+
///
IEnumerable ISqlQueryColumn.PropertyMappings
{
diff --git a/src/EFCore.Relational/Metadata/Internal/StoreFunctionParameter.cs b/src/EFCore.Relational/Metadata/Internal/StoreFunctionParameter.cs
index 34761edbd30..65614b44495 100644
--- a/src/EFCore.Relational/Metadata/Internal/StoreFunctionParameter.cs
+++ b/src/EFCore.Relational/Metadata/Internal/StoreFunctionParameter.cs
@@ -35,8 +35,21 @@ public StoreFunctionParameter(
parameter.StoreFunctionParameter = this;
}
- ///
- public virtual IStoreFunction Function { get; }
+ ///
+ /// 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 StoreFunction Function { get; }
+
+ ///
+ /// 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 bool IsReadOnly => Function.IsReadOnly;
///
public virtual string Name { get; }
@@ -61,10 +74,19 @@ public StoreFunctionParameter(
public override string ToString()
=> this.ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+ ///
+ IStoreFunction IStoreFunctionParameter.Function
+ {
+ [DebuggerStepThrough]
+ get => Function;
+ }
+
+ ///
IEnumerable IStoreFunctionParameter.DbFunctionParameters
{
[DebuggerStepThrough]
get => DbFunctionParameters;
}
+
}
}
diff --git a/src/EFCore.Relational/Metadata/Internal/TableIndex.cs b/src/EFCore.Relational/Metadata/Internal/TableIndex.cs
index e53fe194560..3162715bc4b 100644
--- a/src/EFCore.Relational/Metadata/Internal/TableIndex.cs
+++ b/src/EFCore.Relational/Metadata/Internal/TableIndex.cs
@@ -64,6 +64,14 @@ public TableIndex(
///
public virtual IReadOnlyList Columns { get; }
+ ///
+ /// 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 bool IsReadOnly => Table.Model.IsReadOnly;
+
///
public virtual bool IsUnique { get; }
diff --git a/src/EFCore.Relational/Metadata/Internal/UniqueConstraint.cs b/src/EFCore.Relational/Metadata/Internal/UniqueConstraint.cs
index 71fd4112139..b412b661a41 100644
--- a/src/EFCore.Relational/Metadata/Internal/UniqueConstraint.cs
+++ b/src/EFCore.Relational/Metadata/Internal/UniqueConstraint.cs
@@ -60,6 +60,14 @@ public UniqueConstraint(
///
public virtual IReadOnlyList Columns { get; }
+ ///
+ /// 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 bool IsReadOnly => Table.Model.IsReadOnly;
+
///
ITable IUniqueConstraint.Table
=> Table;
diff --git a/src/EFCore.Relational/Metadata/Internal/ViewColumn.cs b/src/EFCore.Relational/Metadata/Internal/ViewColumn.cs
index 8e9a003b6ea..16f09409ac4 100644
--- a/src/EFCore.Relational/Metadata/Internal/ViewColumn.cs
+++ b/src/EFCore.Relational/Metadata/Internal/ViewColumn.cs
@@ -30,9 +30,14 @@ public ViewColumn([NotNull] string name, [NotNull] string type, [NotNull] View v
{
}
- ///
- public virtual IView View
- => (IView)Table;
+ ///
+ /// 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 View View
+ => (View)Table;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -43,6 +48,13 @@ public virtual IView View
public override string ToString()
=> this.ToDebugString(MetadataDebugStringOptions.SingleLineDefault);
+ ///
+ IView IViewColumn.View
+ {
+ [DebuggerStepThrough]
+ get => View;
+ }
+
///
IEnumerable IViewColumn.PropertyMappings
{
diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs
index 73f5ddcc722..433a93f39cb 100644
--- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs
+++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs
@@ -361,7 +361,7 @@ private void SetServiceProperties(EntityState oldState, EntityState newState)
{
this[serviceProperty]
= serviceProperty
- .GetParameterBinding()
+ .ParameterBinding
.ServiceDelegate(
new MaterializationContext(
ValueBuffer.Empty,
diff --git a/src/EFCore/Infrastructure/ModelRuntimeInitializer.cs b/src/EFCore/Infrastructure/ModelRuntimeInitializer.cs
index 1b3db088bfc..1873857b413 100644
--- a/src/EFCore/Infrastructure/ModelRuntimeInitializer.cs
+++ b/src/EFCore/Infrastructure/ModelRuntimeInitializer.cs
@@ -4,6 +4,7 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.Extensions.DependencyInjection;
@@ -56,12 +57,19 @@ public virtual IModel Initialize(
if (model.SetModelDependencies(Dependencies.ModelDependencies))
{
InitializeModel(model, preValidation: true);
+
if (validationLogger != null
- && model is IMutableModel)
+ && model is IConventionModel)
{
Dependencies.ModelValidator.Validate(model, validationLogger);
}
+
InitializeModel(model, preValidation: false);
+
+ if (model is Model mutableModel)
+ {
+ model = mutableModel.OnModelFinalized();
+ }
}
return model;
diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs
index 3bc46350477..e846db25cdc 100644
--- a/src/EFCore/Infrastructure/ModelValidator.cs
+++ b/src/EFCore/Infrastructure/ModelValidator.cs
@@ -843,8 +843,7 @@ protected virtual void ValidateFieldMapping(
.Concat(entityType.GetDeclaredNavigations())
.Where(p => !p.IsShadowProperty() && !p.IsIndexerProperty()));
- var constructorBinding = (InstantiationBinding?)entityType[CoreAnnotationNames.ConstructorBinding];
-
+ var constructorBinding = entityType.ConstructorBinding;
if (constructorBinding != null)
{
foreach (var consumedProperty in constructorBinding.ParameterBindings.SelectMany(p => p.ConsumedProperties))
diff --git a/src/EFCore/Infrastructure/SingletonModelDependencies.cs b/src/EFCore/Infrastructure/SingletonModelDependencies.cs
index 8f8d73286bd..374ab522e50 100644
--- a/src/EFCore/Infrastructure/SingletonModelDependencies.cs
+++ b/src/EFCore/Infrastructure/SingletonModelDependencies.cs
@@ -55,16 +55,32 @@ public sealed record SingletonModelDependencies
///
[EntityFrameworkInternal]
public SingletonModelDependencies(
- [NotNull] ITypeMappingSource typeMappingSource)
+ [NotNull] ITypeMappingSource typeMappingSource,
+ [NotNull] IConstructorBindingFactory constructorBindingFactory,
+ [NotNull] IParameterBindingFactories parameterBindingFactories)
{
Check.NotNull(typeMappingSource, nameof(typeMappingSource));
+ Check.NotNull(constructorBindingFactory, nameof(constructorBindingFactory));
+ Check.NotNull(parameterBindingFactories, nameof(parameterBindingFactories));
TypeMappingSource = typeMappingSource;
+ ConstructorBindingFactory = constructorBindingFactory;
+ ParameterBindingFactories = parameterBindingFactories;
}
///
/// The type mapper.
///
public ITypeMappingSource TypeMappingSource { get; [param: NotNull] init; }
+
+ ///
+ /// The constructor binding factory.
+ ///
+ public IConstructorBindingFactory ConstructorBindingFactory { get; [param: NotNull] init; }
+
+ ///
+ /// The parameter binding factories.
+ ///
+ public IParameterBindingFactories ParameterBindingFactories { get; [param: NotNull] init; }
}
}
diff --git a/src/EFCore/Metadata/Conventions/ConstructorBindingConvention.cs b/src/EFCore/Metadata/Conventions/ConstructorBindingConvention.cs
index 12183b86f4b..29893a60c57 100644
--- a/src/EFCore/Metadata/Conventions/ConstructorBindingConvention.cs
+++ b/src/EFCore/Metadata/Conventions/ConstructorBindingConvention.cs
@@ -1,13 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.Diagnostics;
-using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
@@ -46,122 +40,18 @@ public virtual void ProcessModelFinalizing(
IConventionModelBuilder modelBuilder,
IConventionContext context)
{
- foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
+ foreach (EntityType entityType in modelBuilder.Metadata.GetEntityTypes())
{
- if (entityType.ClrType?.IsAbstract == false
- && entityType.Builder.CanSetAnnotation(CoreAnnotationNames.ConstructorBinding, null))
+ if (!entityType.ClrType.IsAbstract
+ && ConfigurationSource.Convention.Overrides(entityType.GetConstructorBindingConfigurationSource()))
{
- var maxServiceParams = 0;
- var maxServiceOnlyParams = 0;
- var minPropertyParams = int.MaxValue;
- var foundBindings = new List();
- var foundServiceOnlyBindings = new List();
- var bindingFailures = new List>();
+ Dependencies.ConstructorBindingFactory.GetBindings(
+ (IMutableEntityType)entityType, out var constructorBinding, out var serviceOnlyBinding);
- foreach (var constructor in entityType.ClrType.GetTypeInfo()
- .DeclaredConstructors
- .Where(c => !c.IsStatic))
- {
- // Trying to find the constructor with the most service properties
- // followed by the least scalar property parameters
- if (Dependencies.ConstructorBindingFactory.TryBindConstructor(
- entityType, constructor, out var binding, out var failures))
- {
- var serviceParamCount = binding.ParameterBindings.OfType().Count();
- var propertyParamCount = binding.ParameterBindings.Count - serviceParamCount;
-
- if (propertyParamCount == 0)
- {
- if (serviceParamCount == maxServiceOnlyParams)
- {
- foundServiceOnlyBindings.Add(binding);
- }
- else if (serviceParamCount > maxServiceOnlyParams)
- {
- foundServiceOnlyBindings.Clear();
- foundServiceOnlyBindings.Add(binding);
-
- maxServiceOnlyParams = serviceParamCount;
- }
- }
-
- if (serviceParamCount == maxServiceParams
- && propertyParamCount == minPropertyParams)
- {
- foundBindings.Add(binding);
- }
- else if (serviceParamCount > maxServiceParams)
- {
- foundBindings.Clear();
- foundBindings.Add(binding);
-
- maxServiceParams = serviceParamCount;
- minPropertyParams = propertyParamCount;
- }
- else if (propertyParamCount < minPropertyParams)
- {
- foundBindings.Clear();
- foundBindings.Add(binding);
-
- maxServiceParams = serviceParamCount;
- minPropertyParams = propertyParamCount;
- }
- }
- else
- {
- bindingFailures.Add(failures);
- }
- }
-
- if (foundBindings.Count == 0)
- {
- var constructorErrors = bindingFailures.SelectMany(f => f)
- .GroupBy(f => (ConstructorInfo)f.Member)
- .Select(
- x => CoreStrings.ConstructorBindingFailed(
- string.Join("', '", x.Select(f => f.Name)),
- entityType.DisplayName()
- + "("
- + string.Join(
- ", ", x.Key.GetParameters().Select(
- y => y.ParameterType.ShortDisplayName() + " " + y.Name)
- )
- + ")"
- )
- );
-
- throw new InvalidOperationException(
- CoreStrings.ConstructorNotFound(
- entityType.DisplayName(),
- string.Join("; ", constructorErrors)));
- }
-
- if (foundBindings.Count > 1)
- {
- throw new InvalidOperationException(
- CoreStrings.ConstructorConflict(
- FormatConstructorString(entityType, foundBindings[0]),
- FormatConstructorString(entityType, foundBindings[1])));
- }
-
- entityType.Builder.HasAnnotation(
- CoreAnnotationNames.ConstructorBinding,
- foundBindings[0]);
-
- if (foundServiceOnlyBindings.Count == 1)
- {
- entityType.Builder.HasAnnotation(
- CoreAnnotationNames.ServiceOnlyConstructorBinding,
- foundServiceOnlyBindings[0]);
- }
+ entityType.Builder.HasConstructorBinding(constructorBinding, ConfigurationSource.Convention);
+ entityType.Builder.HasServiceOnlyConstructorBinding(serviceOnlyBinding, ConfigurationSource.Convention);
}
}
}
-
- private static string FormatConstructorString(IReadOnlyEntityType entityType, InstantiationBinding binding)
- => entityType.ClrType.ShortDisplayName()
- + "("
- + string.Join(", ", binding.ParameterBindings.Select(b => b.ParameterType.ShortDisplayName()))
- + ")";
}
}
diff --git a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilderDependencies.cs b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilderDependencies.cs
index 8cd0fe3ebb1..0203df06a35 100644
--- a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilderDependencies.cs
+++ b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilderDependencies.cs
@@ -94,13 +94,13 @@ public ProviderConventionSetBuilderDependencies(
Check.NotNull(validator, nameof(validator));
TypeMappingSource = typeMappingSource;
+ ConstructorBindingFactory = constructorBindingFactory;
ParameterBindingFactories = parameterBindingFactories;
MemberClassifier = memberClassifier;
- ConstructorBindingFactory = constructorBindingFactory;
Logger = logger;
+ ValidationLogger = validationLogger;
SetFinder = setFinder;
_currentContext = currentContext;
- ValidationLogger = validationLogger;
#pragma warning disable CS0618 // Type or member is obsolete
ModelValidator = validator;
#pragma warning restore CS0618 // Type or member is obsolete
diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs
index 0003db732a6..dcdc53c4bb2 100644
--- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs
+++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.ImmediateConventionScope.cs
@@ -85,16 +85,6 @@ public IConventionModelBuilder OnModelFinalizing([NotNull] IConventionModelBuild
return modelBuilder;
}
- public IModel OnModelFinalized([NotNull] IModel model)
- {
- foreach (var modelConvention in _conventionSet.ModelFinalizedConventions)
- {
- model = modelConvention.ProcessModelFinalized(model);
- }
-
- return model;
- }
-
public IConventionModelBuilder OnModelInitialized([NotNull] IConventionModelBuilder modelBuilder)
{
using (_dispatcher.DelayConventions())
diff --git a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs
index 1871f56708f..ab8a63a00cf 100644
--- a/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs
+++ b/src/EFCore/Metadata/Conventions/Internal/ConventionDispatcher.cs
@@ -66,15 +66,6 @@ public virtual IConventionModelBuilder OnModelInitialized([NotNull] IConventionM
public virtual IConventionModelBuilder OnModelFinalizing([NotNull] IConventionModelBuilder modelBuilder)
=> _immediateConventionScope.OnModelFinalizing(modelBuilder);
- ///
- /// 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 IModel OnModelFinalized([NotNull] IModel model)
- => _immediateConventionScope.OnModelFinalized(model);
-
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore/Metadata/Conventions/ServicePropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/ServicePropertyDiscoveryConvention.cs
index 9a5ce788620..b8a9ea984d2 100644
--- a/src/EFCore/Metadata/Conventions/ServicePropertyDiscoveryConvention.cs
+++ b/src/EFCore/Metadata/Conventions/ServicePropertyDiscoveryConvention.cs
@@ -118,7 +118,7 @@ private void Process(IConventionEntityTypeBuilder entityTypeBuilder)
}
entityTypeBuilder.ServiceProperty(propertyInfo)?.HasParameterBinding(
- (ServiceParameterBinding)factory.Bind(entityType, propertyInfo.PropertyType, propertyInfo.GetSimpleMemberName()));
+ (ServiceParameterBinding)factory.Bind(entityType, propertyInfo.PropertyType, name));
}
}
diff --git a/src/EFCore/Metadata/IConstructorBindingFactory.cs b/src/EFCore/Metadata/IConstructorBindingFactory.cs
index 9e3fbfde1ca..5465413f060 100644
--- a/src/EFCore/Metadata/IConstructorBindingFactory.cs
+++ b/src/EFCore/Metadata/IConstructorBindingFactory.cs
@@ -24,6 +24,42 @@ namespace Microsoft.EntityFrameworkCore.Metadata
///
public interface IConstructorBindingFactory
{
+ ///
+ /// Create a for the constructor with most parameters and
+ /// the constructor with only service property parameters.
+ ///
+ /// The entity type.
+ /// The binding for the constructor with most parameters.
+ /// The binding for the constructor with only service property parameters.
+ void GetBindings(
+ [NotNull] IConventionEntityType entityType,
+ [NotNull] out InstantiationBinding constructorBinding,
+ [NotNull] out InstantiationBinding? serviceOnlyBinding);
+
+ ///
+ /// Create a for the constructor with most parameters and
+ /// the constructor with only service property parameters.
+ ///
+ /// The entity type.
+ /// The binding for the constructor with most parameters.
+ /// The binding for the constructor with only service property parameters.
+ void GetBindings(
+ [NotNull] IMutableEntityType entityType,
+ [NotNull] out InstantiationBinding constructorBinding,
+ [NotNull] out InstantiationBinding? serviceOnlyBinding);
+
+ ///
+ /// Create a for the constructor with most parameters and
+ /// the constructor with only service property parameters.
+ ///
+ /// The entity type.
+ /// The binding for the constructor with most parameters.
+ /// The binding for the constructor with only service property parameters.
+ void GetBindings(
+ [NotNull] IReadOnlyEntityType entityType,
+ [NotNull] out InstantiationBinding constructorBinding,
+ [NotNull] out InstantiationBinding? serviceOnlyBinding);
+
///
/// Attempts to create a for the given entity type and
///
diff --git a/src/EFCore/Metadata/IConventionEntityType.cs b/src/EFCore/Metadata/IConventionEntityType.cs
index e1f9f07bf48..85c4cb2b7a6 100644
--- a/src/EFCore/Metadata/IConventionEntityType.cs
+++ b/src/EFCore/Metadata/IConventionEntityType.cs
@@ -102,9 +102,9 @@ void HasNoKey(bool? keyless, bool fromDataAnnotation = false)
=> SetIsKeyless(keyless, fromDataAnnotation);
///
- /// Returns the configuration source for the IsKeyless property.
+ /// Returns the configuration source for .
///
- /// The configuration source for the IsKeyless property.
+ /// The configuration source for .
ConfigurationSource? GetIsKeylessConfigurationSource();
///
diff --git a/src/EFCore/Metadata/IEntityType.cs b/src/EFCore/Metadata/IEntityType.cs
index ea11a369fde..2492869b7fa 100644
--- a/src/EFCore/Metadata/IEntityType.cs
+++ b/src/EFCore/Metadata/IEntityType.cs
@@ -22,6 +22,11 @@ public interface IEntityType : IReadOnlyEntityType, ITypeBase
///
new IEntityType? BaseType { get; }
+ ///
+ /// Gets the for the preferred constructor.
+ ///
+ InstantiationBinding? ConstructorBinding { get; }
+
///
/// Gets primary key for this entity type. Returns if no primary key is defined.
///
diff --git a/src/EFCore/Metadata/IModel.cs b/src/EFCore/Metadata/IModel.cs
index 0e7190a9dcd..5a463aaec4c 100644
--- a/src/EFCore/Metadata/IModel.cs
+++ b/src/EFCore/Metadata/IModel.cs
@@ -6,6 +6,7 @@
using System.Diagnostics;
using System.Linq;
using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -83,6 +84,20 @@ public interface IModel : IReadOnlyModel, IAnnotatable
SingletonModelDependencies? ModelDependencies
=> (SingletonModelDependencies?)FindRuntimeAnnotationValue(CoreAnnotationNames.ModelDependencies);
+ ///
+ /// Gets the runtime service dependencies.
+ ///
+ SingletonModelDependencies GetModelDependencies()
+ {
+ var dependencies = ModelDependencies;
+ if (dependencies == null)
+ {
+ throw new InvalidOperationException(CoreStrings.ModelNotFinalized(nameof(GetModelDependencies)));
+ }
+
+ return dependencies;
+ }
+
///
/// Set the runtime service dependencies.
///
diff --git a/src/EFCore/Metadata/IParameterBindingFactory.cs b/src/EFCore/Metadata/IParameterBindingFactory.cs
index 94bd65ae590..fd64a251274 100644
--- a/src/EFCore/Metadata/IParameterBindingFactory.cs
+++ b/src/EFCore/Metadata/IParameterBindingFactory.cs
@@ -32,6 +32,18 @@ bool CanBind(
[NotNull] Type parameterType,
[NotNull] string parameterName);
+ ///
+ /// Creates a for the given type and name on the given entity type.
+ ///
+ /// The entity type.
+ /// The parameter type.
+ /// The parameter name.
+ /// The binding.
+ ParameterBinding Bind(
+ [NotNull] IReadOnlyEntityType entityType,
+ [NotNull] Type parameterType,
+ [NotNull] string parameterName);
+
///
/// Creates a for the given type and name on the given entity type.
///
diff --git a/src/EFCore/Metadata/IServiceProperty.cs b/src/EFCore/Metadata/IServiceProperty.cs
index 505b57abcd2..e6582e50657 100644
--- a/src/EFCore/Metadata/IServiceProperty.cs
+++ b/src/EFCore/Metadata/IServiceProperty.cs
@@ -15,5 +15,10 @@ public interface IServiceProperty : IReadOnlyServiceProperty, IPropertyBase
/// Gets the entity type that this property belongs to.
///
new IEntityType DeclaringEntityType { get; }
+
+ ///
+ /// The for this property.
+ ///
+ new ServiceParameterBinding ParameterBinding { get; }
}
}
diff --git a/src/EFCore/Metadata/Internal/ConstructorBindingFactory.cs b/src/EFCore/Metadata/Internal/ConstructorBindingFactory.cs
index 427a225d98c..02aa8ebd147 100644
--- a/src/EFCore/Metadata/Internal/ConstructorBindingFactory.cs
+++ b/src/EFCore/Metadata/Internal/ConstructorBindingFactory.cs
@@ -6,6 +6,8 @@
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using CA = System.Diagnostics.CodeAnalysis;
@@ -45,6 +47,164 @@ public ConstructorBindingFactory(
_factories = factories;
}
+ ///
+ /// 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 void GetBindings(
+ IConventionEntityType entityType,
+ out InstantiationBinding constructorBinding,
+ out InstantiationBinding? serviceOnlyBinding)
+ => GetBindings(
+ entityType,
+ static (f, e, p, n) => f?.Bind((IConventionEntityType)e, p, n),
+ out constructorBinding,
+ out serviceOnlyBinding);
+
+ ///
+ /// 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 void GetBindings(
+ IMutableEntityType entityType,
+ out InstantiationBinding constructorBinding,
+ out InstantiationBinding? serviceOnlyBinding)
+ => GetBindings(
+ entityType,
+ static (f, e, p, n) => f?.Bind((IMutableEntityType)e, p, n),
+ out constructorBinding,
+ out serviceOnlyBinding);
+
+ ///
+ /// 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 void GetBindings(
+ IReadOnlyEntityType entityType,
+ out InstantiationBinding constructorBinding,
+ out InstantiationBinding? serviceOnlyBinding)
+ => GetBindings(
+ entityType,
+ static (f, e, p, n) => f?.Bind(e, p, n),
+ out constructorBinding,
+ out serviceOnlyBinding);
+
+ private void GetBindings(
+ IReadOnlyEntityType entityType,
+ Func bind,
+ out InstantiationBinding constructorBinding,
+ out InstantiationBinding? serviceOnlyBinding)
+ {
+ var maxServiceParams = 0;
+ var maxServiceOnlyParams = 0;
+ var minPropertyParams = int.MaxValue;
+ var foundBindings = new List();
+ var foundServiceOnlyBindings = new List();
+ var bindingFailures = new List>();
+
+ foreach (var constructor in entityType.ClrType.GetTypeInfo()
+ .DeclaredConstructors
+ .Where(c => !c.IsStatic))
+ {
+ // Trying to find the constructor with the most service properties
+ // followed by the least scalar property parameters
+ if (TryBindConstructor(
+ entityType, constructor, bind, out var binding, out var failures))
+ {
+ var serviceParamCount = binding.ParameterBindings.OfType().Count();
+ var propertyParamCount = binding.ParameterBindings.Count - serviceParamCount;
+
+ if (propertyParamCount == 0)
+ {
+ if (serviceParamCount == maxServiceOnlyParams)
+ {
+ foundServiceOnlyBindings.Add(binding);
+ }
+ else if (serviceParamCount > maxServiceOnlyParams)
+ {
+ foundServiceOnlyBindings.Clear();
+ foundServiceOnlyBindings.Add(binding);
+
+ maxServiceOnlyParams = serviceParamCount;
+ }
+ }
+
+ if (serviceParamCount == maxServiceParams
+ && propertyParamCount == minPropertyParams)
+ {
+ foundBindings.Add(binding);
+ }
+ else if (serviceParamCount > maxServiceParams)
+ {
+ foundBindings.Clear();
+ foundBindings.Add(binding);
+
+ maxServiceParams = serviceParamCount;
+ minPropertyParams = propertyParamCount;
+ }
+ else if (propertyParamCount < minPropertyParams)
+ {
+ foundBindings.Clear();
+ foundBindings.Add(binding);
+
+ maxServiceParams = serviceParamCount;
+ minPropertyParams = propertyParamCount;
+ }
+ }
+ else
+ {
+ bindingFailures.Add(failures);
+ }
+ }
+
+ if (foundBindings.Count == 0)
+ {
+ var constructorErrors = bindingFailures.SelectMany(f => f)
+ .GroupBy(f => (ConstructorInfo)f.Member)
+ .Select(
+ x => CoreStrings.ConstructorBindingFailed(
+ string.Join("', '", x.Select(f => f.Name)),
+ entityType.DisplayName()
+ + "("
+ + string.Join(
+ ", ", x.Key.GetParameters().Select(
+ y => y.ParameterType.ShortDisplayName() + " " + y.Name)
+ )
+ + ")"
+ )
+ );
+
+ throw new InvalidOperationException(
+ CoreStrings.ConstructorNotFound(
+ entityType.DisplayName(),
+ string.Join("; ", constructorErrors)));
+ }
+
+ if (foundBindings.Count > 1)
+ {
+ throw new InvalidOperationException(
+ CoreStrings.ConstructorConflict(
+ FormatConstructorString(entityType, foundBindings[0]),
+ FormatConstructorString(entityType, foundBindings[1])));
+ }
+
+ constructorBinding = foundBindings[0];
+ if (foundServiceOnlyBindings.Count == 1)
+ {
+ serviceOnlyBinding = foundServiceOnlyBindings[0];
+ }
+ else
+ {
+ serviceOnlyBinding = 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
@@ -59,7 +219,7 @@ public virtual bool TryBindConstructor(
=> TryBindConstructor(
entityType,
constructor,
- (f, e, p, n) => f?.Bind((IMutableEntityType)e, p, n),
+ static (f, e, p, n) => f?.Bind((IMutableEntityType)e, p, n),
out binding,
out unboundParameters);
@@ -77,7 +237,7 @@ public virtual bool TryBindConstructor(
=> TryBindConstructor(
entityType,
constructor,
- (f, e, p, n) => f?.Bind((IConventionEntityType)e, p, n),
+ static (f, e, p, n) => f?.Bind((IConventionEntityType)e, p, n),
out binding,
out unboundParameters);
@@ -109,5 +269,11 @@ private bool TryBindConstructor(
return true;
}
+
+ private static string FormatConstructorString(IReadOnlyEntityType entityType, InstantiationBinding binding)
+ => entityType.ClrType.ShortDisplayName()
+ + "("
+ + string.Join(", ", binding.ParameterBindings.Select(b => b.ParameterType.ShortDisplayName()))
+ + ")";
}
}
diff --git a/src/EFCore/Metadata/Internal/ContextParameterBindingFactory.cs b/src/EFCore/Metadata/Internal/ContextParameterBindingFactory.cs
index 9c87cbb2509..38686ceccd4 100644
--- a/src/EFCore/Metadata/Internal/ContextParameterBindingFactory.cs
+++ b/src/EFCore/Metadata/Internal/ContextParameterBindingFactory.cs
@@ -33,10 +33,11 @@ public virtual bool CanBind(
/// 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 ParameterBinding Bind(IMutableEntityType entityType, Type parameterType, string parameterName)
- => new ContextParameterBinding(
- parameterType,
- (IPropertyBase?)entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType));
+ public virtual ParameterBinding Bind(
+ IMutableEntityType entityType,
+ Type parameterType,
+ string parameterName)
+ => Bind((IReadOnlyEntityType)entityType, parameterType, parameterName);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -48,6 +49,18 @@ public virtual ParameterBinding Bind(
IConventionEntityType entityType,
Type parameterType,
string parameterName)
+ => Bind((IReadOnlyEntityType)entityType, parameterType, parameterName);
+
+ ///
+ /// 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 ParameterBinding Bind(
+ IReadOnlyEntityType entityType,
+ Type parameterType,
+ string parameterName)
=> new ContextParameterBinding(
parameterType,
(IPropertyBase?)entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType));
diff --git a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
index df492680914..3423dba4adb 100644
--- a/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
+++ b/src/EFCore/Metadata/Internal/CoreAnnotationNames.cs
@@ -112,22 +112,6 @@ public static class CoreAnnotationNames
///
public const string DiscriminatorValue = "DiscriminatorValue";
- ///
- /// 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 ConstructorBinding = "ConstructorBinding";
-
- ///
- /// 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 ServiceOnlyConstructorBinding = "ServiceOnlyConstructorBinding";
-
///
/// 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
@@ -295,8 +279,6 @@ public static class CoreAnnotationNames
DiscriminatorProperty,
DiscriminatorMappingComplete,
DiscriminatorValue,
- ConstructorBinding,
- ServiceOnlyConstructorBinding,
ValueConverter,
ValueComparer,
#pragma warning disable 618
diff --git a/src/EFCore/Metadata/Internal/EntityType.cs b/src/EFCore/Metadata/Internal/EntityType.cs
index 474d697f744..0055ca36b8d 100644
--- a/src/EFCore/Metadata/Internal/EntityType.cs
+++ b/src/EFCore/Metadata/Internal/EntityType.cs
@@ -61,11 +61,15 @@ private readonly SortedDictionary _serviceProperties
private EntityType? _baseType;
private ChangeTrackingStrategy? _changeTrackingStrategy;
private InternalEntityTypeBuilder? _builder;
+ private InstantiationBinding? _constructorBinding;
+ private InstantiationBinding? _serviceOnlyConstructorBinding;
private ConfigurationSource? _primaryKeyConfigurationSource;
private ConfigurationSource? _isKeylessConfigurationSource;
private ConfigurationSource? _baseTypeConfigurationSource;
private ConfigurationSource? _changeTrackingStrategyConfigurationSource;
+ private ConfigurationSource? _constructorBindingConfigurationSource;
+ private ConfigurationSource? _serviceOnlyConstructorBindingConfigurationSource;
// Warning: Never access these fields directly as access needs to be thread-safe
private PropertyCounts? _counts;
@@ -247,13 +251,7 @@ public virtual bool IsKeyless
public virtual ConfigurationSource? GetIsKeylessConfigurationSource()
=> _isKeylessConfigurationSource;
- ///
- /// 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 void UpdateIsKeylessConfigurationSource(ConfigurationSource configurationSource)
+ private void UpdateIsKeylessConfigurationSource(ConfigurationSource configurationSource)
=> _isKeylessConfigurationSource = configurationSource.Max(_isKeylessConfigurationSource);
///
@@ -2690,10 +2688,15 @@ public virtual Func InstanceFactory
{
entityType.EnsureReadOnly();
- var binding = (InstantiationBinding?)entityType[CoreAnnotationNames.ServiceOnlyConstructorBinding];
+ var binding = entityType.ServiceOnlyConstructorBinding;
if (binding == null)
{
- throw new InvalidOperationException(CoreStrings.NoParameterlessConstructor(entityType.DisplayName()));
+ var _ = entityType.ConstructorBinding;
+ binding = entityType.ServiceOnlyConstructorBinding;
+ if (binding == null)
+ {
+ throw new InvalidOperationException(CoreStrings.NoParameterlessConstructor(entityType.DisplayName()));
+ }
}
var contextParam = Expression.Parameter(typeof(MaterializationContext), "mc");
@@ -3314,6 +3317,117 @@ public virtual bool IsImplicitlyCreatedJoinEntityType
=> GetConfigurationSource() == ConfigurationSource.Convention
&& ClrType == Model.DefaultPropertyBagType;
+ ///
+ /// 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 InstantiationBinding? ConstructorBinding
+ {
+ get => IsReadOnly && !ClrType.IsAbstract
+ ? NonCapturingLazyInitializer.EnsureInitialized(ref _constructorBinding, this, static entityType =>
+ {
+ ((IModel)entityType.Model).GetModelDependencies().ConstructorBindingFactory.GetBindings(
+ (IReadOnlyEntityType)entityType,
+ out entityType._constructorBinding,
+ out entityType._serviceOnlyConstructorBinding);
+ })
+ : _constructorBinding;
+
+ [param: CanBeNull]
+ set => SetConstructorBinding(value, ConfigurationSource.Explicit);
+ }
+
+ ///
+ /// 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 InstantiationBinding? SetConstructorBinding(
+ [CanBeNull] InstantiationBinding? constructorBinding,
+ ConfigurationSource configurationSource)
+ {
+ EnsureMutable();
+
+ _constructorBinding = constructorBinding;
+
+ if (_constructorBinding == null)
+ {
+ _constructorBindingConfigurationSource = null;
+ }
+ else
+ {
+ UpdateConstructorBindingConfigurationSource(configurationSource);
+ }
+
+ return constructorBinding;
+ }
+
+ ///
+ /// 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 ConfigurationSource? GetConstructorBindingConfigurationSource()
+ => _constructorBindingConfigurationSource;
+
+ private void UpdateConstructorBindingConfigurationSource(ConfigurationSource configurationSource)
+ => _constructorBindingConfigurationSource = configurationSource.Max(_constructorBindingConfigurationSource);
+
+ ///
+ /// 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 InstantiationBinding? ServiceOnlyConstructorBinding
+ {
+ get => _serviceOnlyConstructorBinding;
+ [param: CanBeNull]
+ set => SetServiceOnlyConstructorBinding(value, ConfigurationSource.Explicit);
+ }
+
+ ///
+ /// 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 InstantiationBinding? SetServiceOnlyConstructorBinding(
+ [CanBeNull] InstantiationBinding? constructorBinding,
+ ConfigurationSource configurationSource)
+ {
+ EnsureMutable();
+
+ _serviceOnlyConstructorBinding = constructorBinding;
+
+ if (_serviceOnlyConstructorBinding == null)
+ {
+ _serviceOnlyConstructorBindingConfigurationSource = null;
+ }
+ else
+ {
+ UpdateServiceOnlyConstructorBindingConfigurationSource(configurationSource);
+ }
+
+ return constructorBinding;
+ }
+
+ ///
+ /// 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 ConfigurationSource? GetServiceOnlyConstructorBindingConfigurationSource()
+ => _serviceOnlyConstructorBindingConfigurationSource;
+
+ private void UpdateServiceOnlyConstructorBindingConfigurationSource(ConfigurationSource configurationSource)
+ => _serviceOnlyConstructorBindingConfigurationSource = configurationSource.Max(_serviceOnlyConstructorBindingConfigurationSource);
+
#endregion
#region Explicit interface implementations
diff --git a/src/EFCore/Metadata/Internal/EntityTypeParameterBindingFactory.cs b/src/EFCore/Metadata/Internal/EntityTypeParameterBindingFactory.cs
index 10d65369dbb..7fc168ba874 100644
--- a/src/EFCore/Metadata/Internal/EntityTypeParameterBindingFactory.cs
+++ b/src/EFCore/Metadata/Internal/EntityTypeParameterBindingFactory.cs
@@ -42,9 +42,11 @@ public virtual bool CanBind(
/// 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 ParameterBinding Bind(IMutableEntityType entityType, Type parameterType, string parameterName)
- => new EntityTypeParameterBinding(
- (IPropertyBase?)entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType));
+ public virtual ParameterBinding Bind(
+ IMutableEntityType entityType,
+ Type parameterType,
+ string parameterName)
+ => Bind((IReadOnlyEntityType)entityType, parameterType, parameterName);
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -56,6 +58,18 @@ public virtual ParameterBinding Bind(
IConventionEntityType entityType,
Type parameterType,
string parameterName)
+ => Bind((IReadOnlyEntityType)entityType, parameterType, parameterName);
+
+ ///
+ /// 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 ParameterBinding Bind(
+ IReadOnlyEntityType entityType,
+ Type parameterType,
+ string parameterName)
=> new EntityTypeParameterBinding(
(IPropertyBase?)entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == parameterType));
}
diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
index 737f6a58531..49c6ed06bff 100644
--- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
@@ -4262,6 +4262,64 @@ public virtual bool CanSetPropertyAccessMode(PropertyAccessMode? propertyAccessM
=> configurationSource.Overrides(Metadata.GetPropertyAccessModeConfigurationSource())
|| Metadata.GetPropertyAccessMode() == propertyAccessMode;
+ ///
+ /// 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 IConventionEntityTypeBuilder? HasConstructorBinding(
+ [CanBeNull] InstantiationBinding? constructorBinding, ConfigurationSource configurationSource)
+ {
+ if (CanSetConstructorBinding(constructorBinding, configurationSource))
+ {
+ Metadata.SetConstructorBinding(constructorBinding, 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.
+ ///
+ public virtual bool CanSetConstructorBinding([CanBeNull] InstantiationBinding? constructorBinding, ConfigurationSource configurationSource)
+ => configurationSource.Overrides(Metadata.GetConstructorBindingConfigurationSource())
+ || Metadata.ConstructorBinding == constructorBinding;
+
+ ///
+ /// 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 IConventionEntityTypeBuilder? HasServiceOnlyConstructorBinding(
+ [CanBeNull] InstantiationBinding? constructorBinding, ConfigurationSource configurationSource)
+ {
+ if (CanSetServiceOnlyConstructorBinding(constructorBinding, configurationSource))
+ {
+ Metadata.SetServiceOnlyConstructorBinding(constructorBinding, 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.
+ ///
+ public virtual bool CanSetServiceOnlyConstructorBinding([CanBeNull] InstantiationBinding? constructorBinding, ConfigurationSource configurationSource)
+ => configurationSource.Overrides(Metadata.GetServiceOnlyConstructorBindingConfigurationSource())
+ || Metadata.ServiceOnlyConstructorBinding == constructorBinding;
+
///
/// 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/Model.cs b/src/EFCore/Metadata/Internal/Model.cs
index 2a73a1cb1fd..765773a24e0 100644
--- a/src/EFCore/Metadata/Internal/Model.cs
+++ b/src/EFCore/Metadata/Internal/Model.cs
@@ -53,6 +53,7 @@ public class Model : ConventionAnnotatable, IMutableModel, IConventionModel, IMo
new() { { DefaultPropertyBagType, (ConfigurationSource.Convention, new SortedSet(EntityTypeFullNameComparer.Instance)) } };
private ConventionDispatcher? _conventionDispatcher;
+ private IList? _modelFinalizedConventions;
private bool? _skipDetectChanges;
private ChangeTrackingStrategy? _changeTrackingStrategy;
@@ -85,6 +86,7 @@ public Model([NotNull] ConventionSet conventions, [CanBeNull] ModelDependencies?
var dispatcher = new ConventionDispatcher(conventions);
var builder = new InternalModelBuilder(this);
_conventionDispatcher = dispatcher;
+ _modelFinalizedConventions = conventions.ModelFinalizedConventions;
Builder = builder;
dispatcher.OnModelInitialized(builder);
}
@@ -837,8 +839,6 @@ public virtual IModel FinalizeModel()
var finalizedModel = (IModel)ConventionDispatcher.OnModelFinalizing(Builder).Metadata;
- finalizedModel = ConventionDispatcher.OnModelFinalized(finalizedModel);
-
if (finalizedModel is Model model)
{
finalizedModel = model.MakeReadonly();
@@ -847,6 +847,25 @@ public virtual IModel FinalizeModel()
return finalizedModel;
}
+ ///
+ /// 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 IModel OnModelFinalized()
+ {
+ IModel model = this;
+ foreach (var modelConvention in _modelFinalizedConventions!)
+ {
+ model = modelConvention.ProcessModelFinalized(model);
+ }
+
+ _modelFinalizedConventions = null;
+
+ return model;
+ }
+
///
/// 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/Property.cs b/src/EFCore/Metadata/Internal/Property.cs
index 4e62de3c61e..f847af88344 100644
--- a/src/EFCore/Metadata/Internal/Property.cs
+++ b/src/EFCore/Metadata/Internal/Property.cs
@@ -10,6 +10,7 @@
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
@@ -506,9 +507,9 @@ private static bool DefaultIsConcurrencyToken
[CA.DisallowNull]
public virtual CoreTypeMapping? TypeMapping
{
- get
- => _typeMapping == null && IsReadOnly
- ? _typeMapping ??= ((IModel)DeclaringEntityType.Model).ModelDependencies?.TypeMappingSource.FindMapping(this)
+ get => IsReadOnly
+ ? NonCapturingLazyInitializer.EnsureInitialized(ref _typeMapping, (IProperty)this, static property =>
+ property.DeclaringEntityType.Model.GetModelDependencies().TypeMappingSource.FindMapping(property)!)
: _typeMapping;
[param: NotNull]
diff --git a/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs b/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs
index 9d4ab1db4b8..f992f5e4b4a 100644
--- a/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs
+++ b/src/EFCore/Metadata/Internal/PropertyBaseExtensions.cs
@@ -340,8 +340,7 @@ public static bool TryGetMemberInfo(
private static string GetNoFieldErrorMessage(IPropertyBase propertyBase)
{
- var constructorBinding = (InstantiationBinding?)propertyBase.DeclaringType[CoreAnnotationNames.ConstructorBinding];
-
+ var constructorBinding = ((EntityType)propertyBase.DeclaringType).ConstructorBinding;
return constructorBinding?.ParameterBindings
.OfType()
.Any(b => b.ServiceType == typeof(ILazyLoader))
diff --git a/src/EFCore/Metadata/Internal/ServiceProperty.cs b/src/EFCore/Metadata/Internal/ServiceProperty.cs
index 5bf94f53f91..8dba52349f7 100644
--- a/src/EFCore/Metadata/Internal/ServiceProperty.cs
+++ b/src/EFCore/Metadata/Internal/ServiceProperty.cs
@@ -7,6 +7,7 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Utilities;
@@ -114,7 +115,16 @@ public virtual void SetRemovedFromModel()
///
public virtual ServiceParameterBinding? ParameterBinding
{
- get => _parameterBinding;
+#pragma warning disable CS8766 // Nullability of reference types in return type doesn't match implicitly implemented member (possibly because of nullability attributes).
+ get => IsReadOnly
+ ? NonCapturingLazyInitializer.EnsureInitialized(ref _parameterBinding, (IServiceProperty)this, static property =>
+ {
+ var entityType = property.DeclaringEntityType;
+ var factory = entityType.Model.GetModelDependencies().ParameterBindingFactories.FindFactory(property.ClrType, property.Name)!;
+ return (ServiceParameterBinding)factory.Bind(entityType, property.ClrType, property.Name);
+ })
+ : _parameterBinding;
+#pragma warning restore CS8766 // Nullability of reference types in return type doesn't match implicitly implemented member (possibly because of nullability attributes).
set => SetParameterBinding(value, ConfigurationSource.Explicit);
}
diff --git a/src/EFCore/Metadata/Internal/ServicePropertyExtensions.cs b/src/EFCore/Metadata/Internal/ServicePropertyExtensions.cs
index 214a77ea521..23beb340456 100644
--- a/src/EFCore/Metadata/Internal/ServicePropertyExtensions.cs
+++ b/src/EFCore/Metadata/Internal/ServicePropertyExtensions.cs
@@ -16,15 +16,6 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal
///
public static class ServicePropertyExtensions
{
- ///
- /// 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 ServiceParameterBinding? GetParameterBinding([NotNull] this IServiceProperty serviceProperty)
- => serviceProperty.AsServiceProperty().ParameterBinding;
-
///
/// 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/LazyLoaderParameterBindingFactory.cs b/src/EFCore/Metadata/LazyLoaderParameterBindingFactory.cs
index 4f4034c5651..bd267dd8b05 100644
--- a/src/EFCore/Metadata/LazyLoaderParameterBindingFactory.cs
+++ b/src/EFCore/Metadata/LazyLoaderParameterBindingFactory.cs
@@ -113,6 +113,25 @@ public override ParameterBinding Bind(
return Bind((IEntityType)entityType, parameterType);
}
+ ///
+ /// Creates a for the given type and name on the given entity type.
+ ///
+ /// The entity type.
+ /// The parameter type.
+ /// The parameter name.
+ /// The binding.
+ public override ParameterBinding Bind(
+ IReadOnlyEntityType entityType,
+ Type parameterType,
+ string parameterName)
+ {
+ Check.NotNull(entityType, nameof(entityType));
+ Check.NotNull(parameterType, nameof(parameterType));
+ Check.NotEmpty(parameterName, nameof(parameterName));
+
+ return Bind((IEntityType)entityType, parameterType);
+ }
+
private static ParameterBinding Bind(IEntityType entityType, Type parameterType)
=> parameterType == typeof(ILazyLoader)
? new DependencyInjectionParameterBinding(
diff --git a/src/EFCore/Metadata/ServiceParameterBindingFactory.cs b/src/EFCore/Metadata/ServiceParameterBindingFactory.cs
index f3957eb7dc5..e912902852a 100644
--- a/src/EFCore/Metadata/ServiceParameterBindingFactory.cs
+++ b/src/EFCore/Metadata/ServiceParameterBindingFactory.cs
@@ -55,17 +55,11 @@ public virtual bool CanBind(
/// The parameter type.
/// The parameter name.
/// The binding.
- public virtual ParameterBinding Bind(IMutableEntityType entityType, Type parameterType, string parameterName)
- {
- Check.NotNull(entityType, nameof(entityType));
- Check.NotNull(parameterType, nameof(parameterType));
- Check.NotEmpty(parameterName, nameof(parameterName));
-
- return new DependencyInjectionParameterBinding(
- _serviceType,
- _serviceType,
- (IPropertyBase?)entityType.GetServiceProperties().FirstOrDefault(p => p.ClrType == _serviceType));
- }
+ public virtual ParameterBinding Bind(
+ IMutableEntityType entityType,
+ Type parameterType,
+ string parameterName)
+ => Bind((IReadOnlyEntityType)entityType, parameterType, parameterName);
///
/// Creates a for the given type and name on the given entity type.
@@ -78,6 +72,19 @@ public virtual ParameterBinding Bind(
IConventionEntityType entityType,
Type parameterType,
string parameterName)
+ => Bind((IReadOnlyEntityType)entityType, parameterType, parameterName);
+
+ ///
+ /// Creates a for the given type and name on the given entity type.
+ ///
+ /// The entity type.
+ /// The parameter type.
+ /// The parameter name.
+ /// The binding.
+ public virtual ParameterBinding Bind(
+ IReadOnlyEntityType entityType,
+ Type parameterType,
+ string parameterName)
{
Check.NotNull(entityType, nameof(entityType));
Check.NotNull(parameterType, nameof(parameterType));
diff --git a/src/EFCore/Query/Internal/EntityMaterializerSource.cs b/src/EFCore/Query/Internal/EntityMaterializerSource.cs
index 1d7edf2a337..ce9753fabef 100644
--- a/src/EFCore/Query/Internal/EntityMaterializerSource.cs
+++ b/src/EFCore/Query/Internal/EntityMaterializerSource.cs
@@ -64,19 +64,7 @@ public virtual Expression CreateMaterializeExpression(
throw new InvalidOperationException(CoreStrings.CannotMaterializeAbstractType(entityType.DisplayName()));
}
- var constructorBinding = (InstantiationBinding?)entityType[CoreAnnotationNames.ConstructorBinding];
-
- if (constructorBinding == null)
- {
- var constructorInfo = entityType.ClrType.GetDeclaredConstructor(null);
-
- if (constructorInfo == null)
- {
- throw new InvalidOperationException(CoreStrings.NoParameterlessConstructor(entityType.DisplayName()));
- }
-
- constructorBinding = new ConstructorBinding(constructorInfo, Array.Empty());
- }
+ var constructorBinding = entityType.ConstructorBinding!;
var bindingInfo = new ParameterBindingInfo(
entityType,
@@ -121,7 +109,7 @@ var blockExpressions
var readValueExpression
= property is IServiceProperty serviceProperty
- ? serviceProperty.GetParameterBinding()!.BindToParameter(bindingInfo)
+ ? serviceProperty.ParameterBinding.BindToParameter(bindingInfo)
: valueBufferExpression.CreateValueBufferReadValueExpression(
memberInfo.GetMemberType(),
property.GetIndex(),
diff --git a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs
index 9d5fad851e6..5089d465fa1 100644
--- a/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/TestUtilities/CosmosTestStore.cs
@@ -333,6 +333,9 @@ public bool HasSharedClrType
public bool IsPropertyBag
=> throw new NotImplementedException();
+ public InstantiationBinding ConstructorBinding
+ => throw new NotImplementedException();
+
IReadOnlyEntityType IReadOnlyEntityType.BaseType
=> throw new NotImplementedException();
diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs
index 301bb31e568..3d3867c8cac 100644
--- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationsGeneratorTest.cs
@@ -160,8 +160,6 @@ public void Test_new_annotations_handled_for_properties()
{
CoreAnnotationNames.ProductVersion,
CoreAnnotationNames.OwnedTypes,
- CoreAnnotationNames.ConstructorBinding,
- CoreAnnotationNames.ServiceOnlyConstructorBinding,
CoreAnnotationNames.NavigationAccessMode,
CoreAnnotationNames.EagerLoaded,
CoreAnnotationNames.QueryFilter,
@@ -294,7 +292,7 @@ private static void MissingAnnotationCheck(
? validAnnotations[annotationName].Value
: null);
- modelBuilder.FinalizeModel();
+ SqlServerTestHelpers.Instance.Finalize(modelBuilder);
var sb = new IndentedStringBuilder();
@@ -389,7 +387,7 @@ public void Snapshot_with_enum_discriminator_uses_converted_values()
eb.Property("EnumDiscriminator").HasConversion();
});
- var finalizedModel = modelBuilder.FinalizeModel();
+ var finalizedModel = SqlServerTestHelpers.Instance.Finalize(modelBuilder);
var modelSnapshotCode = generator.GenerateSnapshot(
"MyNamespace",
@@ -509,7 +507,7 @@ protected override void Down(MigrationBuilder migrationBuilder)
var modelBuilder = new ModelBuilder();
modelBuilder.HasAnnotation("Some:EnumValue", RegexOptions.Multiline);
- modelBuilder.HasAnnotation(RelationalAnnotationNames.DbFunctions, new object());
+ modelBuilder.HasAnnotation(RelationalAnnotationNames.DbFunctions, new SortedDictionary());
modelBuilder.Entity(
"T1", eb =>
{
@@ -519,7 +517,7 @@ protected override void Down(MigrationBuilder migrationBuilder)
eb.HasKey("Id");
});
- var finalizedModel = modelBuilder.FinalizeModel();
+ var finalizedModel = SqlServerTestHelpers.Instance.Finalize(modelBuilder);
var migrationMetadataCode = generator.GenerateMetadata(
"MyNamespace",
@@ -651,7 +649,7 @@ public void Snapshots_compile()
entityType.SetPrimaryKey(property2);
- var finalizedModel = modelBuilder.FinalizeModel();
+ var finalizedModel = SqlServerTestHelpers.Instance.Finalize(modelBuilder);
var modelSnapshotCode = generator.GenerateSnapshot(
"MyNamespace",
@@ -767,7 +765,7 @@ public void Snapshot_with_default_values_are_round_tripped()
eb.HasKey(e => e.Boolean);
});
- var finalizedModel = modelBuilder.FinalizeModel();
+ var finalizedModel = SqlServerTestHelpers.Instance.Finalize(modelBuilder);
var modelSnapshotCode = generator.GenerateSnapshot(
"MyNamespace",
diff --git a/test/EFCore.Design.Tests/Migrations/Design/SnapshotModelProcessorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/SnapshotModelProcessorTest.cs
index 72bc33a7732..7b143d62d2b 100644
--- a/test/EFCore.Design.Tests/Migrations/Design/SnapshotModelProcessorTest.cs
+++ b/test/EFCore.Design.Tests/Migrations/Design/SnapshotModelProcessorTest.cs
@@ -6,14 +6,11 @@
using System.Linq;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
-using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
-using Microsoft.EntityFrameworkCore.Metadata.Conventions;
-using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.Extensions.DependencyInjection;
diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalScaffoldingModelFactoryTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalDatabaseModelFactoryTest.cs
similarity index 96%
rename from test/EFCore.Design.Tests/Scaffolding/Internal/RelationalScaffoldingModelFactoryTest.cs
rename to test/EFCore.Design.Tests/Scaffolding/Internal/RelationalDatabaseModelFactoryTest.cs
index 6ebfb7126f1..98305092296 100644
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalScaffoldingModelFactoryTest.cs
+++ b/test/EFCore.Design.Tests/Scaffolding/Internal/RelationalDatabaseModelFactoryTest.cs
@@ -3,7 +3,6 @@
using System;
using System.Linq;
-using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
@@ -12,7 +11,6 @@
using Microsoft.EntityFrameworkCore.Scaffolding.Internal;
using Microsoft.EntityFrameworkCore.Scaffolding.Metadata;
using Microsoft.EntityFrameworkCore.Scaffolding.Metadata.Internal;
-using Microsoft.EntityFrameworkCore.SqlServer.Design.Internal;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
@@ -51,14 +49,14 @@ public RelationalDatabaseModelFactoryTest()
{
_reporter = new TestOperationReporter();
- var services = new ServiceCollection()
- .AddEntityFrameworkDesignTimeServices(_reporter)
- .AddSingleton();
- new SqlServerDesignTimeServices().ConfigureDesignTimeServices(services);
-
- _factory = services
+ var assembly = typeof(RelationalDatabaseModelFactoryTest).Assembly;
+ _factory = new DesignTimeServicesBuilder(assembly, assembly, _reporter, new string[0])
+ .CreateServiceCollection(SqlServerTestHelpers.Instance.CreateContext())
+ .AddSingleton()
.BuildServiceProvider()
.GetRequiredService();
+
+ _reporter.Clear();
}
[ConditionalFact]
@@ -374,7 +372,7 @@ public void Column_type_annotation(string storeType, string expectedColumnType)
var property = (Property)_factory.Create(info, new ModelReverseEngineerOptions()).FindEntityType("A").FindProperty("Col");
- Assert.Equal(expectedColumnType, property.GetColumnType());
+ Assert.Equal(expectedColumnType, property.GetConfiguredColumnType());
}
[ConditionalFact]
@@ -1541,17 +1539,7 @@ public void Pluralization_of_entity_and_DbSet()
}
};
- var services = new ServiceCollection()
- .AddEntityFrameworkDesignTimeServices(_reporter)
- .AddSingleton()
- .AddSingleton();
- new SqlServerDesignTimeServices().ConfigureDesignTimeServices(services);
-
- var factory = services
- .BuildServiceProvider()
- .GetRequiredService();
-
- var model = factory.Create(info, new ModelReverseEngineerOptions());
+ var model = _factory.Create(info, new ModelReverseEngineerOptions());
Assert.Collection(
model.GetEntityTypes().OrderBy(t => t.Name).Cast(),
@@ -1594,17 +1582,7 @@ public void Pluralization_of_entity_and_DbSet_noPluralize()
}
};
- var services = new ServiceCollection()
- .AddEntityFrameworkDesignTimeServices(_reporter)
- .AddSingleton()
- .AddSingleton();
- new SqlServerDesignTimeServices().ConfigureDesignTimeServices(services);
-
- var factory = services
- .BuildServiceProvider()
- .GetRequiredService();
-
- var model = factory.Create(info, new ModelReverseEngineerOptions { NoPluralize = true });
+ var model = _factory.Create(info, new ModelReverseEngineerOptions { NoPluralize = true });
Assert.Collection(
model.GetEntityTypes().OrderBy(t => t.Name).Cast(),
@@ -1622,7 +1600,7 @@ public void Pluralization_of_entity_and_DbSet_noPluralize()
}
);
- model = factory.Create(info, new ModelReverseEngineerOptions { UseDatabaseNames = true, NoPluralize = true });
+ model = _factory.Create(info, new ModelReverseEngineerOptions { UseDatabaseNames = true, NoPluralize = true });
Assert.Collection(
model.GetEntityTypes().OrderBy(t => t.Name).Cast(),
@@ -1682,17 +1660,7 @@ public void Pluralization_of_collection_navigations()
var info = new DatabaseModel { Tables = { blogTable, postTable } };
- var services = new ServiceCollection()
- .AddEntityFrameworkDesignTimeServices(_reporter)
- .AddSingleton()
- .AddSingleton();
- new SqlServerDesignTimeServices().ConfigureDesignTimeServices(services);
-
- var factory = services
- .BuildServiceProvider()
- .GetRequiredService();
-
- var model = factory.Create(info, new ModelReverseEngineerOptions());
+ var model = _factory.Create(info, new ModelReverseEngineerOptions());
Assert.Collection(
model.GetEntityTypes().OrderBy(t => t.Name).Cast(),
@@ -1750,17 +1718,7 @@ public void Pluralization_of_collection_navigations_noPluralize()
var info = new DatabaseModel { Tables = { blogTable, postTable } };
- var services = new ServiceCollection()
- .AddEntityFrameworkDesignTimeServices(_reporter)
- .AddSingleton()
- .AddSingleton();
- new SqlServerDesignTimeServices().ConfigureDesignTimeServices(services);
-
- var factory = services
- .BuildServiceProvider()
- .GetRequiredService();
-
- var model = factory.Create(info, new ModelReverseEngineerOptions { NoPluralize = true });
+ var model = _factory.Create(info, new ModelReverseEngineerOptions { NoPluralize = true });
Assert.Collection(
model.GetEntityTypes().OrderBy(t => t.Name).Cast(),
@@ -1979,11 +1937,11 @@ public void Correct_arguments_to_scaffolding_typemapper()
var model = _factory.Create(dbModel, new ModelReverseEngineerOptions());
- Assert.Null(model.FindEntityType("Principal").FindProperty("PrimaryKey").GetColumnType());
- Assert.Null(model.FindEntityType("Principal").FindProperty("AlternateKey").GetColumnType());
- Assert.Null(model.FindEntityType("Principal").FindProperty("Index").GetColumnType());
- Assert.Null(model.FindEntityType("Principal").FindProperty("Rowversion").GetColumnType());
- Assert.Null(model.FindEntityType("Dependent").FindProperty("BlogAlternateKey").GetColumnType());
+ Assert.Null(model.FindEntityType("Principal").FindProperty("PrimaryKey").GetConfiguredColumnType());
+ Assert.Null(model.FindEntityType("Principal").FindProperty("AlternateKey").GetConfiguredColumnType());
+ Assert.Null(model.FindEntityType("Principal").FindProperty("Index").GetConfiguredColumnType());
+ Assert.Null(model.FindEntityType("Principal").FindProperty("Rowversion").GetConfiguredColumnType());
+ Assert.Null(model.FindEntityType("Dependent").FindProperty("BlogAlternateKey").GetConfiguredColumnType());
}
[ConditionalFact]
diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/ReverseEngineerScaffolderTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/ReverseEngineerScaffolderTest.cs
index 73d04cde77b..6c8864db950 100644
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/ReverseEngineerScaffolderTest.cs
+++ b/test/EFCore.Design.Tests/Scaffolding/Internal/ReverseEngineerScaffolderTest.cs
@@ -6,6 +6,7 @@
using System.Globalization;
using System.IO;
using Microsoft.EntityFrameworkCore.Design;
+using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
@@ -128,14 +129,12 @@ public void Save_throws_when_readonly_files()
}
private static IReverseEngineerScaffolder CreateScaffolder()
- => new ServiceCollection()
- .AddEntityFrameworkDesignTimeServices()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
+ => new DesignTimeServicesBuilder(
+ typeof(ReverseEngineerScaffolderTest).Assembly,
+ typeof(ReverseEngineerScaffolderTest).Assembly,
+ new TestOperationReporter(),
+ new string[0])
+ .CreateServiceCollection(SqlServerTestHelpers.Instance.CreateContext())
.BuildServiceProvider()
.GetRequiredService();
@@ -144,14 +143,14 @@ public void ScaffoldModel_works_with_named_connection_string()
{
var resolver = new TestNamedConnectionStringResolver("Data Source=Test");
var databaseModelFactory = new TestDatabaseModelFactory();
- var scaffolder = new ServiceCollection()
- .AddEntityFrameworkDesignTimeServices()
+ var scaffolder = new DesignTimeServicesBuilder(
+ typeof(ReverseEngineerScaffolderTest).Assembly,
+ typeof(ReverseEngineerScaffolderTest).Assembly,
+ new TestOperationReporter(),
+ new string[0])
+ .CreateServiceCollection(SqlServerTestHelpers.Instance.CreateContext())
.AddSingleton(resolver)
.AddSingleton(databaseModelFactory)
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
.BuildServiceProvider()
.GetRequiredService();
@@ -174,14 +173,14 @@ public void ScaffoldModel_works_with_overridden_connection_string()
var resolver = new TestNamedConnectionStringResolver("Data Source=Test");
var databaseModelFactory = new TestDatabaseModelFactory();
databaseModelFactory.ScaffoldedConnectionString = "Data Source=ScaffoldedConnectionString";
- var scaffolder = new ServiceCollection()
- .AddEntityFrameworkDesignTimeServices()
+ var scaffolder = new DesignTimeServicesBuilder(
+ typeof(ReverseEngineerScaffolderTest).Assembly,
+ typeof(ReverseEngineerScaffolderTest).Assembly,
+ new TestOperationReporter(),
+ new string[0])
+ .CreateServiceCollection(SqlServerTestHelpers.Instance.CreateContext())
.AddSingleton(resolver)
.AddSingleton(databaseModelFactory)
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
.BuildServiceProvider()
.GetRequiredService();
diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/ReverseEngineeringConfigurationTests.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/ReverseEngineeringConfigurationTests.cs
index 67d18e5453f..288bf030ff9 100644
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/ReverseEngineeringConfigurationTests.cs
+++ b/test/EFCore.Design.Tests/Scaffolding/Internal/ReverseEngineeringConfigurationTests.cs
@@ -3,6 +3,7 @@
using System;
using Microsoft.EntityFrameworkCore.Design;
+using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Storage;
@@ -24,15 +25,9 @@ public void Throws_exceptions_for_invalid_context_name()
private void ValidateContextNameInReverseEngineerGenerator(string contextName)
{
- var reverseEngineer = new ServiceCollection()
- .AddEntityFrameworkDesignTimeServices()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .AddSingleton()
- .BuildServiceProvider()
+ var assembly = typeof(ReverseEngineeringConfigurationTests).Assembly;
+ var reverseEngineer = new DesignTimeServicesBuilder(assembly, assembly, new TestOperationReporter(), new string[0])
+ .Build(SqlServerTestHelpers.Instance.CreateContext())
.GetRequiredService();
Assert.Equal(
diff --git a/test/EFCore.Design.Tests/TestUtilities/FakeScaffoldingModelFactory.cs b/test/EFCore.Design.Tests/TestUtilities/FakeScaffoldingModelFactory.cs
index 7e8e9136405..a213a7ca5ab 100644
--- a/test/EFCore.Design.Tests/TestUtilities/FakeScaffoldingModelFactory.cs
+++ b/test/EFCore.Design.Tests/TestUtilities/FakeScaffoldingModelFactory.cs
@@ -6,6 +6,7 @@
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Design.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.EntityFrameworkCore.Scaffolding.Internal;
@@ -21,8 +22,9 @@ public FakeScaffoldingModelFactory(
IPluralizer pluralizer,
ICSharpUtilities cSharpUtilities,
IScaffoldingTypeMapper scaffoldingTypeMapper,
- LoggingDefinitions loggingDefinitions)
- : base(reporter, candidateNamingService, pluralizer, cSharpUtilities, scaffoldingTypeMapper, loggingDefinitions)
+ LoggingDefinitions loggingDefinitions,
+ IModelRuntimeInitializer modelRuntimeInitializer)
+ : base(reporter, candidateNamingService, pluralizer, cSharpUtilities, scaffoldingTypeMapper, loggingDefinitions, modelRuntimeInitializer)
{
}
diff --git a/test/EFCore.Relational.Tests/Metadata/DbFunctionMetadataTests.cs b/test/EFCore.Relational.Tests/Metadata/DbFunctionMetadataTests.cs
index edc09e93a78..86cc1c7b89b 100644
--- a/test/EFCore.Relational.Tests/Metadata/DbFunctionMetadataTests.cs
+++ b/test/EFCore.Relational.Tests/Metadata/DbFunctionMetadataTests.cs
@@ -874,7 +874,6 @@ private ModelBuilder GetModelBuilder(DbContext dbContext = null)
var dbFunctionAttributeConvention = new RelationalDbFunctionAttributeConvention(dependencies, relationalDependencies);
conventionSet.ModelInitializedConventions.Add(dbFunctionAttributeConvention);
conventionSet.ModelFinalizingConventions.Add(dbFunctionAttributeConvention);
- conventionSet.ModelFinalizingConventions.Add(new DbFunctionTypeMappingConvention(dependencies, relationalDependencies));
conventionSet.ModelFinalizingConventions.Add(new TableValuedDbFunctionConvention(dependencies, relationalDependencies));
return new ModelBuilder(conventionSet);
diff --git a/test/EFCore.Tests/DbContextServicesTest.cs b/test/EFCore.Tests/DbContextServicesTest.cs
index 4e718e5eb5e..5090b5fb77a 100644
--- a/test/EFCore.Tests/DbContextServicesTest.cs
+++ b/test/EFCore.Tests/DbContextServicesTest.cs
@@ -2709,6 +2709,9 @@ public ParameterBinding Bind(IMutableEntityType entityType, Type parameterType,
public ParameterBinding Bind(IConventionEntityType entityType, Type parameterType, string parameterName)
=> throw new NotImplementedException();
+
+ public ParameterBinding Bind([NotNull] IReadOnlyEntityType entityType, [NotNull] Type parameterType, [NotNull] string parameterName)
+ => throw new NotImplementedException();
}
private class CustomParameterBindingFactory2 : IParameterBindingFactory
@@ -2721,6 +2724,9 @@ public ParameterBinding Bind(IMutableEntityType entityType, Type parameterType,
public ParameterBinding Bind(IConventionEntityType entityType, Type parameterType, string parameterName)
=> throw new NotImplementedException();
+
+ public ParameterBinding Bind([NotNull] IReadOnlyEntityType entityType, [NotNull] Type parameterType, [NotNull] string parameterName)
+ => throw new NotImplementedException();
}
private class CustomModelCustomizer : ModelCustomizer
diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
index bb79b430a3e..860d8806ec0 100644
--- a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
+++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
@@ -111,18 +111,18 @@ public virtual void Ignores_custom_converter_for_collection_type_with_comparer()
private IMutableProperty CreateConvertedCollectionProperty()
{
- var model = CreateConventionlessModelBuilder().Model;
-
- var entityType = model.AddEntityType(typeof(WithCollectionConversion));
- entityType.SetPrimaryKey(entityType.AddProperty(nameof(WithCollectionConversion.Id), typeof(int)));
-
- var convertedProperty = entityType.AddProperty(
- nameof(WithCollectionConversion.SomeStrings), typeof(string[]));
+ var modelBuilder = CreateConventionalModelBuilder();
- convertedProperty.SetValueConverter(
+ IMutableProperty convertedProperty = null;
+ modelBuilder.Entity(eb =>
+ {
+ eb.Property(e => e.Id);
+ convertedProperty = eb.Property(e => e.SomeStrings).Metadata;
+ convertedProperty.SetValueConverter(
new ValueConverter(
v => string.Join(',', v),
v => v.Split(',', StringSplitOptions.None)));
+ });
return convertedProperty;
}
diff --git a/test/EFCore.Tests/Metadata/Conventions/ConstructorBindingConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/ConstructorBindingConventionTest.cs
index 9bd0fa4e0fe..3e64399978a 100644
--- a/test/EFCore.Tests/Metadata/Conventions/ConstructorBindingConventionTest.cs
+++ b/test/EFCore.Tests/Metadata/Conventions/ConstructorBindingConventionTest.cs
@@ -387,7 +387,7 @@ public void Throws_if_two_constructors_with_same_number_of_parameters_could_be_u
public void Does_not_throw_if_explicit_binding_has_been_set()
{
var constructorBinding = GetBinding(
- e => e[CoreAnnotationNames.ConstructorBinding] = new ConstructorBinding(
+ e => ((EntityType)e).ConstructorBinding = new ConstructorBinding(
typeof(BlogConflict).GetConstructor(
new[] { typeof(string), typeof(int) }),
new[]
@@ -796,7 +796,7 @@ private ConstructorBinding GetBinding(Action setBin
var convention = new ConstructorBindingConvention(CreateDependencies());
convention.ProcessModelFinalizing(model.Builder, context);
- return (ConstructorBinding)entityType[CoreAnnotationNames.ConstructorBinding];
+ return (ConstructorBinding)((EntityType)entityType).ConstructorBinding;
}
private ProviderConventionSetBuilderDependencies CreateDependencies()
diff --git a/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs b/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs
index 4c438ce2d99..ea3bb86d973 100644
--- a/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs
+++ b/test/EFCore.Tests/Metadata/Internal/ClrPropertyGetterFactoryTest.cs
@@ -61,7 +61,7 @@ public void Delegate_getter_is_returned_for_IProperty_property()
{
var modelBuilder = CreateModelBuilder();
var idProperty = modelBuilder.Entity().Property(e => e.Id).Metadata;
- modelBuilder.FinalizeModel();
+ InMemoryTestHelpers.Instance.Finalize(modelBuilder);
Assert.Equal(
7, new ClrPropertyGetterFactory().Create((IPropertyBase)idProperty).GetClrValue(
@@ -82,7 +82,7 @@ public void Delegate_getter_is_returned_for_IProperty_struct_property()
var modelBuilder = CreateModelBuilder();
modelBuilder.Entity().Property(e => e.Id);
var fuelProperty = modelBuilder.Entity().Property(e => e.Fuel).Metadata;
- modelBuilder.FinalizeModel();
+ InMemoryTestHelpers.Instance.Finalize(modelBuilder);
Assert.Equal(
new Fuel(1.0),
@@ -106,7 +106,7 @@ public void Delegate_getter_is_returned_for_index_property()
modelBuilder.Entity().Property(e => e.Id);
var propertyA = modelBuilder.Entity().Metadata.AddIndexerProperty("PropertyA", typeof(string));
var propertyB = modelBuilder.Entity().Metadata.AddIndexerProperty("PropertyB", typeof(int));
- modelBuilder.FinalizeModel();
+ InMemoryTestHelpers.Instance.Finalize(modelBuilder);
Assert.Equal("ValueA", new ClrPropertyGetterFactory().Create((IPropertyBase)propertyA).GetClrValue(new IndexedClass { Id = 7 }));
Assert.Equal(123, new ClrPropertyGetterFactory().Create((IPropertyBase)propertyB).GetClrValue(new IndexedClass { Id = 7 }));
diff --git a/test/EFCore.Tests/Metadata/Internal/EntityMaterializerSourceTest.cs b/test/EFCore.Tests/Metadata/Internal/EntityMaterializerSourceTest.cs
index 57f5578d5c1..77bdd34ddbf 100644
--- a/test/EFCore.Tests/Metadata/Internal/EntityMaterializerSourceTest.cs
+++ b/test/EFCore.Tests/Metadata/Internal/EntityMaterializerSourceTest.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@@ -10,6 +11,7 @@
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;
// ReSharper disable UnusedMember.Local
@@ -27,7 +29,7 @@ public class EntityMaterializerSourceTest
[ConditionalFact]
public void Throws_for_abstract_types()
{
- var entityType = ((IMutableModel)new Model()).AddEntityType(typeof(SomeAbstractEntity));
+ var entityType = ((IMutableModel)CreateConventionalModelBuilder().Model).AddEntityType(typeof(SomeAbstractEntity));
var source = new EntityMaterializerSource(new EntityMaterializerSourceDependencies());
Assert.Equal(
@@ -40,7 +42,7 @@ public void Can_create_materializer_for_entity_with_constructor_properties()
{
var entityType = CreateEntityType();
- entityType[CoreAnnotationNames.ConstructorBinding]
+ entityType.ConstructorBinding
= new ConstructorBinding(
typeof(SomeEntity).GetTypeInfo().DeclaredConstructors.Single(c => c.GetParameters().Length == 2),
new List
@@ -49,14 +51,15 @@ public void Can_create_materializer_for_entity_with_constructor_properties()
new PropertyParameterBinding((IProperty)entityType.FindProperty(nameof(SomeEntity.Goo)))
}
);
- ((Model)entityType.Model).FinalizeModel();
+
+ entityType.Model.FinalizeModel();
var factory = GetMaterializer(new EntityMaterializerSource(new EntityMaterializerSourceDependencies()), entityType);
var gu = Guid.NewGuid();
var entity = (SomeEntity)factory(
new MaterializationContext(
- new ValueBuffer(new object[] { SomeEnum.EnumValue, "Fu", gu, 77, SomeEnum.EnumValue }),
+ new ValueBuffer(new object[] { 77, SomeEnum.EnumValue, "Fu", gu, SomeEnum.EnumValue }),
_fakeContext));
Assert.Equal(77, entity.Id);
@@ -76,7 +79,7 @@ public void Can_create_materializer_for_entity_with_factory_method()
{
var entityType = CreateEntityType();
- entityType[CoreAnnotationNames.ConstructorBinding]
+ entityType.ConstructorBinding
= new FactoryMethodBinding(
typeof(SomeEntity).GetTypeInfo().GetDeclaredMethod(nameof(SomeEntity.Factory)),
new List
@@ -85,14 +88,15 @@ public void Can_create_materializer_for_entity_with_factory_method()
new PropertyParameterBinding((IProperty)entityType.FindProperty(nameof(SomeEntity.Goo)))
},
entityType.ClrType);
- ((Model)entityType.Model).FinalizeModel();
+
+ entityType.Model.FinalizeModel();
var factory = GetMaterializer(new EntityMaterializerSource(new EntityMaterializerSourceDependencies()), entityType);
var gu = Guid.NewGuid();
var entity = (SomeEntity)factory(
new MaterializationContext(
- new ValueBuffer(new object[] { SomeEnum.EnumValue, "Fu", gu, 77, SomeEnum.EnumValue }),
+ new ValueBuffer(new object[] { 77, SomeEnum.EnumValue, "Fu", gu, SomeEnum.EnumValue }),
_fakeContext));
Assert.Equal(77, entity.Id);
@@ -112,7 +116,7 @@ public void Can_create_materializer_for_entity_with_factory_method_with_object_a
{
var entityType = CreateEntityType();
- entityType[CoreAnnotationNames.ConstructorBinding]
+ entityType.ConstructorBinding
= new FactoryMethodBinding(
typeof(SomeEntity).GetTypeInfo().GetDeclaredMethod(nameof(SomeEntity.GeneralFactory)),
new List
@@ -125,14 +129,15 @@ public void Can_create_materializer_for_entity_with_factory_method_with_object_a
})
},
entityType.ClrType);
- ((Model)entityType.Model).FinalizeModel();
+
+ entityType.Model.FinalizeModel();
var factory = GetMaterializer(new EntityMaterializerSource(new EntityMaterializerSourceDependencies()), entityType);
var gu = Guid.NewGuid();
var entity = (SomeEntity)factory(
new MaterializationContext(
- new ValueBuffer(new object[] { SomeEnum.EnumValue, "Fu", gu, 77, SomeEnum.EnumValue }),
+ new ValueBuffer(new object[] { 77, SomeEnum.EnumValue, "Fu", gu, SomeEnum.EnumValue }),
_fakeContext));
Assert.Equal(77, entity.Id);
@@ -152,20 +157,21 @@ public void Can_create_materializer_for_entity_with_instance_factory_method()
{
var entityType = CreateEntityType();
- entityType[CoreAnnotationNames.ConstructorBinding]
+ entityType.ConstructorBinding
= new FactoryMethodBinding(
TestProxyFactory.Instance,
typeof(TestProxyFactory).GetTypeInfo().GetDeclaredMethod(nameof(TestProxyFactory.Create)),
new List { new EntityTypeParameterBinding() },
entityType.ClrType);
- ((Model)entityType.Model).FinalizeModel();
+
+ entityType.Model.FinalizeModel();
var factory = GetMaterializer(new EntityMaterializerSource(new EntityMaterializerSourceDependencies()), entityType);
var gu = Guid.NewGuid();
var entity = (SomeEntity)factory(
new MaterializationContext(
- new ValueBuffer(new object[] { SomeEnum.EnumValue, "Fu", gu, 77, SomeEnum.EnumValue }),
+ new ValueBuffer(new object[] { 77, SomeEnum.EnumValue, "Fu", gu, SomeEnum.EnumValue }),
_fakeContext));
Assert.Equal(77, entity.Id);
@@ -188,29 +194,21 @@ public object Create(IEntityType entityType)
=> Activator.CreateInstance(entityType.ClrType);
}
- private static IMutableEntityType CreateEntityType()
- {
- var entityType = ((IMutableModel)new Model()).AddEntityType(typeof(SomeEntity));
- entityType.AddProperty(SomeEntity.EnumProperty);
- entityType.AddProperty(SomeEntity.FooProperty);
- entityType.AddProperty(SomeEntity.GooProperty);
- entityType.AddProperty(SomeEntity.IdProperty);
- entityType.AddProperty(SomeEntity.MaybeEnumProperty);
- return entityType;
- }
+ private EntityType CreateEntityType()
+ => (EntityType)CreateConventionalModelBuilder().Entity().Metadata;
[ConditionalFact]
public void Can_create_materializer_for_entity_with_auto_properties()
{
var entityType = CreateEntityType();
- ((Model)entityType.Model).FinalizeModel();
+ entityType.Model.FinalizeModel();
var factory = GetMaterializer(new EntityMaterializerSource(new EntityMaterializerSourceDependencies()), entityType);
var gu = Guid.NewGuid();
var entity = (SomeEntity)factory(
new MaterializationContext(
- new ValueBuffer(new object[] { SomeEnum.EnumValue, "Fu", gu, 77, SomeEnum.EnumValue }),
+ new ValueBuffer(new object[] { 77, SomeEnum.EnumValue, "Fu", gu, SomeEnum.EnumValue }),
_fakeContext));
Assert.Equal(77, entity.Id);
@@ -223,20 +221,26 @@ public void Can_create_materializer_for_entity_with_auto_properties()
[ConditionalFact]
public void Can_create_materializer_for_entity_with_fields()
{
- var entityType = ((IMutableModel)new Model()).AddEntityType(typeof(SomeEntityWithFields));
- entityType.AddProperty(SomeEntityWithFields.EnumProperty).SetField("_enum");
- entityType.AddProperty(SomeEntityWithFields.FooProperty).SetField("_foo");
- entityType.AddProperty(SomeEntityWithFields.GooProperty).SetField("_goo");
- entityType.AddProperty(SomeEntityWithFields.IdProperty).SetField("_id");
- entityType.AddProperty(SomeEntityWithFields.MaybeEnumProperty).SetField("_maybeEnum");
- ((Model)entityType.Model).FinalizeModel();
+ var modelBuilder = CreateConventionalModelBuilder();
+ modelBuilder.Entity(eb =>
+ {
+ eb.UsePropertyAccessMode(PropertyAccessMode.Field);
+
+ eb.Property(e => e.Enum).HasField("_enum");
+ eb.Property(e => e.Foo).HasField("_foo");
+ eb.Property(e => e.Goo).HasField("_goo");
+ eb.Property(e => e.Id).HasField("_id");
+ eb.Property(e => e.MaybeEnum).HasField("_maybeEnum");
+ });
+
+ var entityType = modelBuilder.FinalizeModel().FindEntityType(typeof(SomeEntityWithFields));
var factory = GetMaterializer(new EntityMaterializerSource(new EntityMaterializerSourceDependencies()), entityType);
var gu = Guid.NewGuid();
var entity = (SomeEntityWithFields)factory(
new MaterializationContext(
- new ValueBuffer(new object[] { SomeEnum.EnumValue, "Fu", gu, 77, null }),
+ new ValueBuffer(new object[] { 77, SomeEnum.EnumValue, "Fu", gu, null }),
_fakeContext));
Assert.Equal(77, entity.Id);
@@ -249,17 +253,20 @@ public void Can_create_materializer_for_entity_with_fields()
[ConditionalFact]
public void Can_read_nulls()
{
- var entityType = ((IMutableModel)new Model()).AddEntityType(typeof(SomeEntity));
- entityType.AddProperty(SomeEntity.FooProperty);
- entityType.AddProperty(SomeEntity.GooProperty);
- entityType.AddProperty(SomeEntity.IdProperty);
- ((Model)entityType.Model).FinalizeModel();
+ var modelBuilder = CreateConventionalModelBuilder();
+ modelBuilder.Entity(eb =>
+ {
+ eb.Ignore(e => e.Enum);
+ eb.Ignore(e => e.MaybeEnum);
+ });
+
+ var entityType = modelBuilder.FinalizeModel().FindEntityType(typeof(SomeEntity));
var factory = GetMaterializer(new EntityMaterializerSource(new EntityMaterializerSourceDependencies()), entityType);
var entity = (SomeEntity)factory(
new MaterializationContext(
- new ValueBuffer(new object[] { null, null, 77 }),
+ new ValueBuffer(new object[] { 77, null, null}),
_fakeContext));
Assert.Equal(77, entity.Id);
@@ -270,21 +277,28 @@ public void Can_read_nulls()
[ConditionalFact]
public void Can_create_materializer_for_entity_ignoring_shadow_fields()
{
- var entityType = ((IMutableModel)new Model()).AddEntityType(typeof(SomeEntity));
- entityType.AddProperty(SomeEntity.IdProperty);
- entityType.AddProperty("IdShadow", typeof(int));
- entityType.AddProperty(SomeEntity.FooProperty);
- entityType.AddProperty("FooShadow", typeof(string));
- entityType.AddProperty(SomeEntity.GooProperty);
- entityType.AddProperty("GooShadow", typeof(Guid));
- ((Model)entityType.Model).FinalizeModel();
+ var modelBuilder = CreateConventionalModelBuilder();
+
+ modelBuilder.Entity(eb =>
+ {
+ eb.UsePropertyAccessMode(PropertyAccessMode.Property);
+
+ eb.Ignore(e => e.Enum);
+ eb.Ignore(e => e.MaybeEnum);
+
+ eb.Property("IdShadow");
+ eb.Property("FooShadow");
+ eb.Property("GooShadow");
+ });
+
+ var entityType = modelBuilder.FinalizeModel().FindEntityType(typeof(SomeEntity));
var factory = GetMaterializer(new EntityMaterializerSource(new EntityMaterializerSourceDependencies()), entityType);
var gu = Guid.NewGuid();
var entity = (SomeEntity)factory(
new MaterializationContext(
- new ValueBuffer(new object[] { "Fu", "FuS", gu, Guid.NewGuid(), 77, 777 }),
+ new ValueBuffer(new object[] { 77, "Fu", "FuS", gu, Guid.NewGuid(), 777 }),
_fakeContext));
Assert.Equal(77, entity.Id);
@@ -295,15 +309,19 @@ public void Can_create_materializer_for_entity_ignoring_shadow_fields()
[ConditionalFact]
public void Throws_if_parameterless_constructor_is_not_defined_on_entity_type()
{
- var entityType = ((IMutableModel)new Model()).AddEntityType(typeof(EntityWithoutParameterlessConstructor));
- entityType.AddProperty(EntityWithoutParameterlessConstructor.IdProperty);
+ var modelBuilder = CreateConventionalModelBuilder();
+ modelBuilder.Entity();
Assert.Equal(
- CoreStrings.NoParameterlessConstructor(typeof(EntityWithoutParameterlessConstructor).Name),
+ CoreStrings.ConstructorNotFound(
+ typeof(EntityWithoutParameterlessConstructor).Name, "cannot bind 'value' in 'EntityWithoutParameterlessConstructor(int value)'"),
Assert.Throws(
- () => GetMaterializer(new EntityMaterializerSource(new EntityMaterializerSourceDependencies()), entityType)).Message);
+ () => modelBuilder.FinalizeModel()).Message);
}
+ protected virtual ModelBuilder CreateConventionalModelBuilder(bool sensitiveDataLoggingEnabled = false)
+ => InMemoryTestHelpers.Instance.CreateConventionBuilder();
+
private static readonly ParameterExpression _contextParameter
= Expression.Parameter(typeof(MaterializationContext), "materializationContext");
@@ -345,9 +363,16 @@ public static SomeEntity GeneralFactory(object[] constructorArguments)
return Factory((int)constructorArguments[0], (Guid?)constructorArguments[1]);
}
+ [NotMapped]
public bool FactoryUsed { get; set; }
+
+ [NotMapped]
public bool ParameterizedConstructorUsed { get; }
+
+ [NotMapped]
public bool IdSetterCalled { get; set; }
+
+ [NotMapped]
public bool GooSetterCalled { get; set; }
public static readonly PropertyInfo IdProperty = typeof(SomeEntity).GetProperty("Id");
diff --git a/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs b/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs
index cff0cacc81e..ffabd212993 100644
--- a/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs
+++ b/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.cs
@@ -47,6 +47,8 @@ private class FakeEntityType : Annotatable, IReadOnlyEntityType
public string DefiningNavigationName { get; }
public IReadOnlyEntityType DefiningEntityType { get; }
public LambdaExpression QueryFilter { get; }
+ public InstantiationBinding ConstructorBinding { get; }
+ public InstantiationBinding ServiceOnlyConstructorBinding { get; }
public IReadOnlyKey FindPrimaryKey()
=> throw new NotImplementedException();