diff --git a/src/EFCore/Metadata/Conventions/BackingFieldConvention.cs b/src/EFCore/Metadata/Conventions/BackingFieldConvention.cs index 72f29808dd5..7afd542d55b 100644 --- a/src/EFCore/Metadata/Conventions/BackingFieldConvention.cs +++ b/src/EFCore/Metadata/Conventions/BackingFieldConvention.cs @@ -84,7 +84,8 @@ public virtual void ProcessNavigationAdded( private FieldInfo GetFieldToSet(IConventionPropertyBase propertyBase) { if (propertyBase == null - || !ConfigurationSource.Convention.Overrides(propertyBase.GetFieldInfoConfigurationSource())) + || !ConfigurationSource.Convention.Overrides(propertyBase.GetFieldInfoConfigurationSource()) + || propertyBase.IsIndexerProperty()) { return null; } diff --git a/src/EFCore/Metadata/Internal/PropertyBase.cs b/src/EFCore/Metadata/Internal/PropertyBase.cs index 3a3f6f6b789..4c96274ac7b 100644 --- a/src/EFCore/Metadata/Internal/PropertyBase.cs +++ b/src/EFCore/Metadata/Internal/PropertyBase.cs @@ -151,6 +151,13 @@ public virtual void SetField([CanBeNull] FieldInfo fieldInfo, ConfigurationSourc if (fieldInfo != null) { IsCompatible(fieldInfo, ClrType, DeclaringType.ClrType, Name, shouldThrow: true); + + if (PropertyInfo != null + && PropertyInfo.IsIndexerProperty()) + { + throw new InvalidOperationException( + CoreStrings.BackingFieldOnIndexer(fieldInfo.GetSimpleMemberName(), DeclaringType.DisplayName(), Name)); + } } if (PropertyInfo == null diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs index 25169f966c4..28e9e671be8 100644 --- a/src/EFCore/Properties/CoreStrings.Designer.cs +++ b/src/EFCore/Properties/CoreStrings.Designer.cs @@ -2316,6 +2316,14 @@ public static string NonIndexerEntityType([CanBeNull] object property, [CanBeNul GetString("NonIndexerEntityType", nameof(property), nameof(entity), nameof(type)), property, entity, type); + /// + /// Cannot set backing field '{field}' for the indexer property '{entityType}.{property}'. Indexer properties are not allowed to use a backing field. + /// + public static string BackingFieldOnIndexer([CanBeNull] object field, [CanBeNull] object entityType, [CanBeNull] object property) + => string.Format( + GetString("BackingFieldOnIndexer", nameof(field), nameof(entityType), nameof(property)), + field, entityType, property); + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx index 54d2bba2744..724a621090a 100644 --- a/src/EFCore/Properties/CoreStrings.resx +++ b/src/EFCore/Properties/CoreStrings.resx @@ -1248,4 +1248,7 @@ Cannot add property '{property}' on entity type '{entity}' since there is no indexer on '{entity}' taking a single argument of type '{type}'. + + Cannot set backing field '{field}' for the indexer property '{entityType}.{property}'. Indexer properties are not allowed to use a backing field. + \ No newline at end of file diff --git a/test/EFCore.Tests/Metadata/Conventions/BackingFieldConventionTest.cs b/test/EFCore.Tests/Metadata/Conventions/BackingFieldConventionTest.cs index 87e1c52a244..c0120968760 100644 --- a/test/EFCore.Tests/Metadata/Conventions/BackingFieldConventionTest.cs +++ b/test/EFCore.Tests/Metadata/Conventions/BackingFieldConventionTest.cs @@ -241,6 +241,29 @@ public void FieldInfo_set_by_annotation_is_used() Assert.Equal("m_onTheRun", property.GetFieldName()); } + [ConditionalFact] + public void Backing_field_is_not_discovered_for_indexer_property() + { + var entityType = CreateModel().AddEntityType(typeof(IndexedClass)); + var property = entityType.AddIndexedProperty("Nation", typeof(string)); + + RunConvention(property); + Validate(property); + + Assert.Null(property.GetFieldName()); + } + + [ConditionalFact] + public void Setting_field_on_indexer_property_throws() + { + var entityType = CreateModel().AddEntityType(typeof(IndexedClass)); + var property = entityType.AddIndexedProperty("Nation", typeof(string)); + + Assert.Equal( + CoreStrings.BackingFieldOnIndexer("nation", entityType.DisplayName(), "Nation"), + Assert.Throws(() => property.SetField("nation")).Message); + } + private void RunConvention(IMutableProperty property) => new BackingFieldConvention(CreateDependencies()) .ProcessPropertyAdded( @@ -459,6 +482,17 @@ public object OnTheRun } } + private class IndexedClass + { + private string nation; + private string _nation; + private string _Nation; + private string m_nation; + private string m_Nation; + + public object this[string name] => null; + } + #pragma warning disable RCS1222 // Merge preprocessor directives. #pragma warning restore 649, 169 #pragma warning restore IDE0027 // Use expression body for accessors