diff --git a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
index 6b2da830fbc..690e39d60a9 100644
--- a/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
+++ b/src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs
@@ -694,7 +694,7 @@ private void Create(
property.DeclaringEntityType.ShortName(), property.Name, nameof(PropertyBuilder.HasConversion)));
}
- var valueConverterType = (Type?)property[CoreAnnotationNames.ValueConverterType];
+ var valueConverterType = GetValueConverterType(property);
if (valueConverterType == null
&& property.GetValueConverter() != null)
{
@@ -847,6 +847,45 @@ private void Create(
mainBuilder.AppendLine();
}
+ private static Type? GetValueConverterType(IProperty property)
+ {
+ var type = (Type?)property[CoreAnnotationNames.ValueConverterType];
+ if (type != null)
+ {
+ return type;
+ }
+
+ var principalProperty = property;
+ for (var i = 0; i < 10000; i++)
+ {
+ foreach (var foreignKey in principalProperty.GetContainingForeignKeys())
+ {
+ for (var propertyIndex = 0; propertyIndex < foreignKey.Properties.Count; propertyIndex++)
+ {
+ if (principalProperty == foreignKey.Properties[propertyIndex])
+ {
+ var newPrincipalProperty = foreignKey.PrincipalKey.Properties[propertyIndex];
+ if (property == principalProperty
+ || newPrincipalProperty == principalProperty)
+ {
+ break;
+ }
+
+ principalProperty = newPrincipalProperty;
+
+ type = (Type?)principalProperty[CoreAnnotationNames.ValueConverterType];
+ if (type != null)
+ {
+ return type;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
private void PropertyBaseParameters(
IPropertyBase property,
CSharpRuntimeAnnotationCodeGeneratorParameters parameters,
diff --git a/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs b/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs
index 91cdf62e3f9..7965038920d 100644
--- a/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs
+++ b/src/EFCore.Relational/Metadata/Internal/CheckConstraint.cs
@@ -241,7 +241,8 @@ public virtual InternalCheckConstraintBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && ((IConventionAnnotatable)EntityType).IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/EntityTypeMappingFragment.cs b/src/EFCore.Relational/Metadata/Internal/EntityTypeMappingFragment.cs
index e0b475797fe..437ebffadbf 100644
--- a/src/EFCore.Relational/Metadata/Internal/EntityTypeMappingFragment.cs
+++ b/src/EFCore.Relational/Metadata/Internal/EntityTypeMappingFragment.cs
@@ -57,7 +57,8 @@ public virtual InternalEntityTypeMappingFragmentBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && ((IConventionAnnotatable)EntityType).IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
index 699011c6517..45b56c7cf8f 100644
--- a/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
+++ b/src/EFCore.Relational/Metadata/Internal/RelationalPropertyOverrides.cs
@@ -83,7 +83,8 @@ public virtual InternalRelationalPropertyOverridesBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && ((IConventionAnnotatable)Property).IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs
index 92d5ad08f1d..00f38a9abab 100644
--- a/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureParameter.cs
@@ -68,7 +68,8 @@ public virtual InternalStoredProcedureParameterBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && StoredProcedure.IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumn.cs b/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumn.cs
index 044c093c834..bd633a0fb41 100644
--- a/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumn.cs
+++ b/src/EFCore.Relational/Metadata/Internal/StoredProcedureResultColumn.cs
@@ -56,7 +56,8 @@ public virtual InternalStoredProcedureResultColumnBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && StoredProcedure.IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs
index 7abcb5f82e9..52df30d463c 100644
--- a/src/EFCore/Infrastructure/ModelValidator.cs
+++ b/src/EFCore/Infrastructure/ModelValidator.cs
@@ -864,6 +864,7 @@ protected virtual void ValidateTypeMappings(
var type = converter.ModelClrType;
if (type != typeof(string)
&& !(type == typeof(byte[]) && property.IsKey()) // Already special-cased elsewhere
+ && !property.IsForeignKey()
&& type.TryGetSequenceType() != null)
{
logger.CollectionWithoutComparer(property);
diff --git a/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs
index 068cf6e37e6..cbb088a4e62 100644
--- a/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs
+++ b/src/EFCore/Metadata/Conventions/KeyDiscoveryConvention.cs
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using Microsoft.EntityFrameworkCore.Metadata.Builders;
-
namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;
///
diff --git a/src/EFCore/Metadata/Conventions/ModelCleanupConvention.cs b/src/EFCore/Metadata/Conventions/ModelCleanupConvention.cs
index eeff662fc76..141fbe653f4 100644
--- a/src/EFCore/Metadata/Conventions/ModelCleanupConvention.cs
+++ b/src/EFCore/Metadata/Conventions/ModelCleanupConvention.cs
@@ -9,7 +9,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions;
///
/// See Model building conventions for more information and examples.
///
-public class ModelCleanupConvention : IModelFinalizingConvention
+public class ModelCleanupConvention :
+ IForeignKeyRemovedConvention,
+ IModelFinalizingConvention
{
///
/// Creates a new instance of .
@@ -25,6 +27,21 @@ public ModelCleanupConvention(ProviderConventionSetBuilderDependencies dependenc
///
protected virtual ProviderConventionSetBuilderDependencies Dependencies { get; }
+ ///
+ public virtual void ProcessForeignKeyRemoved(
+ IConventionEntityTypeBuilder entityTypeBuilder,
+ IConventionForeignKey foreignKey,
+ IConventionContext context)
+ {
+ var principalKey = foreignKey.PrincipalKey;
+ if (principalKey.IsInModel
+ && !principalKey.IsPrimaryKey()
+ && !principalKey.GetReferencingForeignKeys().Any())
+ {
+ principalKey.DeclaringEntityType.Builder.HasNoKey(principalKey);
+ }
+ }
+
///
public virtual void ProcessModelFinalizing(
IConventionModelBuilder modelBuilder,
diff --git a/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs
index c2d8ef1b5a1..0fc7dd606d9 100644
--- a/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs
+++ b/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs
@@ -809,7 +809,10 @@ private void DiscoverUnidirectionalInverses(
{
foreach (var inverseCandidateEntityType in model.FindEntityTypes(inverseCandidateType).ToList())
{
- DiscoverRelationships(inverseCandidateEntityType.Builder, context);
+ if (inverseCandidateEntityType.IsInModel)
+ {
+ DiscoverRelationships(inverseCandidateEntityType.Builder, context);
+ }
}
}
}
diff --git a/src/EFCore/Metadata/Internal/ForeignKey.cs b/src/EFCore/Metadata/Internal/ForeignKey.cs
index 71e83cc0cf0..166bec83544 100644
--- a/src/EFCore/Metadata/Internal/ForeignKey.cs
+++ b/src/EFCore/Metadata/Internal/ForeignKey.cs
@@ -118,7 +118,8 @@ public virtual InternalForeignKeyBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && DeclaringEntityType.IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/Metadata/Internal/Index.cs b/src/EFCore/Metadata/Internal/Index.cs
index 784c249506a..966ba49aead 100644
--- a/src/EFCore/Metadata/Internal/Index.cs
+++ b/src/EFCore/Metadata/Internal/Index.cs
@@ -103,7 +103,8 @@ public virtual InternalIndexBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && DeclaringEntityType.IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
index 3f74eb23be1..0a15148525d 100644
--- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs
@@ -102,7 +102,8 @@ public InternalEntityTypeBuilder(EntityType metadata, InternalModelBuilder model
var newKey = Metadata.SetPrimaryKey(keyBuilder.Metadata.Properties, configurationSource);
foreach (var key in Metadata.GetDeclaredKeys().ToList())
{
- if (key == keyBuilder.Metadata)
+ if (key == keyBuilder.Metadata
+ || !key.IsInModel)
{
continue;
}
@@ -3956,6 +3957,12 @@ private static bool Contains(IReadOnlyForeignKey? inheritedFk, IReadOnlyForeignK
}
else
{
+ if (targetEntityTypeBuilder != null
+ && targetEntityTypeBuilder.Metadata.GetConfigurationSource().OverridesStrictly(configurationSource))
+ {
+ return targetEntityTypeBuilder;
+ }
+
targetEntityTypeBuilder = targetEntityType.IsNamed
? ModelBuilder.SharedTypeEntity(targetTypeName, targetType, configurationSource.Value, targetShouldBeOwned)
: ModelBuilder.Entity(targetType, configurationSource.Value, targetShouldBeOwned);
@@ -4490,81 +4497,77 @@ public virtual bool ShouldReuniquifyTemporaryProperties(ForeignKey foreignKey)
var clrProperties = Metadata.GetRuntimeProperties();
var clrFields = Metadata.GetRuntimeFields();
var canReuniquify = false;
- using (var principalPropertyNamesEnumerator = principalPropertyNames.GetEnumerator())
- {
- using var principalPropertyTypesEnumerator = principalPropertyTypes.GetEnumerator();
- for (var i = 0;
- i < propertyCount
- && principalPropertyNamesEnumerator.MoveNext()
- && principalPropertyTypesEnumerator.MoveNext();
- i++)
- {
- var keyPropertyName = principalPropertyNamesEnumerator.Current;
- var keyPropertyType = principalPropertyTypesEnumerator.Current;
- var keyModifiedBaseName = keyPropertyName.StartsWith(baseName, StringComparison.OrdinalIgnoreCase)
- ? keyPropertyName
- : baseName + keyPropertyName;
- string propertyName;
- var clrType = keyPropertyType.MakeNullable(!isRequired);
- var index = -1;
- while (true)
+ using var principalPropertyNamesEnumerator = principalPropertyNames.GetEnumerator();
+ using var principalPropertyTypesEnumerator = principalPropertyTypes.GetEnumerator();
+ for (var i = 0;
+ i < propertyCount
+ && principalPropertyNamesEnumerator.MoveNext()
+ && principalPropertyTypesEnumerator.MoveNext();
+ i++)
+ {
+ var keyPropertyName = principalPropertyNamesEnumerator.Current;
+ var keyPropertyType = principalPropertyTypesEnumerator.Current;
+
+ var keyModifiedBaseName = keyPropertyName.StartsWith(baseName, StringComparison.OrdinalIgnoreCase)
+ ? keyPropertyName
+ : baseName + keyPropertyName;
+ string propertyName;
+ var clrType = keyPropertyType.MakeNullable(!isRequired);
+ var index = -1;
+ while (true)
+ {
+ propertyName = keyModifiedBaseName + (++index > 0 ? index.ToString(CultureInfo.InvariantCulture) : "");
+ if (!Metadata.FindPropertiesInHierarchy(propertyName).Any()
+ && !clrProperties.ContainsKey(propertyName)
+ && !clrFields.ContainsKey(propertyName)
+ && !IsIgnored(propertyName, ConfigurationSource.Convention))
{
- propertyName = keyModifiedBaseName + (++index > 0 ? index.ToString(CultureInfo.InvariantCulture) : "");
- if (!Metadata.FindPropertiesInHierarchy(propertyName).Any()
- && !clrProperties.ContainsKey(propertyName)
- && !clrFields.ContainsKey(propertyName)
- && !IsIgnored(propertyName, ConfigurationSource.Convention))
+ if (currentProperties == null)
{
- if (currentProperties == null)
- {
- var propertyBuilder = Property(
- clrType, propertyName, typeConfigurationSource: null,
- configurationSource: ConfigurationSource.Convention);
-
- if (propertyBuilder == null)
- {
- return (false, null);
- }
-
- if (index > 0)
- {
- propertyBuilder.HasAnnotation(
- CoreAnnotationNames.PreUniquificationName,
- keyModifiedBaseName,
- ConfigurationSource.Convention);
- }
-
- if (clrType.IsNullableType())
- {
- propertyBuilder.IsRequired(isRequired, ConfigurationSource.Convention);
- }
+ var propertyBuilder = Property(
+ clrType, propertyName, typeConfigurationSource: null,
+ configurationSource: ConfigurationSource.Convention);
- newProperties![i] = propertyBuilder.Metadata;
+ if (propertyBuilder == null)
+ {
+ return (false, null);
}
- else if (!Metadata.Model.Builder.CanBeConfigured(
- clrType, TypeConfigurationType.Property, ConfigurationSource.Convention))
+
+ if (index > 0)
{
+ propertyBuilder.HasAnnotation(
+ CoreAnnotationNames.PreUniquificationName,
+ keyModifiedBaseName,
+ ConfigurationSource.Convention);
}
- else
+
+ if (clrType.IsNullableType())
{
- canReuniquify = true;
+ propertyBuilder.IsRequired(isRequired, ConfigurationSource.Convention);
}
- break;
+ newProperties![i] = propertyBuilder.Metadata;
}
-
- var currentProperty = currentProperties?.SingleOrDefault(p => p.Name == propertyName);
- if (currentProperty != null)
+ else if (Metadata.Model.Builder.CanBeConfigured(
+ clrType, TypeConfigurationType.Property, ConfigurationSource.Convention))
{
- if (((IConventionProperty)currentProperty).IsImplicitlyCreated()
- && currentProperty.ClrType != clrType
- && isRequired)
- {
- canReuniquify = true;
- }
+ canReuniquify = true;
+ }
+
+ break;
+ }
- break;
+ var currentProperty = currentProperties?.SingleOrDefault(p => p.Name == propertyName);
+ if (currentProperty != null)
+ {
+ if (((IConventionProperty)currentProperty).IsImplicitlyCreated()
+ && currentProperty.ClrType != clrType
+ && isRequired)
+ {
+ canReuniquify = true;
}
+
+ break;
}
}
}
diff --git a/src/EFCore/Metadata/Internal/InternalModelBuilder.cs b/src/EFCore/Metadata/Internal/InternalModelBuilder.cs
index f2fa398780a..7e6ccce0557 100644
--- a/src/EFCore/Metadata/Internal/InternalModelBuilder.cs
+++ b/src/EFCore/Metadata/Internal/InternalModelBuilder.cs
@@ -315,6 +315,14 @@ public override InternalModelBuilder ModelBuilder
return null;
}
+ foreach (var existingEntityType in Metadata.FindEntityTypes(type))
+ {
+ if (!existingEntityType.Builder.CanSetIsOwned(true, configurationSource))
+ {
+ return null;
+ }
+ }
+
Metadata.RemoveIgnored(type);
Metadata.AddOwned(type, ConfigurationSource.Explicit);
@@ -337,15 +345,8 @@ public override InternalModelBuilder ModelBuilder
}
else
{
- if (entityType.Builder.CanSetIsOwned(true, configurationSource))
- {
- // Discover the ownership when the type is added back
- HasNoEntityType(entityType, configurationSource);
- }
- else
- {
- return null;
- }
+ // Discover the ownership when the type is added back
+ HasNoEntityType(entityType, configurationSource);
}
}
@@ -405,9 +406,8 @@ public virtual bool CanBeConfigured(Type type, TypeConfigurationType configurati
}
if (!configurationType.IsEntityType()
- && (!configurationSource.Overrides(Metadata.FindEntityType(type)?.GetConfigurationSource())
- || !configurationSource.Overrides(Metadata.FindIsOwnedConfigurationSource(type))
- || Metadata.IsShared(type)))
+ && (!configurationSource.Overrides(Metadata.FindIsOwnedConfigurationSource(type))
+ || Metadata.FindEntityTypes(type).Any(e => !configurationSource.Overrides(e.GetConfigurationSource()))))
{
return false;
}
diff --git a/src/EFCore/Metadata/Internal/Key.cs b/src/EFCore/Metadata/Internal/Key.cs
index 98b4ef6c540..e2c429977de 100644
--- a/src/EFCore/Metadata/Internal/Key.cs
+++ b/src/EFCore/Metadata/Internal/Key.cs
@@ -76,7 +76,8 @@ public virtual InternalKeyBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && DeclaringEntityType.IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/Metadata/Internal/Navigation.cs b/src/EFCore/Metadata/Internal/Navigation.cs
index f87c1b7e809..d908799692b 100644
--- a/src/EFCore/Metadata/Internal/Navigation.cs
+++ b/src/EFCore/Metadata/Internal/Navigation.cs
@@ -76,7 +76,8 @@ public virtual InternalNavigationBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && ForeignKey.IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs
index 4a9b6abcb3a..205bb52c847 100644
--- a/src/EFCore/Metadata/Internal/Property.cs
+++ b/src/EFCore/Metadata/Internal/Property.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Internal;
@@ -97,7 +98,8 @@ public virtual InternalPropertyBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && DeclaringEntityType.IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -657,7 +659,43 @@ public virtual PropertySaveBehavior GetAfterSaveBehavior()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual ValueConverter? GetValueConverter()
- => (ValueConverter?)this[CoreAnnotationNames.ValueConverter];
+ {
+ var converter = (ValueConverter?)this[CoreAnnotationNames.ValueConverter];
+ if (converter != null)
+ {
+ return converter;
+ }
+
+ var property = this;
+ for (var i = 0; i < 10000; i++)
+ {
+ foreach (var foreignKey in property.GetContainingForeignKeys())
+ {
+ for (var propertyIndex = 0; propertyIndex < foreignKey.Properties.Count; propertyIndex++)
+ {
+ if (property == foreignKey.Properties[propertyIndex])
+ {
+ var principalProperty = foreignKey.PrincipalKey.Properties[propertyIndex];
+ if (principalProperty == this
+ || principalProperty == property)
+ {
+ break;
+ }
+
+ property = principalProperty;
+
+ converter = (ValueConverter?)property[CoreAnnotationNames.ValueConverter];
+ if (converter != null)
+ {
+ return converter;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -700,7 +738,43 @@ public virtual PropertySaveBehavior GetAfterSaveBehavior()
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual Type? GetProviderClrType()
- => (Type?)this[CoreAnnotationNames.ProviderClrType];
+ {
+ var type = (Type?)this[CoreAnnotationNames.ProviderClrType];
+ if (type != null)
+ {
+ return type;
+ }
+
+ var property = this;
+ for (var i = 0; i < 10000; i++)
+ {
+ foreach (var foreignKey in property.GetContainingForeignKeys())
+ {
+ for (var propertyIndex = 0; propertyIndex < foreignKey.Properties.Count; propertyIndex++)
+ {
+ if (property == foreignKey.Properties[propertyIndex])
+ {
+ var principalProperty = foreignKey.PrincipalKey.Properties[propertyIndex];
+ if (principalProperty == this
+ || principalProperty == property)
+ {
+ break;
+ }
+
+ property = principalProperty;
+
+ type = (Type?)property[CoreAnnotationNames.ProviderClrType];
+ if (type != null)
+ {
+ return type;
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/Metadata/Internal/ServiceProperty.cs b/src/EFCore/Metadata/Internal/ServiceProperty.cs
index fbf58a01767..deb8ea20363 100644
--- a/src/EFCore/Metadata/Internal/ServiceProperty.cs
+++ b/src/EFCore/Metadata/Internal/ServiceProperty.cs
@@ -85,7 +85,8 @@ public virtual InternalServicePropertyBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && DeclaringEntityType.IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/Metadata/Internal/SkipNavigation.cs b/src/EFCore/Metadata/Internal/SkipNavigation.cs
index f1849bdeab8..1c3fb7136dc 100644
--- a/src/EFCore/Metadata/Internal/SkipNavigation.cs
+++ b/src/EFCore/Metadata/Internal/SkipNavigation.cs
@@ -91,7 +91,8 @@ public virtual InternalSkipNavigationBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && DeclaringEntityType.IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/src/EFCore/Metadata/Internal/Trigger.cs b/src/EFCore/Metadata/Internal/Trigger.cs
index 855c4868294..b70f749a1ca 100644
--- a/src/EFCore/Metadata/Internal/Trigger.cs
+++ b/src/EFCore/Metadata/Internal/Trigger.cs
@@ -52,7 +52,8 @@ public virtual InternalTriggerBuilder Builder
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual bool IsInModel
- => _builder is not null;
+ => _builder is not null
+ && EntityType.IsInModel;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
diff --git a/test/EFCore.Cosmos.Tests/ModelBuilding/CosmosModelBuilderGenericTest.cs b/test/EFCore.Cosmos.Tests/ModelBuilding/CosmosModelBuilderGenericTest.cs
index c486739e8cc..3cabc98b4f3 100644
--- a/test/EFCore.Cosmos.Tests/ModelBuilding/CosmosModelBuilderGenericTest.cs
+++ b/test/EFCore.Cosmos.Tests/ModelBuilding/CosmosModelBuilderGenericTest.cs
@@ -338,20 +338,6 @@ public override void Navigation_to_shared_type_is_not_discovered_by_convention()
owned.DisplayName());
}
- [ConditionalFact]
- public virtual void Inverse_discovered_after_entity_becomes_non_owned()
- {
- var modelBuilder = CreateModelBuilder();
-
- modelBuilder.Entity();
- modelBuilder.Entity();
-
- var model = modelBuilder.FinalizeModel();
-
- var queryResult = model.FindEntityType(typeof(QueryResult));
- Assert.NotNull(queryResult.FindNavigation(nameof(QueryResult.Value)));
- }
-
protected override TestModelBuilder CreateModelBuilder(Action configure = null)
=> CreateTestModelBuilder(CosmosTestHelpers.Instance, configure);
}
diff --git a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
index 77a132a3415..88ee0c29aa6 100644
--- a/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
+++ b/test/EFCore.Tests/Infrastructure/ModelValidatorTest.cs
@@ -995,6 +995,7 @@ public virtual void Detects_owned_entity_type_without_ownership()
{
var builder = CreateConventionlessModelBuilder();
var modelBuilder = (InternalModelBuilder)builder.GetInfrastructure();
+ modelBuilder.Owned(typeof(A), ConfigurationSource.Convention);
var aBuilder = modelBuilder.Entity(typeof(A), ConfigurationSource.Convention);
aBuilder.Ignore(nameof(A.Id), ConfigurationSource.Explicit);
aBuilder.Ignore(nameof(A.P0), ConfigurationSource.Explicit);
@@ -1002,8 +1003,6 @@ public virtual void Detects_owned_entity_type_without_ownership()
aBuilder.Ignore(nameof(A.P2), ConfigurationSource.Explicit);
aBuilder.Ignore(nameof(A.P3), ConfigurationSource.Explicit);
- modelBuilder.Owned(typeof(A), ConfigurationSource.Convention);
-
VerifyError(CoreStrings.OwnerlessOwnedType(nameof(A)), builder);
}
diff --git a/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs b/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs
index efc8c270fc6..8ee3d93cd9d 100644
--- a/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs
+++ b/test/EFCore.Tests/ModelBuilding/OneToOneTestBase.cs
@@ -4112,14 +4112,18 @@ public virtual void Inverse_discovered_after_entity_unignored()
{
var modelBuilder = CreateModelBuilder();
+ modelBuilder.Ignore();
modelBuilder.Ignore();
- modelBuilder.Entity();
+ modelBuilder.Entity()
+ .Property(x => x.Id)
+ .HasConversion(x => x.Id, x => new CustomId { Id = x });
modelBuilder.Entity();
var model = modelBuilder.FinalizeModel();
var queryResult = model.FindEntityType(typeof(QueryResult));
Assert.NotNull(queryResult.FindNavigation(nameof(QueryResult.Value)));
+ Assert.Null(queryResult.FindProperty("TempId"));
}
}
}
diff --git a/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs b/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs
index fc1e2fea865..c4b5e2d14ac 100644
--- a/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs
+++ b/test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs
@@ -887,13 +887,18 @@ public virtual void Can_configure_fk_on_multiple_ownerships()
lb.WithOwner()
.HasForeignKey("BookLabelId")
.HasAnnotation("Foo", "Bar");
+ lb.Ignore(l => l.Book);
});
- modelBuilder.Entity()
- .OwnsOne(b => b.AlternateLabel)
- .WithOwner()
- .HasForeignKey("BookLabelId");
+ modelBuilder.Entity().OwnsOne(
+ b => b.AlternateLabel, lb =>
+ {
+ lb.WithOwner()
+ .HasForeignKey("BookLabelId");
- IReadOnlyModel model = modelBuilder.Model;
+ lb.Ignore(l => l.Book);
+ });
+
+ var model = modelBuilder.FinalizeModel();
var bookOwnership1 = model.FindEntityType(typeof(Book)).FindNavigation(nameof(Book.Label)).ForeignKey;
var bookOwnership2 = model.FindEntityType(typeof(Book)).FindNavigation(nameof(Book.AlternateLabel)).ForeignKey;
@@ -904,11 +909,6 @@ public virtual void Can_configure_fk_on_multiple_ownerships()
Assert.Equal(2, model.GetEntityTypes().Count(e => e.ClrType == typeof(BookLabel)));
Assert.Equal(3, model.GetEntityTypes().Count());
-
- modelBuilder.Entity().OwnsOne(b => b.Label).Ignore(l => l.Book);
- modelBuilder.Entity().OwnsOne(b => b.AlternateLabel).Ignore(l => l.Book);
-
- modelBuilder.FinalizeModel();
}
[ConditionalFact]
@@ -1039,6 +1039,45 @@ public virtual void Can_map_derived_of_owned_type_first()
modelBuilder.FinalizeModel();
}
+ [ConditionalFact]
+ public virtual void Can_configure_relationship_with_PK_ValueConverter()
+ {
+ var modelBuilder = CreateModelBuilder();
+
+ modelBuilder.Entity().Property(x => x.Id)
+ .HasConversion(x => x.Id, x => new CustomId { Id = x });
+
+ modelBuilder.Entity()
+ .Property(x => x.Id)
+ .HasConversion(x => x.Id, x => new CustomId { Id = x });
+
+ modelBuilder.Entity()
+ .OwnsOne(q => q.Value)
+ .Property(x => x.CategoryId)
+ .HasConversion(x => x.Id, x => new CustomId { Id = x });
+
+ var model = modelBuilder.FinalizeModel();
+
+ var result = model.FindEntityType(typeof(QueryResult));
+ Assert.Null(result.FindProperty("TempId"));
+
+ var owned = result.GetDeclaredNavigations().Single().TargetEntityType;
+ Assert.Null(owned.FindProperty("TempId"));
+
+ var ownedPkProperty = owned.FindPrimaryKey().Properties.Single();
+ Assert.NotNull(ownedPkProperty.GetValueConverter());
+
+ var category = model.FindEntityType(typeof(ValueCategory));
+ Assert.Null(category.FindProperty("TempId"));
+
+ var barNavigation = owned.GetDeclaredNavigations().Single(n => !n.ForeignKey.IsOwnership);
+ Assert.Same(category, barNavigation.TargetEntityType);
+ var fkProperty = barNavigation.ForeignKey.Properties.Single();
+ Assert.Equal("CategoryId", fkProperty.Name);
+
+ Assert.Equal(3, model.GetEntityTypes().Count());
+ }
+
[ConditionalFact]
public virtual void Throws_on_FK_matching_two_relationships()
{
diff --git a/test/EFCore.Tests/ModelBuilding/TestModel.cs b/test/EFCore.Tests/ModelBuilding/TestModel.cs
index 6432e47f3c8..e73dbc5c7f1 100644
--- a/test/EFCore.Tests/ModelBuilding/TestModel.cs
+++ b/test/EFCore.Tests/ModelBuilding/TestModel.cs
@@ -750,15 +750,27 @@ public class KeylessEntityWithFields
protected class QueryResult
{
- public int Id { get; set; }
+ public CustomId Id { get; set; } = null!;
public int ValueFk { get; set; }
public Value Value { get; set; } = null!;
}
+ [Owned]
protected class Value
{
public int Id { get; set; }
- public int AlternateId { get; set; }
+ public CustomId? CategoryId { get; set; }
+ public ValueCategory? Category { get; set; }
+ }
+
+ protected class CustomId
+ {
+ public int Id { get; set; }
+ }
+
+ protected class ValueCategory
+ {
+ public CustomId Id { get; set; } = null!;
}
protected class KeylessEntity