From 77b6197440b712b5b8afd0c3e3f23fa269bbfc20 Mon Sep 17 00:00:00 2001 From: Shay Rojansky Date: Tue, 17 Nov 2020 16:06:26 +0200 Subject: [PATCH] MemberNotNullWhen annotations in metadata * Turn HasDefiningNavigation from extension to DIM * Turn HasClrType into a DIM property --- .../Internal/InternalEntityEntryFactory.cs | 4 +-- .../ChangeTracking/Internal/StateManager.cs | 2 +- src/EFCore/Extensions/EntityTypeExtensions.cs | 13 ++++---- src/EFCore/Infrastructure/ModelValidator.cs | 6 ++-- .../DerivedTypeDiscoveryConvention.cs | 2 +- .../ForeignKeyAttributeConvention.cs | 2 +- .../NavigationAttributeConventionBase.cs | 6 ++-- .../NonNullableNavigationConvention.cs | 2 +- .../NotMappedMemberAttributeConvention.cs | 2 +- .../OwnedEntityTypeAttributeConvention.cs | 2 +- .../PropertyDiscoveryConvention.cs | 2 +- .../RelationshipDiscoveryConvention.cs | 4 +-- .../ServicePropertyDiscoveryConvention.cs | 2 +- src/EFCore/Metadata/IEntityType.cs | 17 ++++++++++ src/EFCore/Metadata/ITypeBase.cs | 9 ++++++ src/EFCore/Metadata/Internal/EntityType.cs | 29 +++++++++++++---- .../Internal/InternalForeignKeyBuilder.cs | 8 ++--- .../Metadata/Internal/InternalModelBuilder.cs | 2 +- src/EFCore/Metadata/Internal/Model.cs | 13 +++----- src/EFCore/Metadata/Internal/Property.cs | 2 +- src/EFCore/Metadata/Internal/TypeBase.cs | 32 ++++++++++++++++--- .../Metadata/Internal/TypeBaseExtensions.cs | 10 ------ .../Internal/EntityMaterializerSource.cs | 2 +- 23 files changed, 111 insertions(+), 62 deletions(-) diff --git a/src/EFCore/ChangeTracking/Internal/InternalEntityEntryFactory.cs b/src/EFCore/ChangeTracking/Internal/InternalEntityEntryFactory.cs index 8a61aa9c1d5..6c65cbc1ce8 100644 --- a/src/EFCore/ChangeTracking/Internal/InternalEntityEntryFactory.cs +++ b/src/EFCore/ChangeTracking/Internal/InternalEntityEntryFactory.cs @@ -48,7 +48,7 @@ public virtual InternalEntityEntry Create( private static InternalEntityEntry NewInternalEntityEntry(IStateManager stateManager, IEntityType entityType, object entity) { - if (!entityType.HasClrType()) + if (!entityType.HasClrType) { return new InternalShadowEntityEntry(stateManager, entityType); } @@ -66,7 +66,7 @@ private static InternalEntityEntry NewInternalEntityEntry( object entity, in ValueBuffer valueBuffer) { - return !entityType.HasClrType() + return !entityType.HasClrType ? new InternalShadowEntityEntry(stateManager, entityType, valueBuffer) : entityType.ShadowPropertyCount() > 0 ? (InternalEntityEntry)new InternalMixedEntityEntry(stateManager, entityType, entity, valueBuffer) diff --git a/src/EFCore/ChangeTracking/Internal/StateManager.cs b/src/EFCore/ChangeTracking/Internal/StateManager.cs index faffa5c53cc..f68f0c344fb 100644 --- a/src/EFCore/ChangeTracking/Internal/StateManager.cs +++ b/src/EFCore/ChangeTracking/Internal/StateManager.cs @@ -306,7 +306,7 @@ public virtual InternalEntityEntry CreateEntry(IDictionary value } var valueBuffer = new ValueBuffer(valuesArray); - var entity = entityType.HasClrType() + var entity = entityType.HasClrType ? EntityMaterializerSource.GetMaterializer(entityType)(new MaterializationContext(valueBuffer, Context)) : null; diff --git a/src/EFCore/Extensions/EntityTypeExtensions.cs b/src/EFCore/Extensions/EntityTypeExtensions.cs index d42159a5e1a..e232ed1e846 100644 --- a/src/EFCore/Extensions/EntityTypeExtensions.cs +++ b/src/EFCore/Extensions/EntityTypeExtensions.cs @@ -357,14 +357,14 @@ public static string FullName([NotNull] this ITypeBase type) var root = entityType; while (true) { - var definingNavigationName = root.DefiningNavigationName; - if (definingNavigationName == null) + if (!root.HasDefiningNavigation()) { break; } - // TODO-NULLABLE: Put MemberNotNull on DefiningNavigationName (or check HasDefiningNavigation) when we target net5.0 - root = root.DefiningEntityType!; + var definingNavigationName = root.DefiningNavigationName; + + root = root.DefiningEntityType; path.Push("#"); path.Push(definingNavigationName); path.Push("."); @@ -410,7 +410,7 @@ public static string ShortName([NotNull] this ITypeBase type) /// if this entity type has a defining navigation. [DebuggerStepThrough] public static bool HasDefiningNavigation([NotNull] this IEntityType entityType) - => entityType.DefiningEntityType != null; + => entityType.HasDefiningNavigation(); /// /// Gets a value indicating whether this entity type is owned by another entity type. @@ -585,8 +585,7 @@ public static IEnumerable GetDeclaredReferencingForeignKeys([NotNul return null; } - // TODO-NULLABLE: Put two MemberNotNulls on HasDefiningNavigation when we target net5.0 - var definingNavigation = entityType.DefiningEntityType!.FindNavigation(entityType.DefiningNavigationName!); + var definingNavigation = entityType.DefiningEntityType.FindNavigation(entityType.DefiningNavigationName); return definingNavigation?.TargetEntityType == entityType ? definingNavigation : null; } diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs index 283b7ac400e..200118662c9 100644 --- a/src/EFCore/Infrastructure/ModelValidator.cs +++ b/src/EFCore/Infrastructure/ModelValidator.cs @@ -163,7 +163,7 @@ protected virtual void ValidatePropertyMapping( entityType.DisplayName(), unmappedProperty.Name, unmappedProperty.ClrType.ShortDisplayName())); } - if (!entityType.HasClrType()) + if (!entityType.HasClrType) { continue; } @@ -375,7 +375,7 @@ protected virtual void ValidateNoShadowEntities( { Check.NotNull(model, nameof(model)); - var firstShadowEntity = model.GetEntityTypes().FirstOrDefault(entityType => !entityType.HasClrType()); + var firstShadowEntity = model.GetEntityTypes().FirstOrDefault(entityType => !entityType.HasClrType); if (firstShadowEntity != null) { throw new InvalidOperationException( @@ -745,7 +745,7 @@ protected virtual void ValidateOwnership( ownership.PrincipalEntityType.DisplayName())); } } - else if (entityType.HasClrType() + else if (entityType.HasClrType && ((IMutableModel)model).IsOwned(entityType.ClrType)) { throw new InvalidOperationException(CoreStrings.OwnerlessOwnedType(entityType.DisplayName())); diff --git a/src/EFCore/Metadata/Conventions/DerivedTypeDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/DerivedTypeDiscoveryConvention.cs index e2337f4b088..a21145b1d76 100644 --- a/src/EFCore/Metadata/Conventions/DerivedTypeDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/DerivedTypeDiscoveryConvention.cs @@ -49,7 +49,7 @@ public virtual void ProcessEntityTypeAdded( foreach (var directlyDerivedType in model.GetEntityTypes()) { if (directlyDerivedType != entityType - && directlyDerivedType.HasClrType() + && directlyDerivedType.HasClrType && !directlyDerivedType.HasSharedClrType && !directlyDerivedType.HasDefiningNavigation() && model.FindIsOwnedConfigurationSource(directlyDerivedType.ClrType) == null diff --git a/src/EFCore/Metadata/Conventions/ForeignKeyAttributeConvention.cs b/src/EFCore/Metadata/Conventions/ForeignKeyAttributeConvention.cs index b31f6e4c269..2bf7b1b8ca4 100644 --- a/src/EFCore/Metadata/Conventions/ForeignKeyAttributeConvention.cs +++ b/src/EFCore/Metadata/Conventions/ForeignKeyAttributeConvention.cs @@ -324,7 +324,7 @@ private static TAttribute GetAttribute(MemberInfo memberInfo) private MemberInfo FindForeignKeyAttributeOnProperty(IConventionEntityType entityType, string navigationName) { if (string.IsNullOrWhiteSpace(navigationName) - || !entityType.HasClrType()) + || !entityType.HasClrType) { return null; } diff --git a/src/EFCore/Metadata/Conventions/NavigationAttributeConventionBase.cs b/src/EFCore/Metadata/Conventions/NavigationAttributeConventionBase.cs index 24dd9e3c0f6..54a0e2a1df7 100644 --- a/src/EFCore/Metadata/Conventions/NavigationAttributeConventionBase.cs +++ b/src/EFCore/Metadata/Conventions/NavigationAttributeConventionBase.cs @@ -49,7 +49,7 @@ public virtual void ProcessEntityTypeAdded( IConventionContext context) { var entityType = entityTypeBuilder.Metadata; - if (!entityType.HasClrType()) + if (!entityType.HasClrType) { return; } @@ -162,7 +162,7 @@ public virtual void ProcessEntityTypeBaseTypeChanged( IConventionContext context) { var entityType = entityTypeBuilder.Metadata; - if (!entityType.HasClrType() + if (!entityType.HasClrType || entityTypeBuilder.Metadata.BaseType != newBaseType) { return; @@ -336,7 +336,7 @@ private static IEnumerable GetAttributes( [NotNull] MemberInfo memberInfo) where TCustomAttribute : Attribute { - if (!entityType.HasClrType() + if (!entityType.HasClrType || memberInfo == null) { return Enumerable.Empty(); diff --git a/src/EFCore/Metadata/Conventions/NonNullableNavigationConvention.cs b/src/EFCore/Metadata/Conventions/NonNullableNavigationConvention.cs index cb734a73449..d88bd16bca9 100644 --- a/src/EFCore/Metadata/Conventions/NonNullableNavigationConvention.cs +++ b/src/EFCore/Metadata/Conventions/NonNullableNavigationConvention.cs @@ -81,7 +81,7 @@ private void ProcessNavigation(IConventionNavigationBuilder navigationBuilder) } private bool IsNonNullable(IConventionModelBuilder modelBuilder, IConventionNavigation navigation) - => navigation.DeclaringEntityType.HasClrType() + => navigation.DeclaringEntityType.HasClrType && navigation.DeclaringEntityType.GetRuntimeProperties().Find(navigation.Name) is PropertyInfo propertyInfo && IsNonNullableReferenceType(modelBuilder, propertyInfo); } diff --git a/src/EFCore/Metadata/Conventions/NotMappedMemberAttributeConvention.cs b/src/EFCore/Metadata/Conventions/NotMappedMemberAttributeConvention.cs index 90985578a2c..2b51aef047d 100644 --- a/src/EFCore/Metadata/Conventions/NotMappedMemberAttributeConvention.cs +++ b/src/EFCore/Metadata/Conventions/NotMappedMemberAttributeConvention.cs @@ -44,7 +44,7 @@ public virtual void ProcessEntityTypeAdded( Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); var entityType = entityTypeBuilder.Metadata; - if (!entityType.HasClrType()) + if (!entityType.HasClrType) { return; } diff --git a/src/EFCore/Metadata/Conventions/OwnedEntityTypeAttributeConvention.cs b/src/EFCore/Metadata/Conventions/OwnedEntityTypeAttributeConvention.cs index b93ca18436f..724f7095973 100644 --- a/src/EFCore/Metadata/Conventions/OwnedEntityTypeAttributeConvention.cs +++ b/src/EFCore/Metadata/Conventions/OwnedEntityTypeAttributeConvention.cs @@ -33,7 +33,7 @@ protected override void ProcessEntityTypeAdded( OwnedAttribute attribute, IConventionContext context) { - if (entityTypeBuilder.Metadata.HasClrType()) + if (entityTypeBuilder.Metadata.HasClrType) { entityTypeBuilder.ModelBuilder.Owned(entityTypeBuilder.Metadata.ClrType, fromDataAnnotation: true); } diff --git a/src/EFCore/Metadata/Conventions/PropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/PropertyDiscoveryConvention.cs index 5d67ade51c2..a33642d9812 100644 --- a/src/EFCore/Metadata/Conventions/PropertyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/PropertyDiscoveryConvention.cs @@ -64,7 +64,7 @@ public virtual void ProcessEntityTypeBaseTypeChanged( private void Process(IConventionEntityTypeBuilder entityTypeBuilder) { var entityType = entityTypeBuilder.Metadata; - if (entityType.HasClrType()) + if (entityType.HasClrType) { foreach (var propertyInfo in entityType.GetRuntimeProperties().Values) { diff --git a/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs index 36aafa031be..1935365814b 100644 --- a/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/RelationshipDiscoveryConvention.cs @@ -45,7 +45,7 @@ public RelationshipDiscoveryConvention([NotNull] ProviderConventionSetBuilderDep private void DiscoverRelationships(IConventionEntityTypeBuilder entityTypeBuilder, IConventionContext context) { - if (!entityTypeBuilder.Metadata.HasClrType() + if (!entityTypeBuilder.Metadata.HasClrType || entityTypeBuilder.ModelBuilder.IsIgnored(entityTypeBuilder.Metadata.ClrType)) { return; @@ -986,7 +986,7 @@ private ImmutableSortedDictionary GetNavigationCandidates(IC } var dictionaryBuilder = ImmutableSortedDictionary.CreateBuilder(MemberInfoNameComparer.Instance); - if (entityType.HasClrType()) + if (entityType.HasClrType) { foreach (var propertyInfo in entityType.GetRuntimeProperties().Values.OrderBy(p => p.Name)) { diff --git a/src/EFCore/Metadata/Conventions/ServicePropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/ServicePropertyDiscoveryConvention.cs index 06d0b1b3eb7..b348b3b99cb 100644 --- a/src/EFCore/Metadata/Conventions/ServicePropertyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/ServicePropertyDiscoveryConvention.cs @@ -71,7 +71,7 @@ private void Process(IConventionEntityTypeBuilder entityTypeBuilder) { var entityType = entityTypeBuilder.Metadata; - if (!entityType.HasClrType()) + if (!entityType.HasClrType) { return; } diff --git a/src/EFCore/Metadata/IEntityType.cs b/src/EFCore/Metadata/IEntityType.cs index 41f31fe9b2a..6d702168d9b 100644 --- a/src/EFCore/Metadata/IEntityType.cs +++ b/src/EFCore/Metadata/IEntityType.cs @@ -6,6 +6,7 @@ using System.Reflection; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Utilities; +using CA = System.Diagnostics.CodeAnalysis; #nullable enable @@ -31,6 +32,22 @@ public interface IEntityType : ITypeBase /// IEntityType? DefiningEntityType { get; } + /// + /// Gets a value indicating whether this entity type has a defining navigation. + /// + /// if this entity type has a defining navigation. + [CA.MemberNotNullWhen(true, nameof(DefiningNavigationName), nameof(DefiningEntityType))] + public bool HasDefiningNavigation() + { + if (DefiningEntityType != null) + { + Check.DebugAssert(DefiningNavigationName != null, + $"{nameof(DefiningEntityType)} is non-null but {nameof(DefiningNavigationName)} is null"); + return true; + } + return false; + } + /// /// Gets primary key for this entity type. Returns if no primary key is defined. /// diff --git a/src/EFCore/Metadata/ITypeBase.cs b/src/EFCore/Metadata/ITypeBase.cs index 73696c1b0ab..f96629fc363 100644 --- a/src/EFCore/Metadata/ITypeBase.cs +++ b/src/EFCore/Metadata/ITypeBase.cs @@ -3,6 +3,7 @@ using System; using Microsoft.EntityFrameworkCore.Infrastructure; +using CA = System.Diagnostics.CodeAnalysis; #nullable enable @@ -35,9 +36,17 @@ public interface ITypeBase : IAnnotatable /// Type? ClrType { get; } + /// + /// Gets whether this entity type has an associated CLR type. An entity type without an associated CLR type is known as + /// a shadow type. + /// + [CA.MemberNotNullWhen(true, nameof(ClrType))] + public bool HasClrType => ClrType != null; + /// /// Gets whether this entity type can share its ClrType with other entities. /// + [CA.MemberNotNullWhen(true, nameof(ClrType))] bool HasSharedClrType { get; } /// diff --git a/src/EFCore/Metadata/Internal/EntityType.cs b/src/EFCore/Metadata/Internal/EntityType.cs index 590adbc3e98..31eb3694f9e 100644 --- a/src/EFCore/Metadata/Internal/EntityType.cs +++ b/src/EFCore/Metadata/Internal/EntityType.cs @@ -228,6 +228,22 @@ public virtual bool IsKeyless /// public virtual EntityType? DefiningEntityType { get; } + /// + /// Gets a value indicating whether this entity type has a defining navigation. + /// + /// if this entity type has a defining navigation. + [CA.MemberNotNullWhen(true, nameof(DefiningNavigationName), nameof(DefiningEntityType))] + public virtual bool HasDefiningNavigation() + { + if (DefiningEntityType != null) + { + Check.DebugAssert(DefiningNavigationName != null, + $"{nameof(DefiningEntityType)} is non-null but {nameof(DefiningNavigationName)} is null"); + return true; + } + return false; + } + /// /// 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 @@ -306,7 +322,7 @@ public virtual void UpdateIsKeylessConfigurationSource(ConfigurationSource confi return newBaseType; } - if (this.HasDefiningNavigation()) + if (HasDefiningNavigation()) { throw new InvalidOperationException(CoreStrings.WeakDerivedType(this.DisplayName())); } @@ -318,15 +334,14 @@ public virtual void UpdateIsKeylessConfigurationSource(ConfigurationSource confi if (newBaseType != null) { - if (this.HasClrType()) + if (HasClrType) { - if (!newBaseType.HasClrType()) + if (!newBaseType.HasClrType) { throw new InvalidOperationException(CoreStrings.NonClrBaseType(this.DisplayName(), newBaseType.DisplayName())); } - // TODO-NULLABLE: Use MemberNotNullWhen on HasClrType when we target net5.0 - if (!newBaseType.ClrType!.IsAssignableFrom(ClrType)) + if (!newBaseType.ClrType.IsAssignableFrom(ClrType)) { throw new InvalidOperationException( CoreStrings.NotAssignableClrBaseType( @@ -340,8 +355,8 @@ public virtual void UpdateIsKeylessConfigurationSource(ConfigurationSource confi } } - if (!this.HasClrType() - && newBaseType.HasClrType()) + if (!HasClrType + && newBaseType.HasClrType) { throw new InvalidOperationException(CoreStrings.NonShadowBaseType(this.DisplayName(), newBaseType.DisplayName())); } diff --git a/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs b/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs index e8c3c286925..530f26aebdd 100644 --- a/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalForeignKeyBuilder.cs @@ -202,7 +202,7 @@ private InternalForeignKeyBuilder HasNavigations( if (navigationToPrincipalName != null && navigationToPrincipal.Value.MemberInfo == null - && dependentEntityType.HasClrType()) + && dependentEntityType.HasClrType) { var navigationProperty = FindCompatibleClrMember( navigationToPrincipalName, dependentEntityType, principalEntityType, shouldThrow); @@ -214,7 +214,7 @@ private InternalForeignKeyBuilder HasNavigations( if (navigationToDependentName != null && navigationToDependent.Value.MemberInfo == null - && principalEntityType.HasClrType()) + && principalEntityType.HasClrType) { var navigationProperty = FindCompatibleClrMember( navigationToDependentName, principalEntityType, dependentEntityType, shouldThrow); @@ -2266,13 +2266,13 @@ private InternalForeignKeyBuilder ReplaceForeignKey( Check.DebugAssert( navigationToPrincipal?.Name == null || navigationToPrincipal.Value.MemberInfo != null - || !dependentEntityTypeBuilder.Metadata.HasClrType(), + || !dependentEntityTypeBuilder.Metadata.HasClrType, "Principal navigation check failed"); Check.DebugAssert( navigationToDependent?.Name == null || navigationToDependent.Value.MemberInfo != null - || !principalEntityTypeBuilder.Metadata.HasClrType(), + || !principalEntityTypeBuilder.Metadata.HasClrType, "Dependent navigation check failed"); Check.DebugAssert( diff --git a/src/EFCore/Metadata/Internal/InternalModelBuilder.cs b/src/EFCore/Metadata/Internal/InternalModelBuilder.cs index f58e466ac19..451a85c9c2f 100644 --- a/src/EFCore/Metadata/Internal/InternalModelBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalModelBuilder.cs @@ -438,7 +438,7 @@ private InternalModelBuilder Ignore(in TypeIdentity type, ConfigurationSource co var removed = false; foreach (var entityType in Metadata.GetEntityTypes(name).ToList()) { - if (entityType.HasClrType()) + if (entityType.HasClrType) { if (entityType.HasSharedClrType) { diff --git a/src/EFCore/Metadata/Internal/Model.cs b/src/EFCore/Metadata/Internal/Model.cs index b2717b0f31f..8998c5c990b 100644 --- a/src/EFCore/Metadata/Internal/Model.cs +++ b/src/EFCore/Metadata/Internal/Model.cs @@ -214,9 +214,8 @@ public virtual IEnumerable GetEntityTypes() for (var i = 0; i < detachedEntityTypes.Count; i++) { var (definingNavigation, definingEntityType) = detachedEntityTypes[i]; - // TODO-NULLABLE: Put MemberNotNull on HasDefiningNavigation when we target net5.0 if (definingNavigation == entityType.DefiningNavigationName - && definingEntityType == entityType.DefiningEntityType!.Name) + && definingEntityType == entityType.DefiningEntityType.Name) { detachedEntityTypes.RemoveAt(i); break; @@ -255,8 +254,6 @@ public virtual IEnumerable GetEntityTypes() CoreStrings.ClashingNonSharedType(entityType.Name, entityType.ClrType.DisplayName())); } - // TODO-NULLABLE: Put MemberNotNull on HasSharedClrType when we target net5.0 - Check.DebugAssert(entityType.ClrType != null, "entityType.ClrType is null"); if (_sharedTypes.TryGetValue(entityType.ClrType, out var existingConfigurationSource)) { _sharedTypes[entityType.ClrType] = entityType.GetConfigurationSource().Max(existingConfigurationSource); @@ -502,9 +499,8 @@ public virtual bool HasOtherEntityTypesWithDefiningNavigation([NotNull] EntityTy if (_detachedEntityTypesWithDefiningNavigation.TryGetValue(entityType.Name, out var detachedEntityTypesWithSameType)) { - // TODO-NULLABLE: Put MemberNotNull on HasDefiningNavigation when we target net5.0 if (detachedEntityTypesWithSameType.Any( - e => e.Item1 != entityType.DefiningNavigationName || e.Item2 != entityType.DefiningEntityType!.Name)) + e => e.Item1 != entityType.DefiningNavigationName || e.Item2 != entityType.DefiningEntityType.Name)) { return true; } @@ -570,12 +566,11 @@ private bool EntityTypeShouldHaveDefiningNavigation(in TypeIdentity type) /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual EntityType? FindActualEntityType([NotNull] EntityType entityType) - // TODO-NULLABLE: Put MemberNotNull on HasDefiningNavigation when we target net5.0 => entityType.Builder != null ? entityType : (entityType.HasDefiningNavigation() - ? FindActualEntityType(entityType.DefiningEntityType!) - ?.FindNavigation(entityType.DefiningNavigationName!)?.TargetEntityType + ? FindActualEntityType(entityType.DefiningEntityType) + ?.FindNavigation(entityType.DefiningNavigationName)?.TargetEntityType : FindEntityType(entityType.Name)); /// diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs index fef8e8a734b..a6a02419048 100644 --- a/src/EFCore/Metadata/Internal/Property.cs +++ b/src/EFCore/Metadata/Internal/Property.cs @@ -640,7 +640,7 @@ public static bool AreCompatible([NotNull] IReadOnlyList properties, [ return properties.All( property => property.IsShadowProperty() - || (entityType.HasClrType() + || (entityType.HasClrType && ((property.PropertyInfo != null && entityType.GetRuntimeProperties()!.ContainsKey(property.Name)) || (property.FieldInfo != null diff --git a/src/EFCore/Metadata/Internal/TypeBase.cs b/src/EFCore/Metadata/Internal/TypeBase.cs index 60f7c32da1e..f8249d0e264 100644 --- a/src/EFCore/Metadata/Internal/TypeBase.cs +++ b/src/EFCore/Metadata/Internal/TypeBase.cs @@ -10,6 +10,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Utilities; +using CA = System.Diagnostics.CodeAnalysis; #nullable enable @@ -23,6 +24,7 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal /// public abstract class TypeBase : ConventionAnnotatable, IMutableTypeBase, IConventionTypeBase { + private readonly bool _hasSharedClrType; private ConfigurationSource _configurationSource; private readonly Dictionary _ignoredMembers @@ -47,7 +49,7 @@ protected TypeBase([NotNull] string name, [NotNull] Model model, ConfigurationSo Name = name; Model = model; _configurationSource = configurationSource; - HasSharedClrType = false; + _hasSharedClrType = false; } /// @@ -64,7 +66,7 @@ protected TypeBase([NotNull] Type type, [NotNull] Model model, ConfigurationSour Model = model; _configurationSource = configurationSource; Name = model.GetDisplayName(type); - HasSharedClrType = false; + _hasSharedClrType = false; IsPropertyBag = type.IsPropertyBagType(); } @@ -84,7 +86,7 @@ protected TypeBase([NotNull] string name, [NotNull] Type type, [NotNull] Model m ClrType = type; Model = model; _configurationSource = configurationSource; - HasSharedClrType = true; + _hasSharedClrType = true; IsPropertyBag = type.IsPropertyBagType(); } @@ -118,7 +120,29 @@ protected TypeBase([NotNull] string name, [NotNull] Type type, [NotNull] Model m /// 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 HasSharedClrType { [DebuggerStepThrough] get; } + [CA.MemberNotNullWhen(true, nameof(ClrType))] + public virtual bool HasClrType => ClrType is not 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. + /// + [CA.MemberNotNullWhen(true, nameof(ClrType))] + public virtual bool HasSharedClrType + { + [DebuggerStepThrough] get + { + if (_hasSharedClrType) + { + Check.DebugAssert(ClrType != null, $"{nameof(_hasSharedClrType)} is true but {nameof(ClrType)} is null"); + return true; + } + + return false; + } + } /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs b/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs index de7a0fb487d..a5d1c5f9900 100644 --- a/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs +++ b/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs @@ -17,16 +17,6 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal /// public static class TypeBaseExtensions { - /// - /// 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. - /// - [DebuggerStepThrough] - public static bool HasClrType([NotNull] this ITypeBase type) - => type.ClrType != 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 diff --git a/src/EFCore/Query/Internal/EntityMaterializerSource.cs b/src/EFCore/Query/Internal/EntityMaterializerSource.cs index 99f35addede..d57fc2b1f8a 100644 --- a/src/EFCore/Query/Internal/EntityMaterializerSource.cs +++ b/src/EFCore/Query/Internal/EntityMaterializerSource.cs @@ -58,7 +58,7 @@ public virtual Expression CreateMaterializeExpression( string entityInstanceName, Expression materializationContextExpression) { - if (!entityType.HasClrType()) + if (!entityType.HasClrType) { throw new InvalidOperationException(CoreStrings.NoClrType(entityType.DisplayName())); }