Skip to content

Commit 15af73b

Browse files
authored
Add helpful exception when a property’s backing field cannot be found. (#36720)
Fixes #35219
1 parent a707320 commit 15af73b

5 files changed

Lines changed: 63 additions & 9 deletions

File tree

src/EFCore.Design/Scaffolding/Internal/CSharpRuntimeModelCodeGenerator.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1769,7 +1769,15 @@ private void RegisterPrivateAccessors(
17691769
Dictionary<Type, HashSet<MemberInfo>> unsafeAccessorTypes,
17701770
ref Dictionary<MemberInfo, QualifiedName>? memberAccessReplacements)
17711771
{
1772-
var member = property.GetMemberInfo(forMaterialization, forSet);
1772+
if (!property.TryGetMemberInfo(forMaterialization, forSet, out var member, out var error))
1773+
{
1774+
throw new InvalidOperationException(error);
1775+
}
1776+
1777+
if (member == null)
1778+
{
1779+
return null;
1780+
}
17731781
switch (member)
17741782
{
17751783
case FieldInfo field:
@@ -1799,6 +1807,7 @@ private void RegisterPrivateAccessors(
17991807
}
18001808

18011809
memberAccessReplacements ??= [];
1810+
18021811
var methodName = LinqToCSharpSyntaxTranslator.GetUnsafeAccessorName(member);
18031812

18041813
var declaringType = member.DeclaringType!;

test/EFCore.Cosmos.FunctionalTests/Scaffolding/CompiledModelCosmosTest.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ protected override BuildSource AddReferences(BuildSource build, [CallerFilePath]
689689
IEnumerable<ScaffoldedFile>? additionalSourceFiles = null,
690690
Action<Assembly>? assertAssembly = null,
691691
string? expectedExceptionMessage = null,
692+
bool skipValidation = false,
692693
[CallerMemberName] string testName = "")
693694
where TContext : class
694695
=> base.Test(
@@ -706,5 +707,6 @@ protected override BuildSource AddReferences(BuildSource build, [CallerFilePath]
706707
additionalSourceFiles,
707708
assertAssembly,
708709
expectedExceptionMessage,
710+
skipValidation,
709711
testName);
710712
}

test/EFCore.Specification.Tests/NonSharedModelTestBase.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ protected virtual ContextFactory<TContext> CreateContextFactory<TContext>(
8686
Func<string, bool>? shouldLogCategory = null,
8787
Func<TestStore>? createTestStore = null,
8888
bool usePooling = true,
89-
bool useServiceProvider = true)
89+
bool useServiceProvider = true,
90+
bool skipValidation = false)
9091
where TContext : DbContext
9192
{
9293
if (createTestStore != null)
@@ -110,7 +111,7 @@ protected virtual ContextFactory<TContext> CreateContextFactory<TContext>(
110111

111112
if (onModelCreating != null)
112113
{
113-
services = services.AddSingleton(TestModelSource.GetFactory(onModelCreating, configureConventions));
114+
services = services.AddSingleton(TestModelSource.GetFactory(onModelCreating, configureConventions, skipValidation));
114115
}
115116

116117
addServices?.Invoke(services);

test/EFCore.Specification.Tests/Scaffolding/CompiledModelTestBase.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1167,6 +1167,21 @@ public virtual Task ComplexTypes()
11671167
},
11681168
options: new CompiledModelCodeGenerationOptions { UseNullableReferenceTypes = true, ForNativeAot = true });
11691169

1170+
[ConditionalFact]
1171+
public virtual Task Throws_for_Backing_Field_Not_Found()
1172+
=> Test(
1173+
modelBuilder =>
1174+
{
1175+
modelBuilder.Entity<EntityWithNoBackingFieldScalar>(eb =>
1176+
{
1177+
eb.Property(e => e.Computed)
1178+
.UsePropertyAccessMode(PropertyAccessMode.FieldDuringConstruction);
1179+
});
1180+
},
1181+
options: new CompiledModelCodeGenerationOptions { ForNativeAot = true },
1182+
expectedExceptionMessage: CoreStrings.NoFieldOrSetter("Computed", "EntityWithNoBackingFieldScalar"),
1183+
skipValidation: true);
1184+
11701185
protected virtual void BuildComplexTypesModel(ModelBuilder modelBuilder)
11711186
{
11721187
modelBuilder.Entity<PrincipalBase>(eb =>
@@ -1790,6 +1805,12 @@ public class Data
17901805
public byte[]? Blob { get; set; }
17911806
}
17921807

1808+
public class EntityWithNoBackingFieldScalar
1809+
{
1810+
public int Id { get; set; }
1811+
public int Computed => 1;
1812+
}
1813+
17931814
public class PrincipalBase : AbstractBase
17941815
{
17951816
public new long? Id { get; set; }
@@ -2007,6 +2028,7 @@ protected virtual Task Test(
20072028
IEnumerable<ScaffoldedFile>? additionalSourceFiles = null,
20082029
Action<Assembly>? assertAssembly = null,
20092030
string? expectedExceptionMessage = null,
2031+
bool skipValidation = false,
20102032
[CallerMemberName] string testName = "")
20112033
=> Test<DbContext>(
20122034
onModelCreating,
@@ -2019,6 +2041,7 @@ protected virtual Task Test(
20192041
additionalSourceFiles,
20202042
assertAssembly,
20212043
expectedExceptionMessage,
2044+
skipValidation,
20222045
testName);
20232046

20242047
protected virtual async Task<(TContext?, IModel?)> Test<TContext>(
@@ -2032,6 +2055,7 @@ protected virtual Task Test(
20322055
IEnumerable<ScaffoldedFile>? additionalSourceFiles = null,
20332056
Action<Assembly>? assertAssembly = null,
20342057
string? expectedExceptionMessage = null,
2058+
bool skipValidation = false,
20352059
[CallerMemberName] string testName = "")
20362060
where TContext : DbContext
20372061
{
@@ -2044,7 +2068,8 @@ protected virtual Task Test(
20442068
onModelCreating?.Invoke(modelBuilder);
20452069
},
20462070
onConfiguring,
2047-
addServices);
2071+
addServices,
2072+
skipValidation: skipValidation);
20482073
using var context = contextFactory.CreateContext();
20492074
var model = context.GetService<IDesignTimeModel>().Model;
20502075

test/EFCore.Specification.Tests/TestUtilities/TestModelSource.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,28 @@ public class TestModelSource : ModelSource
99
{
1010
private readonly Action<ModelConfigurationBuilder>? _configureConventions;
1111
private readonly Action<ModelBuilder, DbContext> _onModelCreating;
12+
private readonly bool _skipValidation;
1213

1314
private TestModelSource(
1415
Action<ModelConfigurationBuilder>? configureConventions,
1516
Action<ModelBuilder, DbContext> onModelCreating,
16-
ModelSourceDependencies dependencies)
17+
ModelSourceDependencies dependencies,
18+
bool skipValidation = false)
1719
: base(dependencies)
1820
{
1921
_configureConventions = configureConventions;
2022
_onModelCreating = onModelCreating;
23+
_skipValidation = skipValidation;
24+
}
25+
26+
public override IModel CreateModel(
27+
DbContext context,
28+
ModelCreationDependencies modelCreationDependencies,
29+
bool designTime)
30+
{
31+
var model = CreateModel(context, modelCreationDependencies.ConventionSetBuilder, modelCreationDependencies.ModelDependencies);
32+
return modelCreationDependencies.ModelRuntimeInitializer.Initialize(
33+
model, designTime, _skipValidation ? null : modelCreationDependencies.ValidationLogger);
2134
}
2235

2336
protected override IModel CreateModel(
@@ -41,17 +54,21 @@ protected override IModel CreateModel(
4154

4255
public static Func<IServiceProvider, IModelSource> GetFactory(
4356
Action<ModelBuilder> onModelCreating,
44-
Action<ModelConfigurationBuilder>? configureConventions = null)
57+
Action<ModelConfigurationBuilder>? configureConventions = null,
58+
bool skipValidation = false)
4559
=> p => new TestModelSource(
4660
configureConventions,
4761
(mb, c) => onModelCreating(mb),
48-
p.GetRequiredService<ModelSourceDependencies>());
62+
p.GetRequiredService<ModelSourceDependencies>(),
63+
skipValidation);
4964

5065
public static Func<IServiceProvider, IModelSource> GetFactory(
5166
Action<ModelBuilder, DbContext> onModelCreating,
52-
Action<ModelConfigurationBuilder>? configureConventions = null)
67+
Action<ModelConfigurationBuilder>? configureConventions = null,
68+
bool skipValidation = false)
5369
=> p => new TestModelSource(
5470
configureConventions,
5571
onModelCreating,
56-
p.GetRequiredService<ModelSourceDependencies>());
72+
p.GetRequiredService<ModelSourceDependencies>(),
73+
skipValidation);
5774
}

0 commit comments

Comments
 (0)