From adbb2b3b3f0f602594572a7a7c23f6422c3e92a3 Mon Sep 17 00:00:00 2001 From: AndriySvyryd Date: Fri, 30 Mar 2018 17:24:48 -0700 Subject: [PATCH] Cache property and field infos Memoize DisplayName() for Type Call Attribute.IsDefined before GetCustomAttributes() Use in parameters for TypeMappingInfo and RelationalTypeMappingInfo Implement IEquatable on TypeMappingInfo and RelationalTypeMappingInfo Fixes #9347 --- .../Internal/OracleTypeMappingSource.cs | 2 +- .../FallbackRelationalTypeMappingSource.cs | 12 +- .../Storage/RelationalTypeMapping.cs | 2 +- .../Storage/RelationalTypeMappingInfo.cs | 15 +- .../Storage/RelationalTypeMappingSource.cs | 50 +++---- .../Metadata/SqlServerPropertyAnnotations.cs | 1 - .../Internal/SqlServerTypeMappingSource.cs | 2 +- .../Internal/SqliteTypeMappingSource.cs | 2 +- .../Extensions/MutableModelExtensions.cs | 2 +- src/EFCore/Infrastructure/ModelValidator.cs | 16 ++- .../Metadata/Builders/EntityTypeBuilder.cs | 6 +- .../Builders/ReferenceOwnershipBuilder.cs | 6 +- .../Internal/BackingFieldConvention.cs | 29 ++-- .../Internal/CacheCleanupConvention.cs | 28 ++++ .../Internal/CoreConventionSetBuilder.cs | 1 + .../Internal/EntityTypeAttributeConvention.cs | 9 +- .../Internal/ForeignKeyAttributeConvention.cs | 68 ++++----- .../InversePropertyAttributeConvention.cs | 25 ++-- .../Internal/KeyAttributeConvention.cs | 10 +- ...NavigationAttributeEntityTypeConvention.cs | 87 ++++++------ ...NavigationAttributeNavigationConvention.cs | 38 +++-- .../NotMappedMemberAttributeConvention.cs | 11 +- .../Internal/PropertyAttributeConvention.cs | 12 +- .../Internal/PropertyDiscoveryConvention.cs | 4 +- .../PropertyMappingValidationConvention.cs | 131 +++++++++--------- .../RelationshipDiscoveryConvention.cs | 10 +- .../ServicePropertyDiscoveryConvention.cs | 6 +- src/EFCore/Metadata/Internal/EntityType.cs | 14 ++ .../Internal/InternalEntityTypeBuilder.cs | 18 ++- .../Metadata/Internal/InternalModelBuilder.cs | 20 +-- .../Internal/InternalPropertyBuilder.cs | 2 +- .../InternalServicePropertyBuilder.cs | 2 +- src/EFCore/Metadata/Internal/Model.cs | 47 ++++--- .../Metadata/Internal/ModelExtensions.cs | 7 +- src/EFCore/Metadata/Internal/Navigation.cs | 7 +- src/EFCore/Metadata/Internal/Property.cs | 19 ++- src/EFCore/Metadata/Internal/PropertyBase.cs | 15 +- .../Metadata/Internal/ServiceProperty.cs | 7 +- src/EFCore/Metadata/Internal/TypeBase.cs | 71 +++++++++- src/EFCore/Metadata/Internal/TypeIdentity.cs | 27 +--- .../Internal/FallbackTypeMappingSource.cs | 2 +- src/EFCore/Storage/TypeMappingInfo.cs | 4 +- src/EFCore/Storage/TypeMappingSource.cs | 41 +++--- src/EFCore/Storage/TypeMappingSourceBase.cs | 6 +- .../IValueConverterSelector.cs | 2 +- src/Shared/PropertyInfoExtensions.cs | 5 +- .../TestRelationalTypeMappingSource.cs | 2 +- .../EverythingIsBytesSqlServerTest.cs | 2 +- .../EverythingIsStringsSqlServerTest.cs | 2 +- .../NavigationAttributeConventionTest.cs | 2 +- 50 files changed, 527 insertions(+), 382 deletions(-) create mode 100644 src/EFCore/Metadata/Conventions/Internal/CacheCleanupConvention.cs diff --git a/samples/OracleProvider/src/OracleProvider/Storage/Internal/OracleTypeMappingSource.cs b/samples/OracleProvider/src/OracleProvider/Storage/Internal/OracleTypeMappingSource.cs index ab93cefe78b..a0a1af4086e 100644 --- a/samples/OracleProvider/src/OracleProvider/Storage/Internal/OracleTypeMappingSource.cs +++ b/samples/OracleProvider/src/OracleProvider/Storage/Internal/OracleTypeMappingSource.cs @@ -164,7 +164,7 @@ public OracleTypeMappingSource( /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - protected override RelationalTypeMapping FindMapping(RelationalTypeMappingInfo mappingInfo) + protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) { var mapping = FindRawMapping(mappingInfo)?.Clone(mappingInfo); diff --git a/src/EFCore.Relational/Storage/Internal/FallbackRelationalTypeMappingSource.cs b/src/EFCore.Relational/Storage/Internal/FallbackRelationalTypeMappingSource.cs index 82762cbaab8..198e5cb0699 100644 --- a/src/EFCore.Relational/Storage/Internal/FallbackRelationalTypeMappingSource.cs +++ b/src/EFCore.Relational/Storage/Internal/FallbackRelationalTypeMappingSource.cs @@ -41,7 +41,7 @@ public FallbackRelationalTypeMappingSource( /// directly from your code. This API may change or be removed in future releases. /// protected override RelationalTypeMapping FindMappingWithConversion( - RelationalTypeMappingInfo mappingInfo, + in RelationalTypeMappingInfo mappingInfo, IProperty property) { _property = property; @@ -53,7 +53,7 @@ protected override RelationalTypeMapping FindMappingWithConversion( /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - protected override RelationalTypeMapping FindMapping(RelationalTypeMappingInfo mappingInfo) + protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) { Check.NotNull(mappingInfo, nameof(mappingInfo)); @@ -83,12 +83,12 @@ protected override RelationalTypeMapping FindMapping(RelationalTypeMappingInfo m return mapping; } - private RelationalTypeMapping FindMappingForProperty(RelationalTypeMappingInfo mappingInfo) + private RelationalTypeMapping FindMappingForProperty(in RelationalTypeMappingInfo mappingInfo) => _property != null ? _relationalTypeMapper.FindMapping(_property) : null; - private RelationalTypeMapping FindMappingForClrType(RelationalTypeMappingInfo mappingInfo) + private RelationalTypeMapping FindMappingForClrType(in RelationalTypeMappingInfo mappingInfo) { if (mappingInfo.ClrType == null || (mappingInfo.StoreTypeName != null @@ -118,7 +118,7 @@ private RelationalTypeMapping FindMappingForClrType(RelationalTypeMappingInfo ma return _relationalTypeMapper.FindMapping(mappingInfo.ClrType); } - private RelationalTypeMapping FindMappingForStoreTypeName(RelationalTypeMappingInfo mappingInfo) + private RelationalTypeMapping FindMappingForStoreTypeName(in RelationalTypeMappingInfo mappingInfo) { if (mappingInfo.StoreTypeName != null) { @@ -130,7 +130,7 @@ private RelationalTypeMapping FindMappingForStoreTypeName(RelationalTypeMappingI return null; } - private static RelationalTypeMapping FilterByClrType(RelationalTypeMapping mapping, RelationalTypeMappingInfo mappingInfo) + private static RelationalTypeMapping FilterByClrType(RelationalTypeMapping mapping, in RelationalTypeMappingInfo mappingInfo) => mapping != null && (mappingInfo.ClrType == null || mappingInfo.ClrType == mapping.ClrType) diff --git a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs index 290514806dd..6024e9397ba 100644 --- a/src/EFCore.Relational/Storage/RelationalTypeMapping.cs +++ b/src/EFCore.Relational/Storage/RelationalTypeMapping.cs @@ -295,7 +295,7 @@ public override CoreTypeMapping Clone(ValueConverter converter) /// /// The mapping info containing the facets to use. /// The cloned mapping, or the original mapping if no clone was needed. - public virtual RelationalTypeMapping Clone(RelationalTypeMappingInfo mappingInfo) + public virtual RelationalTypeMapping Clone(in RelationalTypeMappingInfo mappingInfo) { Check.NotNull(mappingInfo, nameof(mappingInfo)); diff --git a/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs b/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs index 43537d85468..2cabbb8fc82 100644 --- a/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs +++ b/src/EFCore.Relational/Storage/RelationalTypeMappingInfo.cs @@ -17,7 +17,7 @@ namespace Microsoft.EntityFrameworkCore.Storage /// Describes metadata needed to decide on a relational type mapping for /// a property, type, or provider-specific relational type name. /// - public readonly struct RelationalTypeMappingInfo + public readonly struct RelationalTypeMappingInfo : IEquatable { private readonly TypeMappingInfo _coreTypeMappingInfo; private readonly int? _parsedSize; @@ -88,11 +88,12 @@ public RelationalTypeMappingInfo([NotNull] MemberInfo member) _coreTypeMappingInfo = new TypeMappingInfo(member); - var attribute = member.GetCustomAttributes(true)?.FirstOrDefault(); - if (attribute != null) + if (Attribute.IsDefined(member, typeof(ColumnAttribute), inherit: true)) { + var attribute = member.GetCustomAttributes(inherit: true).First(); StoreTypeName = attribute.TypeName; - StoreTypeNameBase = ParseStoreTypeName(attribute.TypeName, out _parsedSize, out _parsedPrecision, out _parsedScale, out _isMax); + StoreTypeNameBase = ParseStoreTypeName( + attribute.TypeName, out _parsedSize, out _parsedPrecision, out _parsedScale, out _isMax); } else { @@ -113,8 +114,8 @@ public RelationalTypeMappingInfo([NotNull] MemberInfo member) /// The source info. /// The converter to apply. public RelationalTypeMappingInfo( - RelationalTypeMappingInfo source, - ValueConverterInfo converter) + in RelationalTypeMappingInfo source, + in ValueConverterInfo converter) { _coreTypeMappingInfo = new TypeMappingInfo( source._coreTypeMappingInfo, @@ -277,7 +278,7 @@ private static string ParseStoreTypeName( /// /// The converter to apply. /// The new mapping info. - public RelationalTypeMappingInfo WithConverter(ValueConverterInfo converterInfo) + public RelationalTypeMappingInfo WithConverter(in ValueConverterInfo converterInfo) => new RelationalTypeMappingInfo(this, converterInfo); /// diff --git a/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs b/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs index 4ca3b62e39f..680f115e8f1 100644 --- a/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs +++ b/src/EFCore.Relational/Storage/RelationalTypeMappingSource.cs @@ -9,15 +9,16 @@ using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Utilities; +#pragma warning disable 1574 +#pragma warning disable CS0419 // Ambiguous reference in cref attribute namespace Microsoft.EntityFrameworkCore.Storage { /// /// - /// The base class for non-relational type mapping starting with version 2.1. Non-relational providers - /// should derive from this class and override + /// The base class for relational type mapping starting with version 2.1. Relational providers + /// should derive from this class and override /// /// /// This type is typically used by database providers (and other extensions). It is generally @@ -26,8 +27,8 @@ namespace Microsoft.EntityFrameworkCore.Storage /// public abstract class RelationalTypeMappingSource : TypeMappingSourceBase, IRelationalTypeMappingSource { - private readonly ConcurrentDictionary _explicitMappings - = new ConcurrentDictionary(); + private readonly ConcurrentDictionary<(RelationalTypeMappingInfo, Type), RelationalTypeMapping> _explicitMappings + = new ConcurrentDictionary<(RelationalTypeMappingInfo, Type), RelationalTypeMapping>(); /// /// Initializes a new instance of the this class. @@ -56,7 +57,7 @@ protected RelationalTypeMappingSource( /// /// The mapping info to use to create the mapping. /// The type mapping, or null if none could be found. - protected abstract RelationalTypeMapping FindMapping(RelationalTypeMappingInfo mappingInfo); + protected abstract RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo); /// /// Dependencies used to create this @@ -64,11 +65,11 @@ protected RelationalTypeMappingSource( protected virtual RelationalTypeMappingSourceDependencies RelationalDependencies { get; } /// - /// Overridden to call + /// Call instead /// /// The mapping info to use to create the mapping. /// The type mapping, or null if none could be found. - protected override CoreTypeMapping FindMapping(TypeMappingInfo mappingInfo) + protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) => throw new InvalidOperationException("FindMapping on a 'RelationalTypeMappingSource' with a non-relational 'TypeMappingInfo'."); /// @@ -76,7 +77,7 @@ protected override CoreTypeMapping FindMapping(TypeMappingInfo mappingInfo) /// directly from your code. This API may change or be removed in future releases. /// protected virtual RelationalTypeMapping FindMappingWithConversion( - RelationalTypeMappingInfo mappingInfo, + in RelationalTypeMappingInfo mappingInfo, [CanBeNull] IProperty property) { Check.NotNull(mappingInfo, nameof(mappingInfo)); @@ -93,33 +94,34 @@ protected virtual RelationalTypeMapping FindMappingWithConversion( ?.UnwrapNullableType(); var resolvedMapping = _explicitMappings.GetOrAdd( - mappingInfo, + (mappingInfo, providerClrType), k => { - var mapping = providerClrType == null - || providerClrType == mappingInfo.ClrType - ? FindMapping(mappingInfo) + var (info, providerType) = k; + var mapping = providerType == null + || providerType == info.ClrType + ? FindMapping(info) : null; if (mapping == null) { - var sourceType = mappingInfo.ClrType; + var sourceType = info.ClrType; if (sourceType != null) { foreach (var converterInfo in Dependencies .ValueConverterSelector - .Select(sourceType, providerClrType)) + .Select(sourceType, providerType)) { - var mappingInfoUsed = mappingInfo.WithConverter(converterInfo); + var mappingInfoUsed = info.WithConverter(converterInfo); mapping = FindMapping(mappingInfoUsed); if (mapping == null - && providerClrType != null) + && providerType != null) { foreach (var secondConverterInfo in Dependencies .ValueConverterSelector - .Select(providerClrType)) + .Select(providerType)) { mapping = FindMapping(mappingInfoUsed.WithConverter(secondConverterInfo)); @@ -140,15 +142,15 @@ protected virtual RelationalTypeMapping FindMappingWithConversion( } } - if (mapping != null - && customConverter != null) - { - mapping = (RelationalTypeMapping)mapping.Clone(customConverter); - } - return mapping; }); + if (resolvedMapping != null + && customConverter != null) + { + resolvedMapping = (RelationalTypeMapping)resolvedMapping.Clone(customConverter); + } + ValidateMapping(resolvedMapping, property); return resolvedMapping; diff --git a/src/EFCore.SqlServer/Metadata/SqlServerPropertyAnnotations.cs b/src/EFCore.SqlServer/Metadata/SqlServerPropertyAnnotations.cs index 4d6947b24ac..93a378a5ed6 100644 --- a/src/EFCore.SqlServer/Metadata/SqlServerPropertyAnnotations.cs +++ b/src/EFCore.SqlServer/Metadata/SqlServerPropertyAnnotations.cs @@ -7,7 +7,6 @@ using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Internal; using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Metadata diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs index 5a5c5d0bb0c..84723b6552d 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerTypeMappingSource.cs @@ -224,7 +224,7 @@ protected override void ValidateMapping(CoreTypeMapping mapping, IProperty prope /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - protected override RelationalTypeMapping FindMapping(RelationalTypeMappingInfo mappingInfo) + protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) => FindRawMapping(mappingInfo)?.Clone(mappingInfo); private RelationalTypeMapping FindRawMapping(RelationalTypeMappingInfo mappingInfo) diff --git a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs index 3f37e0041d0..3910687fb04 100644 --- a/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs +++ b/src/EFCore.Sqlite.Core/Storage/Internal/SqliteTypeMappingSource.cs @@ -64,7 +64,7 @@ public SqliteTypeMappingSource( /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - protected override RelationalTypeMapping FindMapping(RelationalTypeMappingInfo mappingInfo) + protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) { var clrType = mappingInfo.ClrType; if (clrType != null diff --git a/src/EFCore/Extensions/MutableModelExtensions.cs b/src/EFCore/Extensions/MutableModelExtensions.cs index f915412a691..ea332bfb0f3 100644 --- a/src/EFCore/Extensions/MutableModelExtensions.cs +++ b/src/EFCore/Extensions/MutableModelExtensions.cs @@ -70,7 +70,7 @@ public static IMutableEntityType RemoveEntityType([NotNull] this IMutableModel m Check.NotNull(model, nameof(model)); Check.NotNull(type, nameof(type)); - return model.RemoveEntityType(type.DisplayName()); + return model.AsModel().RemoveEntityType(type); } /// diff --git a/src/EFCore/Infrastructure/ModelValidator.cs b/src/EFCore/Infrastructure/ModelValidator.cs index ab824438054..1b558fb071e 100644 --- a/src/EFCore/Infrastructure/ModelValidator.cs +++ b/src/EFCore/Infrastructure/ModelValidator.cs @@ -511,12 +511,7 @@ protected virtual void ValidateData([NotNull] IModel model) foreach (var entityType in model.GetEntityTypes().Where(et => !et.IsQueryType)) { var key = entityType.FindPrimaryKey(); - if (!identityMaps.TryGetValue(key, out var identityMap)) - { - identityMap = key.GetIdentityMapFactory()(sensitiveDataLogged); - identityMaps[key] = identityMap; - } - + IIdentityMap identityMap = null; foreach (var seedDatum in entityType.GetData()) { foreach (var property in entityType.GetProperties()) @@ -578,6 +573,15 @@ protected virtual void ValidateData([NotNull] IModel model) } } + if (identityMap == null) + { + if (!identityMaps.TryGetValue(key, out identityMap)) + { + identityMap = key.GetIdentityMapFactory()(sensitiveDataLogged); + identityMaps[key] = identityMap; + } + } + var entry = identityMap.TryGetEntry(keyValues); if (entry != null) { diff --git a/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs b/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs index 0b39bc8cc58..bb4ad69b239 100644 --- a/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Builders/EntityTypeBuilder.cs @@ -226,7 +226,7 @@ public virtual ReferenceOwnershipBuilder OwnsOne( [NotNull] Type ownedType, [NotNull] string navigationName) => OwnsOneBuilder( - new TypeIdentity(Check.NotNull(ownedType, nameof(ownedType))), + new TypeIdentity(Check.NotNull(ownedType, nameof(ownedType)), (Model)Metadata.Model), Check.NotEmpty(navigationName, nameof(navigationName))); /// @@ -278,7 +278,7 @@ public virtual EntityTypeBuilder OwnsOne( using (Builder.Metadata.Model.ConventionDispatcher.StartBatch()) { - buildAction.Invoke(OwnsOneBuilder(new TypeIdentity(ownedType), navigationName)); + buildAction.Invoke(OwnsOneBuilder(new TypeIdentity(ownedType, (Model)Metadata.Model), navigationName)); return this; } } @@ -311,7 +311,7 @@ public virtual EntityTypeBuilder OwnsOne( } } - private ReferenceOwnershipBuilder OwnsOneBuilder(TypeIdentity ownedType, string navigationName) + private ReferenceOwnershipBuilder OwnsOneBuilder(in TypeIdentity ownedType, string navigationName) { InternalRelationshipBuilder relationship; using (Builder.Metadata.Model.ConventionDispatcher.StartBatch()) diff --git a/src/EFCore/Metadata/Builders/ReferenceOwnershipBuilder.cs b/src/EFCore/Metadata/Builders/ReferenceOwnershipBuilder.cs index f43a4e67d13..947d823823e 100644 --- a/src/EFCore/Metadata/Builders/ReferenceOwnershipBuilder.cs +++ b/src/EFCore/Metadata/Builders/ReferenceOwnershipBuilder.cs @@ -279,7 +279,7 @@ public virtual ReferenceOwnershipBuilder OwnsOne( [NotNull] Type ownedType, [NotNull] string navigationName) => OwnsOneBuilder( - new TypeIdentity(Check.NotNull(ownedType, nameof(ownedType))), + new TypeIdentity(Check.NotNull(ownedType, nameof(ownedType)), (Model)OwnedEntityType.Model), Check.NotEmpty(navigationName, nameof(navigationName))); /// @@ -339,7 +339,7 @@ public virtual ReferenceOwnershipBuilder OwnsOne( using (DeclaringEntityType.Model.ConventionDispatcher.StartBatch()) { - buildAction.Invoke(OwnsOneBuilder(new TypeIdentity(ownedType), navigationName)); + buildAction.Invoke(OwnsOneBuilder(new TypeIdentity(ownedType, (Model)OwnedEntityType.Model), navigationName)); return this; } } @@ -380,7 +380,7 @@ public virtual ReferenceOwnershipBuilder OwnsOne( } } - private ReferenceOwnershipBuilder OwnsOneBuilder(TypeIdentity ownedType, string navigationName) + private ReferenceOwnershipBuilder OwnsOneBuilder(in TypeIdentity ownedType, string navigationName) { InternalRelationshipBuilder relationship; using (RelatedEntityType.Model.ConventionDispatcher.StartBatch()) diff --git a/src/EFCore/Metadata/Conventions/Internal/BackingFieldConvention.cs b/src/EFCore/Metadata/Conventions/Internal/BackingFieldConvention.cs index 605e5e971b9..f7b863fdf57 100644 --- a/src/EFCore/Metadata/Conventions/Internal/BackingFieldConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/BackingFieldConvention.cs @@ -49,7 +49,8 @@ protected virtual void Apply([NotNull] PropertyBase propertyBase) var type = propertyBase.DeclaringType.ClrType; while (type != null) { - var fieldInfo = TryMatchFieldName(type, propertyBase.ClrType, propertyBase.Name); + var fieldInfo = TryMatchFieldName( + (Model)propertyBase.DeclaringType.Model, type, propertyBase.ClrType,propertyBase.Name); if (fieldInfo != null) { propertyBase.SetFieldInfo(fieldInfo, ConfigurationSource.Convention); @@ -60,22 +61,26 @@ protected virtual void Apply([NotNull] PropertyBase propertyBase) } } - /// - /// This API supports the Entity Framework Core infrastructure and is not intended to be used - /// directly from your code. This API may change or be removed in future releases. - /// - protected virtual FieldInfo TryMatchFieldName( - [NotNull] Type entityType, [NotNull] Type propertyType, [NotNull] string propertyName) + private FieldInfo TryMatchFieldName(Model model, Type entityClrType, Type propertyType, string propertyName) { - var fields = new Dictionary(); - foreach (var field in entityType.GetRuntimeFields()) + Dictionary fields; + var entityType = model.FindEntityType(entityClrType); + if (entityType == null) { - if (!field.IsStatic - && !fields.ContainsKey(field.Name)) + fields = new Dictionary(); + foreach (var field in entityClrType.GetRuntimeFields()) { - fields[field.Name] = field; + if (!field.IsStatic + && !fields.ContainsKey(field.Name)) + { + fields[field.Name] = field; + } } } + else + { + fields = entityType.GetRuntimeFields(); + } var sortedFields = fields.OrderBy(p => p.Key, StringComparer.Ordinal).ToArray(); diff --git a/src/EFCore/Metadata/Conventions/Internal/CacheCleanupConvention.cs b/src/EFCore/Metadata/Conventions/Internal/CacheCleanupConvention.cs new file mode 100644 index 00000000000..f0f3cf0768f --- /dev/null +++ b/src/EFCore/Metadata/Conventions/Internal/CacheCleanupConvention.cs @@ -0,0 +1,28 @@ +// 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 Microsoft.EntityFrameworkCore.Metadata.Internal; + +namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal +{ + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public class CacheCleanupConvention : IModelBuiltConvention + { + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public virtual InternalModelBuilder Apply(InternalModelBuilder modelBuilder) + { + foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()) + { + entityType.ClearCaches(); + } + + return modelBuilder; + } + } +} diff --git a/src/EFCore/Metadata/Conventions/Internal/CoreConventionSetBuilder.cs b/src/EFCore/Metadata/Conventions/Internal/CoreConventionSetBuilder.cs index b3ad000e636..c7507006520 100644 --- a/src/EFCore/Metadata/Conventions/Internal/CoreConventionSetBuilder.cs +++ b/src/EFCore/Metadata/Conventions/Internal/CoreConventionSetBuilder.cs @@ -160,6 +160,7 @@ var servicePropertyDiscoveryConvention conventionSet.ModelBuiltConventions.Add(new RelationshipValidationConvention()); conventionSet.ModelBuiltConventions.Add(foreignKeyPropertyDiscoveryConvention); conventionSet.ModelBuiltConventions.Add(servicePropertyDiscoveryConvention); + conventionSet.ModelBuiltConventions.Add(new CacheCleanupConvention()); conventionSet.NavigationAddedConventions.Add(backingFieldConvention); conventionSet.NavigationAddedConventions.Add(new RequiredNavigationAttributeConvention(Dependencies.Logger)); diff --git a/src/EFCore/Metadata/Conventions/Internal/EntityTypeAttributeConvention.cs b/src/EFCore/Metadata/Conventions/Internal/EntityTypeAttributeConvention.cs index cea16a694de..0b9a3dd712f 100644 --- a/src/EFCore/Metadata/Conventions/Internal/EntityTypeAttributeConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/EntityTypeAttributeConvention.cs @@ -24,7 +24,14 @@ public virtual InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityT { Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); - var attributes = entityTypeBuilder.Metadata.ClrType?.GetTypeInfo().GetCustomAttributes(true); + var type = entityTypeBuilder.Metadata.ClrType; + if (type == null + || !Attribute.IsDefined(type, typeof(TAttribute), inherit: true)) + { + return entityTypeBuilder; + } + + var attributes = type.GetTypeInfo().GetCustomAttributes(true); if (attributes != null) { foreach (var attribute in attributes) diff --git a/src/EFCore/Metadata/Conventions/Internal/ForeignKeyAttributeConvention.cs b/src/EFCore/Metadata/Conventions/Internal/ForeignKeyAttributeConvention.cs index 17093e55e44..1c1ab06fe65 100644 --- a/src/EFCore/Metadata/Conventions/Internal/ForeignKeyAttributeConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/ForeignKeyAttributeConvention.cs @@ -231,18 +231,16 @@ private static InternalRelationshipBuilder SplitNavigationsToSeparateRelationshi } private static InversePropertyAttribute GetInversePropertyAttributeOnNavigation(Navigation navigation) - { - return navigation.DeclaringEntityType.ClrType?.GetRuntimeProperties() - .FirstOrDefault(p => string.Equals(p.Name, navigation.Name, StringComparison.OrdinalIgnoreCase)) - ?.GetCustomAttribute(true); - } + => navigation.DeclaringEntityType.GetRuntimeProperties()?.Values + .FirstOrDefault(p => string.Equals(p.Name, navigation.Name, StringComparison.OrdinalIgnoreCase) + && Attribute.IsDefined(p, typeof(InversePropertyAttribute), inherit: true)) + ?.GetCustomAttribute(inherit: true); private static ForeignKeyAttribute GetForeignKeyAttribute(TypeBase entityType, string propertyName) - { - return entityType.ClrType?.GetRuntimeProperties() - .FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparison.OrdinalIgnoreCase)) - ?.GetCustomAttribute(true); - } + => entityType.GetRuntimeProperties()?.Values + .FirstOrDefault(p => string.Equals(p.Name, propertyName, StringComparison.OrdinalIgnoreCase) + && Attribute.IsDefined(p, typeof(ForeignKeyAttribute), inherit: true)) + ?.GetCustomAttribute(inherit: true); [ContractAnnotation("navigationName:null => null")] private MemberInfo FindForeignKeyAttributeOnProperty(EntityType entityType, string navigationName) @@ -254,29 +252,31 @@ private MemberInfo FindForeignKeyAttributeOnProperty(EntityType entityType, stri } MemberInfo candidateProperty = null; - var clrType = entityType.ClrType; - foreach (var memberInfo in clrType.GetRuntimeProperties().Cast() - .Concat(clrType.GetRuntimeFields())) + foreach (var memberInfo in entityType.GetRuntimeProperties().Values.Cast() + .Concat(entityType.GetRuntimeFields().Values)) { - var attribute = memberInfo.GetCustomAttribute(true); - - if (attribute != null - && attribute.Name == navigationName) + if (!Attribute.IsDefined(memberInfo, typeof(ForeignKeyAttribute), inherit: true)) { - if (memberInfo is PropertyInfo propertyInfo - && FindCandidateNavigationPropertyType(propertyInfo) != null) - { - continue; - } + continue; + } - if (candidateProperty != null) - { - throw new InvalidOperationException(CoreStrings.CompositeFkOnProperty(navigationName, entityType.DisplayName())); - } + var attribute = memberInfo.GetCustomAttribute(inherit: true); - candidateProperty = memberInfo; + if (attribute.Name != navigationName + || (memberInfo is PropertyInfo propertyInfo + && FindCandidateNavigationPropertyType(propertyInfo) != null)) + { + continue; + } + + if (candidateProperty != null) + { + throw new InvalidOperationException( + CoreStrings.CompositeFkOnProperty(navigationName,entityType.DisplayName())); } + + candidateProperty = memberInfo; } if (candidateProperty != null) @@ -327,18 +327,22 @@ private static IReadOnlyList FindCandidateDependentPropertiesThroughNavi CoreStrings.InvalidPropertyListOnNavigation(navigation.Name, navigation.DeclaringEntityType.DisplayName())); } - var navigationPropertyTargetType = navigation.DeclaringEntityType.ClrType.GetRuntimeProperties() - .Single(p => p.Name == navigation.Name).PropertyType; + var navigationPropertyTargetType = + navigation.DeclaringEntityType.GetRuntimeProperties()[navigation.Name].PropertyType; - var otherNavigations = navigation.DeclaringEntityType.ClrType.GetRuntimeProperties() + var otherNavigations = navigation.DeclaringEntityType.GetRuntimeProperties().Values .Where(p => p.PropertyType == navigationPropertyTargetType && p.Name != navigation.Name) .OrderBy(p => p.Name); foreach (var propertyInfo in otherNavigations) { + if (!Attribute.IsDefined(propertyInfo, typeof(ForeignKeyAttribute), inherit: true)) + { + continue; + } + var attribute = propertyInfo.GetCustomAttribute(true); - if (attribute != null - && attribute.Name == navigationFkAttribute.Name) + if (attribute.Name == navigationFkAttribute.Name) { throw new InvalidOperationException( CoreStrings.MultipleNavigationsSameFk(navigation.DeclaringEntityType.DisplayName(), attribute.Name)); diff --git a/src/EFCore/Metadata/Conventions/Internal/InversePropertyAttributeConvention.cs b/src/EFCore/Metadata/Conventions/Internal/InversePropertyAttributeConvention.cs index d85885f6d6f..128c5876762 100644 --- a/src/EFCore/Metadata/Conventions/Internal/InversePropertyAttributeConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/InversePropertyAttributeConvention.cs @@ -82,7 +82,8 @@ private InternalRelationshipBuilder ConfigureInverseNavigation( { var entityType = entityTypeBuilder.Metadata; var targetClrType = targetEntityTypeBuilder.Metadata.ClrType; - var inverseNavigationPropertyInfo = targetClrType.GetRuntimeProperties().FirstOrDefault(p => string.Equals(p.Name, attribute.Property, StringComparison.OrdinalIgnoreCase)); + var inverseNavigationPropertyInfo = targetEntityTypeBuilder.Metadata.GetRuntimeProperties().Values + .FirstOrDefault(p => string.Equals(p.Name, attribute.Property, StringComparison.OrdinalIgnoreCase)); if (inverseNavigationPropertyInfo == null || !FindCandidateNavigationPropertyType(inverseNavigationPropertyInfo).GetTypeInfo() @@ -104,16 +105,18 @@ private InternalRelationshipBuilder ConfigureInverseNavigation( } // Check for InversePropertyAttribute on the inverseNavigation to verify that it matches. - var inverseAttribute = inverseNavigationPropertyInfo.GetCustomAttribute(true); - if (inverseAttribute != null - && inverseAttribute.Property != navigationMemberInfo.Name) + if (Attribute.IsDefined(inverseNavigationPropertyInfo, typeof(InversePropertyAttribute))) { - throw new InvalidOperationException( - CoreStrings.InversePropertyMismatch( - navigationMemberInfo.Name, - entityType.DisplayName(), - inverseNavigationPropertyInfo.Name, - targetEntityTypeBuilder.Metadata.DisplayName())); + var inverseAttribute = inverseNavigationPropertyInfo.GetCustomAttribute(true); + if (inverseAttribute.Property != navigationMemberInfo.Name) + { + throw new InvalidOperationException( + CoreStrings.InversePropertyMismatch( + navigationMemberInfo.Name, + entityType.DisplayName(), + inverseNavigationPropertyInfo.Name, + targetEntityTypeBuilder.Metadata.DisplayName())); + } } var referencingNavigationsWithAttribute = @@ -153,7 +156,7 @@ private InternalRelationshipBuilder ConfigureInverseNavigation( { _logger.NonDefiningInverseNavigationWarning(entityType, navigationMemberInfo, targetEntityTypeBuilder.Metadata, inverseNavigationPropertyInfo, - targetClrType.GetRuntimeProperties().First(p => p.Name == entityType.DefiningNavigationName)); + targetEntityTypeBuilder.Metadata.GetRuntimeProperties()[entityType.DefiningNavigationName]); return null; } diff --git a/src/EFCore/Metadata/Conventions/Internal/KeyAttributeConvention.cs b/src/EFCore/Metadata/Conventions/Internal/KeyAttributeConvention.cs index 94e6c677e4b..1abf9977c97 100644 --- a/src/EFCore/Metadata/Conventions/Internal/KeyAttributeConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/KeyAttributeConvention.cs @@ -36,7 +36,10 @@ public override InternalPropertyBuilder Apply( var entityTypeBuilder = entityType.Builder; var currentKey = entityTypeBuilder.Metadata.FindPrimaryKey(); - var properties = new List { propertyBuilder.Metadata.Name }; + var properties = new List + { + propertyBuilder.Metadata.Name + }; if (currentKey != null && entityType.GetPrimaryKeyConfigurationSource() == ConfigurationSource.DataAnnotation) @@ -81,8 +84,9 @@ public virtual InternalModelBuilder Apply(InternalModelBuilder modelBuilder) foreach (var declaredProperty in entityType.GetDeclaredProperties()) { var memberInfo = declaredProperty.GetIdentifyingMemberInfo(); - var attributes = memberInfo?.GetCustomAttributes(true); - if (attributes?.Any() == true) + + if (memberInfo != null + && Attribute.IsDefined(memberInfo, typeof(KeyAttribute), inherit: true)) { throw new InvalidOperationException( CoreStrings.KeyAttributeOnDerivedEntity(entityType.DisplayName(), declaredProperty.Name)); diff --git a/src/EFCore/Metadata/Conventions/Internal/NavigationAttributeEntityTypeConvention.cs b/src/EFCore/Metadata/Conventions/Internal/NavigationAttributeEntityTypeConvention.cs index 709c288e256..6fe13f1129f 100644 --- a/src/EFCore/Metadata/Conventions/Internal/NavigationAttributeEntityTypeConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/NavigationAttributeEntityTypeConvention.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reflection; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Utilities; @@ -53,23 +54,20 @@ public virtual InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityT return entityTypeBuilder; } - foreach (var navigationPropertyInfo in entityType.ClrType.GetRuntimeProperties().OrderBy(p => p.Name)) + foreach (var navigationPropertyInfo in entityType.GetRuntimeProperties().Values.OrderBy(p => p.Name)) { - var targetClrType = FindCandidateNavigationPropertyType(navigationPropertyInfo); + var targetClrType = FindCandidateNavigationWithAttributePropertyType(navigationPropertyInfo); if (targetClrType == null) { continue; } - var attributes = navigationPropertyInfo.GetCustomAttributes(true); - if (attributes != null) + var attributes = navigationPropertyInfo.GetCustomAttributes(inherit: true); + foreach (var attribute in attributes) { - foreach (var attribute in attributes) + if (Apply(entityTypeBuilder, navigationPropertyInfo, targetClrType, attribute) == null) { - if (Apply(entityTypeBuilder, navigationPropertyInfo, targetClrType, attribute) == null) - { - return null; - } + return null; } } } @@ -90,21 +88,18 @@ public virtual bool Apply(InternalModelBuilder modelBuilder, string name, Type t foreach (var navigationPropertyInfo in type.GetRuntimeProperties().OrderBy(p => p.Name)) { - var targetClrType = FindCandidateNavigationPropertyType(navigationPropertyInfo); + var targetClrType = FindCandidateNavigationWithAttributePropertyType(navigationPropertyInfo); if (targetClrType == null) { continue; } var attributes = navigationPropertyInfo.GetCustomAttributes(true); - if (attributes != null) + foreach (var attribute in attributes) { - foreach (var attribute in attributes) + if (!Apply(modelBuilder, type, navigationPropertyInfo, targetClrType, attribute)) { - if (!Apply(modelBuilder, type, navigationPropertyInfo, targetClrType, attribute)) - { - return false; - } + return false; } } } @@ -116,24 +111,23 @@ public virtual bool Apply(InternalModelBuilder modelBuilder, string name, Type t /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - public virtual InternalRelationshipBuilder Apply(InternalRelationshipBuilder relationshipBuilder, Navigation navigation) + public virtual InternalRelationshipBuilder Apply( + InternalRelationshipBuilder relationshipBuilder, Navigation navigation) { var navigationPropertyInfo = navigation.GetIdentifyingMemberInfo(); - if (navigationPropertyInfo == null) + if (navigationPropertyInfo == null + || !Attribute.IsDefined(navigationPropertyInfo, typeof(TAttribute), inherit: true)) { return relationshipBuilder; } var attributes = navigationPropertyInfo.GetCustomAttributes(true); - if (attributes != null) + foreach (var attribute in attributes) { - foreach (var attribute in attributes) + relationshipBuilder = Apply(relationshipBuilder, navigation, attribute); + if (relationshipBuilder == null) { - relationshipBuilder = Apply(relationshipBuilder, navigation, attribute); - if (relationshipBuilder == null) - { - return null; - } + return null; } } @@ -146,29 +140,26 @@ public virtual InternalRelationshipBuilder Apply(InternalRelationshipBuilder rel /// public virtual bool Apply(InternalEntityTypeBuilder entityTypeBuilder, EntityType oldBaseType) { - var clrType = entityTypeBuilder.Metadata.ClrType; - if (clrType == null) + var entityType = entityTypeBuilder.Metadata; + if (!entityType.HasClrType()) { return true; } - foreach (var navigationPropertyInfo in clrType.GetRuntimeProperties().OrderBy(p => p.Name)) + foreach (var navigationPropertyInfo in entityType.GetRuntimeProperties().Values.OrderBy(p => p.Name)) { - var targetClrType = FindCandidateNavigationPropertyType(navigationPropertyInfo); + var targetClrType = FindCandidateNavigationWithAttributePropertyType(navigationPropertyInfo); if (targetClrType == null) { continue; } var attributes = navigationPropertyInfo.GetCustomAttributes(true); - if (attributes != null) + foreach (var attribute in attributes) { - foreach (var attribute in attributes) + if (!Apply(entityTypeBuilder, oldBaseType, navigationPropertyInfo, targetClrType, attribute)) { - if (!Apply(entityTypeBuilder, oldBaseType, navigationPropertyInfo, targetClrType, attribute)) - { - return false; - } + return false; } } } @@ -183,27 +174,24 @@ public virtual bool Apply(InternalEntityTypeBuilder entityTypeBuilder, EntityTyp public virtual bool Apply(InternalEntityTypeBuilder entityTypeBuilder, string ignoredMemberName) { var navigationPropertyInfo = - entityTypeBuilder.Metadata.ClrType.GetRuntimeProperties().FirstOrDefault(p => p.Name == ignoredMemberName); + entityTypeBuilder.Metadata.GetRuntimeProperties()?.Find(ignoredMemberName); if (navigationPropertyInfo == null) { return true; } - var targetClrType = FindCandidateNavigationPropertyType(navigationPropertyInfo); + var targetClrType = FindCandidateNavigationWithAttributePropertyType(navigationPropertyInfo); if (targetClrType == null) { return true; } var attributes = navigationPropertyInfo.GetCustomAttributes(true); - if (attributes != null) + foreach (var attribute in attributes) { - foreach (var attribute in attributes) + if (!ApplyIgnored(entityTypeBuilder, navigationPropertyInfo, targetClrType, attribute)) { - if (!ApplyIgnored(entityTypeBuilder, navigationPropertyInfo, targetClrType, attribute)) - { - return false; - } + return false; } } return true; @@ -214,10 +202,17 @@ public virtual bool Apply(InternalEntityTypeBuilder entityTypeBuilder, string ig /// directly from your code. This API may change or be removed in future releases. /// public virtual Type FindCandidateNavigationPropertyType([NotNull] PropertyInfo propertyInfo) - { - Check.NotNull(propertyInfo, nameof(propertyInfo)); + => propertyInfo.FindCandidateNavigationPropertyType(_typeMappingSource, _parameterBindingFactories); - return propertyInfo.FindCandidateNavigationPropertyType(_typeMappingSource, _parameterBindingFactories); + private Type FindCandidateNavigationWithAttributePropertyType([NotNull] PropertyInfo propertyInfo) + { + var targetClrType = propertyInfo.FindCandidateNavigationPropertyType(_typeMappingSource, _parameterBindingFactories); + if (targetClrType == null + || !Attribute.IsDefined(propertyInfo, typeof(TAttribute), inherit: true)) + { + return null; + } + return targetClrType; } /// diff --git a/src/EFCore/Metadata/Conventions/Internal/NavigationAttributeNavigationConvention.cs b/src/EFCore/Metadata/Conventions/Internal/NavigationAttributeNavigationConvention.cs index b999784e111..a798b56450d 100644 --- a/src/EFCore/Metadata/Conventions/Internal/NavigationAttributeNavigationConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/NavigationAttributeNavigationConvention.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Utilities; @@ -22,24 +23,22 @@ public abstract class NavigationAttributeNavigationConvention : INav /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - public virtual InternalRelationshipBuilder Apply(InternalRelationshipBuilder relationshipBuilder, Navigation navigation) + public virtual InternalRelationshipBuilder Apply(InternalRelationshipBuilder relationshipBuilder, + Navigation navigation) { Check.NotNull(relationshipBuilder, nameof(relationshipBuilder)); Check.NotNull(navigation, nameof(navigation)); var attributes = GetAttributes(navigation.DeclaringEntityType, navigation.Name); - - if (attributes != null) + foreach (var attribute in attributes) { - foreach (var attribute in attributes) + relationshipBuilder = Apply(relationshipBuilder, navigation, attribute); + if (relationshipBuilder == null) { - relationshipBuilder = Apply(relationshipBuilder, navigation, attribute); - if (relationshipBuilder == null) - { - break; - } + break; } } + return relationshipBuilder; } @@ -47,20 +46,33 @@ public virtual InternalRelationshipBuilder Apply(InternalRelationshipBuilder rel /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - public abstract InternalRelationshipBuilder Apply([NotNull] InternalRelationshipBuilder relationshipBuilder, [NotNull] Navigation navigation, [NotNull] TAttribute attribute); + public abstract InternalRelationshipBuilder Apply([NotNull] InternalRelationshipBuilder relationshipBuilder, + [NotNull] Navigation navigation, [NotNull] TAttribute attribute); /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - protected static IEnumerable GetAttributes([NotNull] EntityType entityType, [NotNull] string propertyName) + protected static IEnumerable GetAttributes( + [NotNull] EntityType entityType, [NotNull] string propertyName) where TCustomAttribute : Attribute { Check.NotNull(entityType, nameof(entityType)); Check.NotNull(propertyName, nameof(propertyName)); - return entityType.ClrType?.GetRuntimeProperties().FirstOrDefault(p => p.Name == propertyName) - ?.GetCustomAttributes(true); + if (!entityType.HasClrType()) + { + return Enumerable.Empty(); + } + + var property = entityType.GetRuntimeProperties().Find(propertyName); + if (property != null + && Attribute.IsDefined(property, typeof(TCustomAttribute), inherit: true)) + { + return property.GetCustomAttributes(true); + } + + return Enumerable.Empty(); } } } diff --git a/src/EFCore/Metadata/Conventions/Internal/NotMappedMemberAttributeConvention.cs b/src/EFCore/Metadata/Conventions/Internal/NotMappedMemberAttributeConvention.cs index 7e101e4eb1f..91171f25895 100644 --- a/src/EFCore/Metadata/Conventions/Internal/NotMappedMemberAttributeConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/NotMappedMemberAttributeConvention.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 System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Reflection; @@ -23,18 +24,18 @@ public virtual InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityT { Check.NotNull(entityTypeBuilder, nameof(entityTypeBuilder)); - var clrType = entityTypeBuilder.Metadata.ClrType; - if (clrType == null) + var entityType = entityTypeBuilder.Metadata; + if (!entityType.HasClrType()) { return entityTypeBuilder; } - var members = clrType.GetRuntimeProperties().Cast().Concat(clrType.GetRuntimeFields()); + var members = entityType.GetRuntimeProperties().Values.Cast() + .Concat(entityType.GetRuntimeFields().Values); foreach (var member in members) { - var attributes = member.GetCustomAttributes(inherit: true); - if (attributes.Any()) + if (Attribute.IsDefined(member, typeof(NotMappedAttribute), inherit: true)) { entityTypeBuilder.Ignore(member.Name, ConfigurationSource.DataAnnotation); } diff --git a/src/EFCore/Metadata/Conventions/Internal/PropertyAttributeConvention.cs b/src/EFCore/Metadata/Conventions/Internal/PropertyAttributeConvention.cs index 5f9b7c0c5d0..9c8a4f7acd1 100644 --- a/src/EFCore/Metadata/Conventions/Internal/PropertyAttributeConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/PropertyAttributeConvention.cs @@ -25,7 +25,17 @@ public virtual InternalPropertyBuilder Apply(InternalPropertyBuilder propertyBui Check.NotNull(propertyBuilder, nameof(propertyBuilder)); var memberInfo = propertyBuilder.Metadata.GetIdentifyingMemberInfo(); - var attributes = memberInfo?.GetCustomAttributes(true); + if (memberInfo == null) + { + return propertyBuilder; + } + + if (!Attribute.IsDefined(memberInfo, typeof(TAttribute), inherit: true)) + { + return propertyBuilder; + } + + var attributes = memberInfo.GetCustomAttributes(inherit: true); if (attributes != null) { foreach (var attribute in attributes) diff --git a/src/EFCore/Metadata/Conventions/Internal/PropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/Internal/PropertyDiscoveryConvention.cs index fd1a91577c7..9befac37b02 100644 --- a/src/EFCore/Metadata/Conventions/Internal/PropertyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/PropertyDiscoveryConvention.cs @@ -40,9 +40,7 @@ public virtual InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityT if (entityType.HasClrType()) { - var candidates = entityType.ClrType.GetRuntimeProperties(); - - foreach (var propertyInfo in candidates) + foreach (var propertyInfo in entityType.GetRuntimeProperties().Values) { if (IsCandidatePrimitiveProperty(propertyInfo)) { diff --git a/src/EFCore/Metadata/Conventions/Internal/PropertyMappingValidationConvention.cs b/src/EFCore/Metadata/Conventions/Internal/PropertyMappingValidationConvention.cs index 123e7f68033..a608720b004 100644 --- a/src/EFCore/Metadata/Conventions/Internal/PropertyMappingValidationConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/PropertyMappingValidationConvention.cs @@ -43,8 +43,6 @@ public PropertyMappingValidationConvention( /// public virtual InternalModelBuilder Apply(InternalModelBuilder modelBuilder) { - Check.NotNull(modelBuilder, nameof(modelBuilder)); - foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()) { var unmappedProperty = entityType.GetProperties().FirstOrDefault(p => @@ -58,73 +56,80 @@ public virtual InternalModelBuilder Apply(InternalModelBuilder modelBuilder) entityType.DisplayName(), unmappedProperty.Name, unmappedProperty.ClrType.ShortDisplayName())); } - if (entityType.HasClrType()) + if (!entityType.HasClrType()) + { + continue; + } + + var clrProperties = new HashSet(); + + clrProperties.UnionWith( + entityType.GetRuntimeProperties().Values + .Where(pi => pi.IsCandidateProperty()) + .Select(pi => pi.Name)); + + clrProperties.ExceptWith(entityType.GetProperties().Select(p => p.Name)); + clrProperties.ExceptWith(entityType.GetNavigations().Select(p => p.Name)); + clrProperties.ExceptWith(entityType.GetServiceProperties().Select(p => p.Name)); + clrProperties.RemoveWhere(p => entityType.Builder.IsIgnored(p, ConfigurationSource.Convention)); + + if (clrProperties.Count <= 0) + { + continue; + } + + foreach (var clrProperty in clrProperties) { - var clrProperties = new HashSet(); + var actualProperty = entityType.GetRuntimeProperties()[clrProperty]; + var propertyType = actualProperty.PropertyType; + var targetSequenceType = propertyType.TryGetSequenceType(); + + if (modelBuilder.IsIgnored(modelBuilder.Metadata.GetDisplayName(propertyType), + ConfigurationSource.Convention) + || (targetSequenceType != null + && modelBuilder.IsIgnored(modelBuilder.Metadata.GetDisplayName(targetSequenceType), + ConfigurationSource.Convention))) + { + continue; + } - clrProperties.UnionWith( - entityType.ClrType.GetRuntimeProperties() - .Where(pi => pi.IsCandidateProperty()) - .Select(pi => pi.Name)); + var targetType = FindCandidateNavigationPropertyType(actualProperty); - clrProperties.ExceptWith(entityType.GetProperties().Select(p => p.Name)); - clrProperties.ExceptWith(entityType.GetNavigations().Select(p => p.Name)); - clrProperties.ExceptWith(entityType.GetServiceProperties().Select(p => p.Name)); - clrProperties.RemoveWhere(p => entityType.Builder.IsIgnored(p, ConfigurationSource.Convention)); + var isTargetWeakOrOwned + = targetType != null + && (modelBuilder.Metadata.HasEntityTypeWithDefiningNavigation(targetType) + || modelBuilder.Metadata.ShouldBeOwnedType(targetType)); - if (clrProperties.Count > 0) + if (targetType != null + && targetType.IsValidEntityType() + && (isTargetWeakOrOwned + || modelBuilder.Metadata.FindEntityType(targetType) != null + || targetType.GetRuntimeProperties().Any(p => p.IsCandidateProperty()))) { - foreach (var clrProperty in clrProperties) + if ((!isTargetWeakOrOwned + || !targetType.GetTypeInfo().Equals(entityType.ClrType.GetTypeInfo())) + && entityType.GetDerivedTypes().All( + dt => dt.FindDeclaredNavigation(actualProperty.Name) == null) + && !entityType.IsInDefinitionPath(targetType)) { - var actualProperty = entityType.ClrType.GetRuntimeProperties().First(p => p.Name == clrProperty); - var propertyType = actualProperty.PropertyType; - var targetSequenceType = propertyType.TryGetSequenceType(); - - if (modelBuilder.IsIgnored(propertyType.DisplayName(), ConfigurationSource.Convention) - || (targetSequenceType != null - && modelBuilder.IsIgnored(targetSequenceType.DisplayName(), ConfigurationSource.Convention))) - { - continue; - } - - var targetType = FindCandidateNavigationPropertyType(actualProperty); - - var isTargetWeakOrOwned - = targetType != null - && (modelBuilder.Metadata.HasEntityTypeWithDefiningNavigation(targetType) - || modelBuilder.Metadata.ShouldBeOwnedType(targetType)); - - if (targetType != null - && targetType.IsValidEntityType() - && (isTargetWeakOrOwned - || modelBuilder.Metadata.FindEntityType(targetType) != null - || targetType.GetRuntimeProperties().Any(p => p.IsCandidateProperty()))) - { - if ((!isTargetWeakOrOwned - || !targetType.GetTypeInfo().Equals(entityType.ClrType.GetTypeInfo())) - && entityType.GetDerivedTypes().All(dt => dt.FindDeclaredNavigation(actualProperty.Name) == null) - && !entityType.IsInDefinitionPath(targetType)) - { - throw new InvalidOperationException( - CoreStrings.NavigationNotAdded( - entityType.DisplayName(), actualProperty.Name, propertyType.ShortDisplayName())); - } - } - else if (targetSequenceType == null && propertyType.GetTypeInfo().IsInterface - || targetSequenceType != null && targetSequenceType.GetTypeInfo().IsInterface) - { - throw new InvalidOperationException( - CoreStrings.InterfacePropertyNotAdded( - entityType.DisplayName(), actualProperty.Name, propertyType.ShortDisplayName())); - } - else - { - throw new InvalidOperationException( - CoreStrings.PropertyNotAdded( - entityType.DisplayName(), actualProperty.Name, propertyType.ShortDisplayName())); - } + throw new InvalidOperationException( + CoreStrings.NavigationNotAdded( + entityType.DisplayName(), actualProperty.Name, propertyType.ShortDisplayName())); } } + else if (targetSequenceType == null && propertyType.GetTypeInfo().IsInterface + || targetSequenceType != null && targetSequenceType.GetTypeInfo().IsInterface) + { + throw new InvalidOperationException( + CoreStrings.InterfacePropertyNotAdded( + entityType.DisplayName(), actualProperty.Name, propertyType.ShortDisplayName())); + } + else + { + throw new InvalidOperationException( + CoreStrings.PropertyNotAdded( + entityType.DisplayName(), actualProperty.Name, propertyType.ShortDisplayName())); + } } } @@ -137,8 +142,6 @@ var isTargetWeakOrOwned /// public virtual bool IsMappedPrimitiveProperty([NotNull] IProperty property) { - Check.NotNull(property, nameof(property)); - return _typeMappingSource.FindMapping(property) != null; } @@ -148,8 +151,6 @@ public virtual bool IsMappedPrimitiveProperty([NotNull] IProperty property) /// public virtual Type FindCandidateNavigationPropertyType([NotNull] PropertyInfo propertyInfo) { - Check.NotNull(propertyInfo, nameof(propertyInfo)); - return propertyInfo.FindCandidateNavigationPropertyType(_typeMappingSource, _parameterBindingFactories); } } diff --git a/src/EFCore/Metadata/Conventions/Internal/RelationshipDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/Internal/RelationshipDiscoveryConvention.cs index 9eacd7f9c17..e5357f77231 100644 --- a/src/EFCore/Metadata/Conventions/Internal/RelationshipDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/RelationshipDiscoveryConvention.cs @@ -353,7 +353,7 @@ private static bool IsCompatibleInverse( var entityType = entityTypeBuilder.Metadata; var existingNavigation = entityType.FindNavigation(navigationProperty.Name); if (existingNavigation != null - && !CanMergeWith(existingNavigation, inversePropertyInfo.Name, targetEntityTypeBuilder)) + && !CanMergeWith(existingNavigation, inversePropertyInfo, targetEntityTypeBuilder)) { return false; } @@ -362,7 +362,7 @@ private static bool IsCompatibleInverse( if (existingInverse != null) { if (existingInverse.DeclaringEntityType != targetEntityTypeBuilder.Metadata - || !CanMergeWith(existingInverse, navigationProperty.Name, entityTypeBuilder)) + || !CanMergeWith(existingInverse, navigationProperty, entityTypeBuilder)) { return false; } @@ -379,12 +379,12 @@ private static bool IsCompatibleInverse( } private static bool CanMergeWith( - Navigation existingNavigation, string inverseName, InternalEntityTypeBuilder inverseEntityTypeBuilder) + Navigation existingNavigation, PropertyInfo inverse, InternalEntityTypeBuilder inverseEntityTypeBuilder) { var fk = existingNavigation.ForeignKey; return (fk.IsSelfReferencing() || fk.ResolveOtherEntityType(existingNavigation.DeclaringEntityType) == inverseEntityTypeBuilder.Metadata) - && fk.Builder.CanSetNavigation(inverseName, !existingNavigation.IsDependentToPrincipal(), ConfigurationSource.Convention); + && fk.Builder.CanSetNavigation(inverse, !existingNavigation.IsDependentToPrincipal(), ConfigurationSource.Convention); } private static IReadOnlyList RemoveInheritedInverseNavigations( @@ -824,7 +824,7 @@ private ImmutableSortedDictionary GetNavigationCandidates(En var dictionaryBuilder = ImmutableSortedDictionary.CreateBuilder(MemberInfoNameComparer.Instance); if (entityType.HasClrType()) { - foreach (var propertyInfo in entityType.ClrType.GetRuntimeProperties().OrderBy(p => p.Name)) + foreach (var propertyInfo in entityType.GetRuntimeProperties().Values.OrderBy(p => p.Name)) { var targetType = FindCandidateNavigationPropertyType(propertyInfo); if (targetType != null) diff --git a/src/EFCore/Metadata/Conventions/Internal/ServicePropertyDiscoveryConvention.cs b/src/EFCore/Metadata/Conventions/Internal/ServicePropertyDiscoveryConvention.cs index 8090f4f7f3d..4388e35074d 100644 --- a/src/EFCore/Metadata/Conventions/Internal/ServicePropertyDiscoveryConvention.cs +++ b/src/EFCore/Metadata/Conventions/Internal/ServicePropertyDiscoveryConvention.cs @@ -57,7 +57,7 @@ public virtual InternalEntityTypeBuilder Apply(InternalEntityTypeBuilder entityT return entityTypeBuilder; } - var candidates = entityType.ClrType.GetRuntimeProperties(); + var candidates = entityType.GetRuntimeProperties().Values; foreach (var propertyInfo in candidates) { @@ -129,8 +129,8 @@ public virtual bool Apply(InternalEntityTypeBuilder entityTypeBuilder, string ig return true; } - var member = (MemberInfo)entityType.ClrType.GetRuntimeProperties().FirstOrDefault(p => p.Name == ignoredMemberName) - ?? entityType.ClrType.GetFieldInfo(ignoredMemberName); + var member = (MemberInfo)entityType.GetRuntimeProperties().Find(ignoredMemberName) + ?? entityType.GetRuntimeFields().Find(ignoredMemberName); var type = member.GetMemberType(); if (duplicateMap.TryGetValue(type, out var duplicateServiceProperties) && duplicateServiceProperties.Remove(member)) diff --git a/src/EFCore/Metadata/Internal/EntityType.cs b/src/EFCore/Metadata/Internal/EntityType.cs index 25aad44ada9..2b3bfb68c4a 100644 --- a/src/EFCore/Metadata/Internal/EntityType.cs +++ b/src/EFCore/Metadata/Internal/EntityType.cs @@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Microsoft.EntityFrameworkCore.Utilities; @@ -374,6 +375,19 @@ private bool InheritsFrom(EntityType entityType) /// public override string ToString() => this.ToDebugString(); + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public override void ClearCaches() + { + base.ClearCaches(); + + RemoveAnnotation(RelationshipDiscoveryConvention.AmbiguousNavigationsAnnotationName); + RemoveAnnotation(RelationshipDiscoveryConvention.NavigationCandidatesAnnotationName); + RemoveAnnotation(InversePropertyAttributeConvention.InverseNavigationsAnnotationName); + } + /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. diff --git a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs index f226d18725b..cf3c18f3cf5 100644 --- a/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalEntityTypeBuilder.cs @@ -2037,7 +2037,7 @@ public virtual InternalRelationshipBuilder Owns( [NotNull] string navigationName, ConfigurationSource configurationSource) => Owns( - new TypeIdentity(targetEntityType), + new TypeIdentity(targetEntityType, Metadata.Model), PropertyIdentity.Create(navigationName), inverse: null, configurationSource: configurationSource); @@ -2051,7 +2051,7 @@ public virtual InternalRelationshipBuilder Owns( [NotNull] PropertyInfo navigationProperty, ConfigurationSource configurationSource) => Owns( - new TypeIdentity(targetEntityType), + new TypeIdentity(targetEntityType, Metadata.Model), PropertyIdentity.Create(navigationProperty), inverse: null, configurationSource: configurationSource); @@ -2066,13 +2066,13 @@ public virtual InternalRelationshipBuilder Owns( [CanBeNull] MemberInfo inverseProperty, ConfigurationSource configurationSource) => Owns( - new TypeIdentity(targetEntityType), + new TypeIdentity(targetEntityType, Metadata.Model), PropertyIdentity.Create(navigationProperty), PropertyIdentity.Create(inverseProperty), configurationSource); private InternalRelationshipBuilder Owns( - TypeIdentity targetEntityType, + in TypeIdentity targetEntityType, PropertyIdentity navigation, PropertyIdentity? inverse, ConfigurationSource configurationSource) @@ -2334,11 +2334,8 @@ private IReadOnlyList CreateUniqueProperties( string baseName) { var newProperties = new Property[propertyCount]; - var clrMembers = Metadata.ClrType == null - ? null - : new HashSet( - Metadata.ClrType.GetRuntimeProperties().Select(p => p.Name) - .Concat(Metadata.ClrType.GetRuntimeFields().Select(p => p.Name))); + var clrProperties = Metadata.GetRuntimeProperties(); + var clrFields = Metadata.GetRuntimeFields(); var noNewProperties = true; using (var principalPropertyNamesEnumerator = principalPropertyNames.GetEnumerator()) { @@ -2362,7 +2359,8 @@ private IReadOnlyList CreateUniqueProperties( { propertyName = keyModifiedBaseName + (++index > 0 ? index.ToString(CultureInfo.InvariantCulture) : ""); if (!Metadata.FindPropertiesInHierarchy(propertyName).Any() - && clrMembers?.Contains(propertyName) != true) + && clrProperties?.ContainsKey(propertyName) != true + && clrFields?.ContainsKey(propertyName) != true) { var propertyBuilder = Property(propertyName, clrType, ConfigurationSource.Convention, typeConfigurationSource: null); if (propertyBuilder == null) diff --git a/src/EFCore/Metadata/Internal/InternalModelBuilder.cs b/src/EFCore/Metadata/Internal/InternalModelBuilder.cs index bfcb65dd051..05ec10b254f 100644 --- a/src/EFCore/Metadata/Internal/InternalModelBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalModelBuilder.cs @@ -48,10 +48,10 @@ public virtual InternalEntityTypeBuilder Entity( /// public virtual InternalEntityTypeBuilder Entity( [NotNull] Type type, ConfigurationSource configurationSource, bool throwOnQuery = false) - => Entity(new TypeIdentity(type), configurationSource, throwOnQuery); + => Entity(new TypeIdentity(type, Metadata), configurationSource, throwOnQuery); private InternalEntityTypeBuilder Entity( - TypeIdentity type, ConfigurationSource configurationSource, bool throwOnQuery) + in TypeIdentity type, ConfigurationSource configurationSource, bool throwOnQuery) { if (IsIgnored(type, configurationSource)) { @@ -187,10 +187,10 @@ public virtual InternalEntityTypeBuilder Entity( [NotNull] string definingNavigationName, [NotNull] EntityType definingEntityType, ConfigurationSource configurationSource) - => Entity(new TypeIdentity(type), definingNavigationName, definingEntityType, configurationSource); + => Entity(new TypeIdentity(type, Metadata), definingNavigationName, definingEntityType, configurationSource); private InternalEntityTypeBuilder Entity( - TypeIdentity type, + in TypeIdentity type, string definingNavigationName, EntityType definingEntityType, ConfigurationSource configurationSource) @@ -266,9 +266,9 @@ public virtual bool Owned( /// public virtual bool Owned( [NotNull] Type type, ConfigurationSource configurationSource) - => Owned(new TypeIdentity(type), configurationSource); + => Owned(new TypeIdentity(type, Metadata), configurationSource); - private bool Owned(TypeIdentity type, ConfigurationSource configurationSource) + private bool Owned(in TypeIdentity type, ConfigurationSource configurationSource) { if (IsIgnored(type, configurationSource)) { @@ -320,7 +320,7 @@ private bool Owned(TypeIdentity type, ConfigurationSource configurationSource) /// directly from your code. This API may change or be removed in future releases. /// public virtual bool IsIgnored([NotNull] Type type, ConfigurationSource configurationSource) - => IsIgnored(new TypeIdentity(type), configurationSource); + => IsIgnored(new TypeIdentity(type, Metadata), configurationSource); /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -329,7 +329,7 @@ public virtual bool IsIgnored([NotNull] Type type, ConfigurationSource configura public virtual bool IsIgnored([NotNull] string name, ConfigurationSource configurationSource) => IsIgnored(new TypeIdentity(name), configurationSource); - private bool IsIgnored(TypeIdentity type, ConfigurationSource configurationSource) + private bool IsIgnored(in TypeIdentity type, ConfigurationSource configurationSource) { if (configurationSource == ConfigurationSource.Explicit) { @@ -346,7 +346,7 @@ private bool IsIgnored(TypeIdentity type, ConfigurationSource configurationSourc /// directly from your code. This API may change or be removed in future releases. /// public virtual bool Ignore([NotNull] Type type, ConfigurationSource configurationSource) - => Ignore(new TypeIdentity(type), configurationSource); + => Ignore(new TypeIdentity(type, Metadata), configurationSource); /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -355,7 +355,7 @@ public virtual bool Ignore([NotNull] Type type, ConfigurationSource configuratio public virtual bool Ignore([NotNull] string name, ConfigurationSource configurationSource) => Ignore(new TypeIdentity(name), configurationSource); - private bool Ignore(TypeIdentity type, ConfigurationSource configurationSource) + private bool Ignore(in TypeIdentity type, ConfigurationSource configurationSource) { var name = type.Name; var ignoredConfigurationSource = Metadata.FindIgnoredTypeConfigurationSource(name); diff --git a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs index 5b13d8870f3..99adc494d68 100644 --- a/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs @@ -148,7 +148,7 @@ public virtual bool HasField([CanBeNull] string fieldName, ConfigurationSource c if (fieldName != null) { var fieldInfo = PropertyBase.GetFieldInfo( - fieldName, Metadata.DeclaringType.ClrType, Metadata.Name, + fieldName, Metadata.DeclaringType, Metadata.Name, shouldThrow: configurationSource == ConfigurationSource.Explicit); Metadata.SetFieldInfo(fieldInfo, configurationSource); return true; diff --git a/src/EFCore/Metadata/Internal/InternalServicePropertyBuilder.cs b/src/EFCore/Metadata/Internal/InternalServicePropertyBuilder.cs index 8c84747e565..351dedfcc4f 100644 --- a/src/EFCore/Metadata/Internal/InternalServicePropertyBuilder.cs +++ b/src/EFCore/Metadata/Internal/InternalServicePropertyBuilder.cs @@ -43,7 +43,7 @@ public virtual bool HasField([CanBeNull] string fieldName, ConfigurationSource c if (fieldName != null) { var fieldInfo = PropertyBase.GetFieldInfo( - fieldName, Metadata.DeclaringType.ClrType, Metadata.Name, + fieldName, Metadata.DeclaringType, Metadata.Name, shouldThrow: configurationSource == ConfigurationSource.Explicit); Metadata.SetFieldInfo(fieldInfo, configurationSource); return true; diff --git a/src/EFCore/Metadata/Internal/Model.cs b/src/EFCore/Metadata/Internal/Model.cs index f297f88764c..421187ee701 100644 --- a/src/EFCore/Metadata/Internal/Model.cs +++ b/src/EFCore/Metadata/Internal/Model.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -23,8 +23,8 @@ public class Model : ConventionalAnnotatable, IMutableModel private readonly SortedDictionary _entityTypes = new SortedDictionary(); - private readonly Dictionary _clrTypeMap - = new Dictionary(); + private readonly Dictionary _clrTypeNameMap + = new Dictionary(); private readonly SortedDictionary> _entityTypesWithDefiningNavigation = new SortedDictionary>(); @@ -109,8 +109,6 @@ public virtual EntityType AddEntityType( var entityType = new EntityType(type, this, configurationSource); - _clrTypeMap[type] = entityType; - return AddEntityType(entityType); } @@ -127,8 +125,6 @@ public virtual EntityType AddQueryType([NotNull] Type type) IsQueryType = true }; - _clrTypeMap[type] = queryType; - return AddEntityType(queryType); } @@ -188,9 +184,7 @@ public virtual EntityType GetOrAddEntityType([NotNull] string name) /// directly from your code. This API may change or be removed in future releases. /// public virtual EntityType FindEntityType([NotNull] Type type) - => _clrTypeMap.TryGetValue(Check.NotNull(type, nameof(type)), out var entityType) - ? entityType - : FindEntityType(type.DisplayName()); + => FindEntityType(GetDisplayName(type)); /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -280,12 +274,6 @@ public virtual EntityType RemoveEntityType([CanBeNull] EntityType entityType) { var removed = _entityTypes.Remove(entityTypeName); Debug.Assert(removed); - - if (entityType.ClrType != null) - { - removed = _clrTypeMap.Remove(entityType.ClrType); - Debug.Assert(removed); - } } entityType.OnTypeRemoved(); @@ -327,12 +315,27 @@ public virtual EntityType AddEntityType( return AddEntityType(entityType); } + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public virtual string GetDisplayName(Type type) + { + if (!_clrTypeNameMap.TryGetValue(type, out var name)) + { + name = type.DisplayName(); + _clrTypeNameMap[type] = name; + } + + return name; + } + /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// public virtual bool HasEntityTypeWithDefiningNavigation([NotNull] Type clrType) - => _entityTypesWithDefiningNavigation.ContainsKey(clrType.DisplayName()); + => _entityTypesWithDefiningNavigation.ContainsKey(GetDisplayName(clrType)); /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -349,7 +352,7 @@ public virtual EntityType FindEntityType( [NotNull] Type type, [NotNull] string definingNavigationName, [NotNull] EntityType definingEntityType) - => FindEntityType(type.DisplayName(), definingNavigationName, definingEntityType); + => FindEntityType(GetDisplayName(type), definingNavigationName, definingEntityType); /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -393,7 +396,7 @@ public virtual EntityType FindEntityType( /// directly from your code. This API may change or be removed in future releases. /// public virtual IReadOnlyCollection GetEntityTypes([NotNull] Type type) - => GetEntityTypes(type.DisplayName()); + => GetEntityTypes(GetDisplayName(type)); /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -437,7 +440,7 @@ public virtual EntityType RemoveEntityType( public virtual void Ignore( [NotNull] Type type, ConfigurationSource configurationSource = ConfigurationSource.Explicit) - => Ignore(Check.NotNull(type, nameof(type)).DisplayName(), type, configurationSource); + => Ignore(GetDisplayName(Check.NotNull(type, nameof(type))), type, configurationSource); /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -473,7 +476,7 @@ private void Ignore( { Check.NotNull(type, nameof(type)); - return FindIgnoredTypeConfigurationSource(type.DisplayName()); + return FindIgnoredTypeConfigurationSource(GetDisplayName(type)); } /// @@ -492,7 +495,7 @@ private void Ignore( public virtual void Unignore([NotNull] Type type) { Check.NotNull(type, nameof(type)); - Unignore(type.DisplayName()); + Unignore(GetDisplayName(type)); } /// diff --git a/src/EFCore/Metadata/Internal/ModelExtensions.cs b/src/EFCore/Metadata/Internal/ModelExtensions.cs index 13cdf401682..1b15da449a3 100644 --- a/src/EFCore/Metadata/Internal/ModelExtensions.cs +++ b/src/EFCore/Metadata/Internal/ModelExtensions.cs @@ -58,7 +58,8 @@ public static bool ShouldBeOwnedType([NotNull] this IModel model, [NotNull] Type while (clrType != null) { - if (ownedTypes.Contains(clrType.DisplayName())) + var name = (model as Model)?.GetDisplayName(clrType) ?? clrType.DisplayName(); + if (ownedTypes.Contains(name)) { return true; } @@ -89,7 +90,7 @@ public static void MarkAsOwnedType([NotNull] this Model model, [NotNull] string /// directly from your code. This API may change or be removed in future releases. /// public static void MarkAsOwnedType([NotNull] this Model model, [NotNull] Type clrType) - => model.MarkAsOwnedType(clrType.DisplayName()); + => model.MarkAsOwnedType(model.GetDisplayName(clrType)); /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -114,7 +115,7 @@ public static void UnmarkAsOwnedType([NotNull] this Model model, [NotNull] Type while (clrType != null) { - ownedTypes.Remove(clrType.DisplayName()); + ownedTypes.Remove(model.GetDisplayName(clrType)); clrType = clrType.BaseType; } diff --git a/src/EFCore/Metadata/Internal/Navigation.cs b/src/EFCore/Metadata/Internal/Navigation.cs index ae5f52a493b..5fd6a21a344 100644 --- a/src/EFCore/Metadata/Internal/Navigation.cs +++ b/src/EFCore/Metadata/Internal/Navigation.cs @@ -75,7 +75,10 @@ public virtual EntityType DeclaringEntityType /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - public new virtual EntityType DeclaringType => DeclaringEntityType; + public override TypeBase DeclaringType + { + [DebuggerStepThrough] get => DeclaringEntityType; + } /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -217,8 +220,6 @@ public virtual IClrCollectionAccessor CollectionAccessor IMutableForeignKey IMutableNavigation.ForeignKey => ForeignKey; IEntityType INavigation.DeclaringEntityType => DeclaringEntityType; IMutableEntityType IMutableNavigation.DeclaringEntityType => DeclaringEntityType; - ITypeBase IPropertyBase.DeclaringType => DeclaringType; - IMutableTypeBase IMutablePropertyBase.DeclaringType => DeclaringType; /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used diff --git a/src/EFCore/Metadata/Internal/Property.cs b/src/EFCore/Metadata/Internal/Property.cs index 95c10cc0608..8f7c650e905 100644 --- a/src/EFCore/Metadata/Internal/Property.cs +++ b/src/EFCore/Metadata/Internal/Property.cs @@ -75,7 +75,7 @@ public Property( /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - public new virtual EntityType DeclaringType + public override TypeBase DeclaringType { [DebuggerStepThrough] get => DeclaringEntityType; } @@ -465,9 +465,7 @@ public static string Format([NotNull] IEnumerable properties) + "}"; IEntityType IProperty.DeclaringEntityType => DeclaringEntityType; - ITypeBase IPropertyBase.DeclaringType => DeclaringType; IMutableEntityType IMutableProperty.DeclaringEntityType => DeclaringEntityType; - IMutableTypeBase IMutablePropertyBase.DeclaringType => DeclaringType; /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -478,14 +476,13 @@ public static bool AreCompatible([NotNull] IReadOnlyList properties, [ Check.NotNull(properties, nameof(properties)); Check.NotNull(entityType, nameof(entityType)); - return properties.All( - property => - property.IsShadowProperty - || (entityType.HasClrType() - && ((property.PropertyInfo != null - && entityType.ClrType.GetRuntimeProperties().FirstOrDefault(p => p.Name == property.Name) != null) - || (property.FieldInfo != null - && entityType.ClrType.GetFieldInfo(property.Name) != null)))); + return properties.All(property => + property.IsShadowProperty + || (entityType.HasClrType() + && ((property.PropertyInfo != null + && entityType.GetRuntimeProperties().ContainsKey(property.Name)) + || (property.FieldInfo != null + && entityType.GetRuntimeFields().ContainsKey(property.Name))))); } /// diff --git a/src/EFCore/Metadata/Internal/PropertyBase.cs b/src/EFCore/Metadata/Internal/PropertyBase.cs index 8114576ddc5..a80f90e598a 100644 --- a/src/EFCore/Metadata/Internal/PropertyBase.cs +++ b/src/EFCore/Metadata/Internal/PropertyBase.cs @@ -51,10 +51,7 @@ protected PropertyBase( /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - public virtual IMutableTypeBase DeclaringType - { - [DebuggerStepThrough] get => ((IMutablePropertyBase)this).DeclaringType; - } + public abstract TypeBase DeclaringType { get; } /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -99,7 +96,7 @@ public virtual void SetField([CanBeNull] string fieldName, ConfigurationSource c return; } - var fieldInfo = GetFieldInfo(fieldName, DeclaringType.ClrType, Name, shouldThrow: true); + var fieldInfo = GetFieldInfo(fieldName, DeclaringType, Name, shouldThrow: true); if (fieldInfo != null) { SetFieldInfo(fieldInfo, configurationSource); @@ -111,16 +108,15 @@ public virtual void SetField([CanBeNull] string fieldName, ConfigurationSource c /// directly from your code. This API may change or be removed in future releases. /// public static FieldInfo GetFieldInfo( - [NotNull] string fieldName, [NotNull] Type type, [CanBeNull] string propertyName, bool shouldThrow) + [NotNull] string fieldName, [NotNull] TypeBase type, [CanBeNull] string propertyName, bool shouldThrow) { Debug.Assert(propertyName != null || !shouldThrow); - var fieldInfo = type.GetFieldInfo(fieldName); - if (fieldInfo == null + if (!type.GetRuntimeFields().TryGetValue(fieldName, out var fieldInfo) && shouldThrow) { throw new InvalidOperationException( - CoreStrings.MissingBackingField(fieldName, propertyName, type.ShortDisplayName())); + CoreStrings.MissingBackingField(fieldName, propertyName, type.DisplayName())); } return fieldInfo; @@ -276,5 +272,6 @@ public virtual PropertyAccessors Accessors => NonCapturingLazyInitializer.EnsureInitialized(ref _accessors, this, p => new PropertyAccessorsFactory().Create(p)); ITypeBase IPropertyBase.DeclaringType => DeclaringType; + IMutableTypeBase IMutablePropertyBase.DeclaringType => DeclaringType; } } diff --git a/src/EFCore/Metadata/Internal/ServiceProperty.cs b/src/EFCore/Metadata/Internal/ServiceProperty.cs index 7f63a4f6969..67653303baa 100644 --- a/src/EFCore/Metadata/Internal/ServiceProperty.cs +++ b/src/EFCore/Metadata/Internal/ServiceProperty.cs @@ -51,7 +51,10 @@ public ServiceProperty( /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - public new virtual EntityType DeclaringType => DeclaringEntityType; + public override TypeBase DeclaringType + { + [DebuggerStepThrough] get => DeclaringEntityType; + } /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used @@ -136,7 +139,5 @@ public virtual DebugView DebugView IEntityType IServiceProperty.DeclaringEntityType => DeclaringEntityType; IMutableEntityType IMutableServiceProperty.DeclaringEntityType => DeclaringEntityType; - ITypeBase IPropertyBase.DeclaringType => DeclaringType; - IMutableTypeBase IMutablePropertyBase.DeclaringType => DeclaringType; } } diff --git a/src/EFCore/Metadata/Internal/TypeBase.cs b/src/EFCore/Metadata/Internal/TypeBase.cs index 2b3f9887c3e..0386af55a47 100644 --- a/src/EFCore/Metadata/Internal/TypeBase.cs +++ b/src/EFCore/Metadata/Internal/TypeBase.cs @@ -5,8 +5,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Reflection; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Metadata.Internal @@ -20,6 +20,9 @@ public abstract class TypeBase : ConventionalAnnotatable, IMutableTypeBase private ConfigurationSource _configurationSource; private readonly Dictionary _ignoredMembers = new Dictionary(); + private Dictionary _runtimeProperties; + private Dictionary _runtimeFields; + /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. @@ -42,7 +45,7 @@ protected TypeBase([NotNull] Type clrType, [NotNull] Model model, ConfigurationS { Check.NotNull(model, nameof(model)); - Name = clrType.DisplayName(); + Name = model.GetDisplayName(clrType); ClrType = clrType; } @@ -90,6 +93,70 @@ public virtual void UpdateConfigurationSource(ConfigurationSource configurationS /// public abstract void PropertyMetadataChanged(); + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public virtual Dictionary GetRuntimeProperties() + { + if (ClrType == null) + { + return null; + } + + if (_runtimeProperties == null) + { + _runtimeProperties = new Dictionary(); + foreach (var property in ClrType.GetRuntimeProperties()) + { + if (!property.IsStatic() + && !_runtimeProperties.ContainsKey(property.Name)) + { + _runtimeProperties[property.Name] = property; + } + } + } + + return _runtimeProperties; + } + + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public virtual Dictionary GetRuntimeFields() + { + if (ClrType == null) + { + return null; + } + + if (_runtimeFields == null) + { + _runtimeFields = new Dictionary(); + foreach (var field in ClrType.GetRuntimeFields()) + { + if (!field.IsStatic + && !_runtimeFields.ContainsKey(field.Name)) + { + _runtimeFields[field.Name] = field; + } + } + } + + return _runtimeFields; + } + + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public virtual void ClearCaches() + { + _runtimeProperties = null; + _runtimeFields = null; + } + /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. diff --git a/src/EFCore/Metadata/Internal/TypeIdentity.cs b/src/EFCore/Metadata/Internal/TypeIdentity.cs index c939e8a3768..a4a95ad9ea5 100644 --- a/src/EFCore/Metadata/Internal/TypeIdentity.cs +++ b/src/EFCore/Metadata/Internal/TypeIdentity.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Internal; namespace Microsoft.EntityFrameworkCore.Metadata.Internal { @@ -15,16 +14,15 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal [DebuggerDisplay("{DebuggerDisplay(),nq}")] public readonly struct TypeIdentity { - private readonly object _nameOrType; - /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// [DebuggerStepThrough] public TypeIdentity([NotNull] string name) - : this((object)name) { + Name = name; + Type = null; } /// @@ -32,34 +30,23 @@ public TypeIdentity([NotNull] string name) /// directly from your code. This API may change or be removed in future releases. /// [DebuggerStepThrough] - public TypeIdentity([NotNull] Type type) - : this((object)type) - { - } - - [DebuggerStepThrough] - private TypeIdentity(object nameOrType) + public TypeIdentity([NotNull] Type type, [NotNull] Model model) { - _nameOrType = nameOrType; + Name = model.GetDisplayName(type); + Type = type; } /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - public string Name - { - [DebuggerStepThrough] get { return Type?.DisplayName() ?? (string)_nameOrType; } - } + public string Name { [DebuggerStepThrough] get; } /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - public Type Type - { - [DebuggerStepThrough] get { return _nameOrType as Type; } - } + public Type Type { [DebuggerStepThrough] get; } private string DebuggerDisplay() => Name; } diff --git a/src/EFCore/Storage/Internal/FallbackTypeMappingSource.cs b/src/EFCore/Storage/Internal/FallbackTypeMappingSource.cs index 9a094d1c9a8..363e910c7fe 100644 --- a/src/EFCore/Storage/Internal/FallbackTypeMappingSource.cs +++ b/src/EFCore/Storage/Internal/FallbackTypeMappingSource.cs @@ -42,7 +42,7 @@ public FallbackTypeMappingSource( /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - protected override CoreTypeMapping FindMapping(TypeMappingInfo mappingInfo) + protected override CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo) { Check.NotNull(mappingInfo, nameof(mappingInfo)); diff --git a/src/EFCore/Storage/TypeMappingInfo.cs b/src/EFCore/Storage/TypeMappingInfo.cs index 79d001acd40..997b2b673cd 100644 --- a/src/EFCore/Storage/TypeMappingInfo.cs +++ b/src/EFCore/Storage/TypeMappingInfo.cs @@ -15,7 +15,7 @@ namespace Microsoft.EntityFrameworkCore.Storage /// /// Describes metadata needed to decide on a type mapping for a property or type. /// - public readonly struct TypeMappingInfo + public readonly struct TypeMappingInfo : IEquatable { private readonly Type _providerClrType; private readonly ValueConverter _customConverter; @@ -153,7 +153,7 @@ public TypeMappingInfo( /// /// The converter to apply. /// The new mapping info. - public TypeMappingInfo WithConverter(ValueConverterInfo converterInfo) + public TypeMappingInfo WithConverter(in ValueConverterInfo converterInfo) => new TypeMappingInfo(this, converterInfo); /// diff --git a/src/EFCore/Storage/TypeMappingSource.cs b/src/EFCore/Storage/TypeMappingSource.cs index 6d2d2bdf984..dc03dc394da 100644 --- a/src/EFCore/Storage/TypeMappingSource.cs +++ b/src/EFCore/Storage/TypeMappingSource.cs @@ -10,12 +10,14 @@ using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Utilities; +#pragma warning disable 1574 +#pragma warning disable CS0419 // Ambiguous reference in cref attribute namespace Microsoft.EntityFrameworkCore.Storage { /// /// /// The base class for non-relational type mapping starting with version 2.1. Non-relational providers - /// should derive from this class and override + /// should derive from this class and override /// /// /// This type is typically used by database providers (and other extensions). It is generally @@ -24,8 +26,8 @@ namespace Microsoft.EntityFrameworkCore.Storage /// public abstract class TypeMappingSource : TypeMappingSourceBase { - private readonly ConcurrentDictionary _explicitMappings - = new ConcurrentDictionary(); + private readonly ConcurrentDictionary<(TypeMappingInfo, Type), CoreTypeMapping> _explicitMappings + = new ConcurrentDictionary<(TypeMappingInfo, Type), CoreTypeMapping>(); /// /// Initializes a new instance of the this class. @@ -37,7 +39,7 @@ protected TypeMappingSource([NotNull] TypeMappingSourceDependencies dependencies } private CoreTypeMapping FindMappingWithConversion( - TypeMappingInfo mappingInfo, + in TypeMappingInfo mappingInfo, [CanBeNull] IProperty property) { Check.NotNull(mappingInfo, nameof(mappingInfo)); @@ -54,33 +56,34 @@ private CoreTypeMapping FindMappingWithConversion( ?.UnwrapNullableType(); var resolvedMapping = _explicitMappings.GetOrAdd( - mappingInfo, + (mappingInfo, providerClrType), k => { - var mapping = providerClrType == null - || providerClrType == mappingInfo.ClrType - ? FindMapping(mappingInfo) + var (info, providerType) = k; + var mapping = providerType == null + || providerType == info.ClrType + ? FindMapping(info) : null; if (mapping == null) { - var sourceType = mappingInfo.ClrType; + var sourceType = info.ClrType; if (sourceType != null) { foreach (var converterInfo in Dependencies .ValueConverterSelector - .Select(sourceType, providerClrType)) + .Select(sourceType, providerType)) { - var mappingInfoUsed = mappingInfo.WithConverter(converterInfo); + var mappingInfoUsed = info.WithConverter(converterInfo); mapping = FindMapping(mappingInfoUsed); if (mapping == null - && providerClrType != null) + && providerType != null) { foreach (var secondConverterInfo in Dependencies .ValueConverterSelector - .Select(providerClrType)) + .Select(providerType)) { mapping = FindMapping(mappingInfoUsed.WithConverter(secondConverterInfo)); @@ -101,15 +104,15 @@ private CoreTypeMapping FindMappingWithConversion( } } - if (mapping != null - && customConverter != null) - { - mapping = mapping.Clone(customConverter); - } - return mapping; }); + if (resolvedMapping != null + && customConverter != null) + { + resolvedMapping = resolvedMapping.Clone(customConverter); + } + ValidateMapping(resolvedMapping, property); return resolvedMapping; diff --git a/src/EFCore/Storage/TypeMappingSourceBase.cs b/src/EFCore/Storage/TypeMappingSourceBase.cs index 1a56e207be2..613f783206c 100644 --- a/src/EFCore/Storage/TypeMappingSourceBase.cs +++ b/src/EFCore/Storage/TypeMappingSourceBase.cs @@ -7,12 +7,14 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Utilities; +#pragma warning disable 1574 +#pragma warning disable CS0419 // Ambiguous reference in cref attribute namespace Microsoft.EntityFrameworkCore.Storage { /// /// /// The base class for non-relational type mapping starting with version 2.1. Non-relational providers - /// should derive from this class and override + /// should derive from this class and override /// /// /// This type is typically used by database providers (and other extensions). It is generally @@ -49,7 +51,7 @@ protected TypeMappingSourceBase([NotNull] TypeMappingSourceDependencies dependen /// /// The mapping info to use to create the mapping. /// The type mapping, or null if none could be found. - protected abstract CoreTypeMapping FindMapping(TypeMappingInfo mappingInfo); + protected abstract CoreTypeMapping FindMapping(in TypeMappingInfo mappingInfo); /// /// Called after a mapping has been found so that it can be validated for the given property. diff --git a/src/EFCore/Storage/ValueConversion/IValueConverterSelector.cs b/src/EFCore/Storage/ValueConversion/IValueConverterSelector.cs index 41af8e929c5..aaebd81ac9d 100644 --- a/src/EFCore/Storage/ValueConversion/IValueConverterSelector.cs +++ b/src/EFCore/Storage/ValueConversion/IValueConverterSelector.cs @@ -23,7 +23,7 @@ public interface IValueConverterSelector /// The store type to target, or null for any. /// The converters available. IEnumerable Select( - [NotNull] Type modelClrType, + [NotNull] Type modelClrType, [CanBeNull] Type providerClrType = null); } } diff --git a/src/Shared/PropertyInfoExtensions.cs b/src/Shared/PropertyInfoExtensions.cs index e16258b1c04..5608235882f 100644 --- a/src/Shared/PropertyInfoExtensions.cs +++ b/src/Shared/PropertyInfoExtensions.cs @@ -18,11 +18,10 @@ public static bool IsStatic(this PropertyInfo property) public static bool IsCandidateProperty(this PropertyInfo propertyInfo, bool needsWrite = true, bool publicOnly = true) => !propertyInfo.IsStatic() - && propertyInfo.GetIndexParameters().Length == 0 && propertyInfo.CanRead && (!needsWrite || propertyInfo.FindSetterProperty() != null) - && propertyInfo.GetMethod != null && (!publicOnly || propertyInfo.GetMethod.IsPublic); - + && propertyInfo.GetMethod != null && (!publicOnly || propertyInfo.GetMethod.IsPublic) + && propertyInfo.GetIndexParameters().Length == 0; public static Type FindCandidateNavigationPropertyType( this PropertyInfo propertyInfo, diff --git a/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs b/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs index 97e13b87039..01a8f0f8b9d 100644 --- a/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs +++ b/test/EFCore.Relational.Tests/TestUtilities/TestRelationalTypeMappingSource.cs @@ -154,7 +154,7 @@ public TestStringTypeMapping( } - protected override RelationalTypeMapping FindMapping(RelationalTypeMappingInfo mappingInfo) + protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) { var clrType = mappingInfo.ClrType; var storeTypeName = mappingInfo.StoreTypeName; diff --git a/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs index 9dd19c3b306..4bde591012c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/EverythingIsBytesSqlServerTest.cs @@ -222,7 +222,7 @@ public SqlServerBytesTypeMappingSource( }; } - protected override RelationalTypeMapping FindMapping(RelationalTypeMappingInfo mappingInfo) + protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) => FindRawMapping(mappingInfo)?.Clone(mappingInfo); private RelationalTypeMapping FindRawMapping(RelationalTypeMappingInfo mappingInfo) diff --git a/test/EFCore.SqlServer.FunctionalTests/EverythingIsStringsSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/EverythingIsStringsSqlServerTest.cs index a83a1bdec08..15cf1d8ff02 100644 --- a/test/EFCore.SqlServer.FunctionalTests/EverythingIsStringsSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/EverythingIsStringsSqlServerTest.cs @@ -243,7 +243,7 @@ public SqlServerStringsTypeMappingSource( }; } - protected override RelationalTypeMapping FindMapping(RelationalTypeMappingInfo mappingInfo) + protected override RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo) => FindRawMapping(mappingInfo)?.Clone(mappingInfo); private RelationalTypeMapping FindRawMapping(RelationalTypeMappingInfo mappingInfo) diff --git a/test/EFCore.Tests/Metadata/Conventions/Internal/NavigationAttributeConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/Internal/NavigationAttributeConventionTest.cs index 073e46b1cbc..f637cf0793a 100644 --- a/test/EFCore.Tests/Metadata/Conventions/Internal/NavigationAttributeConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/Internal/NavigationAttributeConventionTest.cs @@ -16,8 +16,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Xunit; -// ReSharper disable MemberHidesStaticFromOuterClass +// ReSharper disable MemberHidesStaticFromOuterClass // ReSharper disable InconsistentNaming // ReSharper disable ClassNeverInstantiated.Local // ReSharper disable UnusedMember.Local