Skip to content

Commit b7234f9

Browse files
committed
Refactor InternalEntityEntry
Part of #31237
1 parent 9e74f1c commit b7234f9

File tree

221 files changed

+7948
-7376
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

221 files changed

+7948
-7376
lines changed

.github/copilot-instructions.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,9 @@ If you are not sure, do not guess, just tell that you don't know or ask clarifyi
8686

8787
- Follow the existing test patterns in the corresponding test projects
8888
- Create both unit tests and functional tests where appropriate
89-
- Add or modify `SQL` and `C#` baselines for tests when necessary
90-
- When running the tests specify the test project and let it be rebuilt.
89+
- Fix `SQL` and `C#` baselines for tests when necessary by setting the `EF_TEST_REWRITE_BASELINES` env var to `1`
90+
- Before building or running the tests execute `restore.cmd` or `restore.sh` and `activate.ps1` or `activate.sh` to set up the environment
91+
- When running the tests specify the test project and let it be rebuilt by not adding `--no-build`
9192

9293
## Documentation
9394

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

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
77
using Microsoft.EntityFrameworkCore.Design.Internal;
88
using Microsoft.EntityFrameworkCore.Internal;
9-
using Microsoft.EntityFrameworkCore.Metadata;
109
using Microsoft.EntityFrameworkCore.Metadata.Internal;
1110
using Microsoft.EntityFrameworkCore.Query.Internal;
1211

@@ -1498,8 +1497,10 @@ private void
14981497
var variableName = parameters.TargetName;
14991498
var mainBuilder = parameters.MainBuilder;
15001499
var unsafeAccessors = new HashSet<string>();
1500+
var isOnComplexCollection = property.DeclaringType is IReadOnlyComplexType complexType && complexType.ComplexProperty.IsCollection;
15011501

15021502
if (!property.IsShadowProperty()
1503+
&& !isOnComplexCollection
15031504
&& property is not IServiceProperty) // Service properties don't use property accessors
15041505
{
15051506
ClrPropertyGetterFactory.Instance.Create(
@@ -1559,7 +1560,8 @@ private void
15591560
.DecrementIndent();
15601561
}
15611562

1562-
if (property is not IServiceProperty)
1563+
if (property is not IServiceProperty
1564+
&& !isOnComplexCollection)
15631565
{
15641566
PropertyAccessorsFactory.Instance.Create(
15651567
property,
@@ -2783,6 +2785,21 @@ private void CreateAnnotations(
27832785
mainBuilder.AppendLine(";");
27842786
}
27852787

2788+
foreach (var navigation in entityType.GetSkipNavigations())
2789+
{
2790+
var variableName = _code.Identifier(navigation.Name, navigation, parameters.ScopeObjects, capitalize: false);
2791+
2792+
mainBuilder
2793+
.Append($"var {variableName} = ")
2794+
.Append($"{parameters.TargetName}.FindSkipNavigation({_code.Literal(navigation.Name)})");
2795+
if (nullable)
2796+
{
2797+
mainBuilder.Append("!");
2798+
}
2799+
2800+
mainBuilder.AppendLine(";");
2801+
}
2802+
27862803
var runtimeType = (IRuntimeEntityType)entityType;
27872804
var unsafeAccessors = new HashSet<string>();
27882805

@@ -2866,6 +2883,7 @@ private void CreateAnnotations(
28662883
.Append("propertyCount: ").Append(_code.Literal(counts.PropertyCount)).AppendLine(",")
28672884
.Append("navigationCount: ").Append(_code.Literal(counts.NavigationCount)).AppendLine(",")
28682885
.Append("complexPropertyCount: ").Append(_code.Literal(counts.ComplexPropertyCount)).AppendLine(",")
2886+
.Append("complexCollectionCount: ").Append(_code.Literal(counts.ComplexCollectionCount)).AppendLine(",")
28692887
.Append("originalValueCount: ").Append(_code.Literal(counts.OriginalValueCount)).AppendLine(",")
28702888
.Append("shadowCount: ").Append(_code.Literal(counts.ShadowCount)).AppendLine(",")
28712889
.Append("relationshipCount: ").Append(_code.Literal(counts.RelationshipCount)).AppendLine(",")

src/EFCore.Relational/Metadata/Internal/RelationalModel.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Reflection.Metadata;
54
using System.Text;
6-
using System.Text.Json;
75

86
namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
97

@@ -282,7 +280,7 @@ private static void AddDefaultMappings(
282280
principalRootEntityType = ownership.PrincipalEntityType;
283281
}
284282

285-
if (principalRootEntityType.FindDiscriminatorProperty() is not null) // tph
283+
if (principalRootEntityType.FindDiscriminatorProperty() is not null)
286284
{
287285
principalRootEntityType = principalRootEntityType.GetRootType();
288286
}

src/EFCore/ChangeTracking/CollectionEntry.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ public override bool IsModified
109109

110110
if (Metadata is ISkipNavigation skipNavigation)
111111
{
112-
if (InternalEntry.EntityState != EntityState.Unchanged
113-
&& InternalEntry.EntityState != EntityState.Detached)
112+
if (InternalEntityEntry.EntityState != EntityState.Unchanged
113+
&& InternalEntityEntry.EntityState != EntityState.Detached)
114114
{
115115
return true;
116116
}
@@ -231,7 +231,7 @@ public override void Load(LoadOptions options)
231231

232232
if (!IsLoaded)
233233
{
234-
TargetLoader.Load(InternalEntry, options);
234+
TargetLoader.Load(InternalEntityEntry, options);
235235
}
236236
}
237237

@@ -279,7 +279,7 @@ public override Task LoadAsync(LoadOptions options, CancellationToken cancellati
279279

280280
return IsLoaded
281281
? Task.CompletedTask
282-
: TargetLoader.LoadAsync(InternalEntry, options, cancellationToken);
282+
: TargetLoader.LoadAsync(InternalEntityEntry, options, cancellationToken);
283283
}
284284

285285
/// <summary>
@@ -300,11 +300,11 @@ public override IQueryable Query()
300300
{
301301
EnsureInitialized();
302302

303-
return TargetLoader.Query(InternalEntry);
303+
return TargetLoader.Query(InternalEntityEntry);
304304
}
305305

306306
private void EnsureInitialized()
307-
=> InternalEntry.GetOrCreateCollection(Metadata, forMaterialization: true);
307+
=> InternalEntityEntry.GetOrCreateCollection(Metadata, forMaterialization: true);
308308

309309
/// <summary>
310310
/// The <see cref="EntityEntry" /> of an entity this navigation targets.
@@ -332,7 +332,7 @@ private void EnsureInitialized()
332332
[EntityFrameworkInternal]
333333
protected virtual InternalEntityEntry? GetInternalTargetEntry(object entity)
334334
=> CurrentValue == null
335-
|| !InternalEntry.CollectionContains(Metadata, entity)
335+
|| !InternalEntityEntry.CollectionContains(Metadata, entity)
336336
? null
337337
: InternalEntry.StateManager.GetOrCreateEntry(entity, Metadata.TargetEntityType);
338338

src/EFCore/ChangeTracking/CollectionEntry`.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public CollectionEntry(InternalEntityEntry internalEntry, INavigationBase naviga
5959
/// </remarks>
6060
/// <value> An entry for the entity that owns this member. </value>
6161
public new virtual EntityEntry<TEntity> EntityEntry
62-
=> new(InternalEntry);
62+
=> new(InternalEntityEntry);
6363

6464
/// <summary>
6565
/// Gets or sets the value currently assigned to this property. If the current value is set using this property,
@@ -73,7 +73,7 @@ public CollectionEntry(InternalEntityEntry internalEntry, INavigationBase naviga
7373
/// </remarks>
7474
public new virtual IEnumerable<TRelatedEntity>? CurrentValue
7575
{
76-
get => (IEnumerable<TRelatedEntity>?)this.GetInfrastructure().GetCurrentValue(Metadata);
76+
get => (IEnumerable<TRelatedEntity>?)InternalEntry.GetCurrentValue(Metadata);
7777
set => base.CurrentValue = value;
7878
}
7979

@@ -93,7 +93,7 @@ public CollectionEntry(InternalEntityEntry internalEntry, INavigationBase naviga
9393
/// </remarks>
9494
public new virtual IQueryable<TRelatedEntity> Query()
9595
{
96-
InternalEntry.GetOrCreateCollection(Metadata, forMaterialization: true);
96+
InternalEntityEntry.GetOrCreateCollection(Metadata, forMaterialization: true);
9797

9898
return (IQueryable<TRelatedEntity>)base.Query();
9999
}

src/EFCore/ChangeTracking/ComplexPropertyEntry.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public class ComplexPropertyEntry : MemberEntry
2828
/// doing so can result in application failures when updating to a new Entity Framework Core release.
2929
/// </summary>
3030
[EntityFrameworkInternal]
31-
public ComplexPropertyEntry(InternalEntityEntry internalEntry, IComplexProperty complexProperty)
31+
public ComplexPropertyEntry(IInternalEntry internalEntry, IComplexProperty complexProperty)
3232
: base(internalEntry, complexProperty)
3333
{
3434
}
@@ -48,7 +48,7 @@ public ComplexPropertyEntry(InternalEntityEntry internalEntry, IComplexProperty
4848
/// </remarks>
4949
public override bool IsModified
5050
{
51-
get => Metadata.ComplexType.GetFlattenedProperties().Any(property => InternalEntry.IsModified(property));
51+
get => Metadata.ComplexType.GetFlattenedProperties().Any(InternalEntry.IsModified);
5252
set
5353
{
5454
foreach (var property in Metadata.ComplexType.GetFlattenedProperties())

src/EFCore/ChangeTracking/ComplexPropertyEntry`.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public class ComplexPropertyEntry<TEntity, TComplexProperty> : ComplexPropertyEn
3131
/// doing so can result in application failures when updating to a new Entity Framework Core release.
3232
/// </summary>
3333
[EntityFrameworkInternal]
34-
public ComplexPropertyEntry(InternalEntityEntry internalEntry, IComplexProperty complexProperty)
34+
public ComplexPropertyEntry(IInternalEntry internalEntry, IComplexProperty complexProperty)
3535
: base(internalEntry, complexProperty)
3636
{
3737
}
@@ -45,7 +45,7 @@ public ComplexPropertyEntry(InternalEntityEntry internalEntry, IComplexProperty
4545
/// examples.
4646
/// </remarks>
4747
public new virtual EntityEntry<TEntity> EntityEntry
48-
=> new(InternalEntry);
48+
=> new(InternalEntry.EntityEntry);
4949

5050
/// <summary>
5151
/// Gets or sets the value currently assigned to this property. If the current value is set using this property,

src/EFCore/ChangeTracking/Internal/ArrayPropertyValues.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public override void SetValues(PropertyValues propertyValues)
108108
/// doing so can result in application failures when updating to a new Entity Framework Core release.
109109
/// </summary>
110110
public override IReadOnlyList<IProperty> Properties
111-
=> _properties ??= EntityType.GetFlattenedProperties().ToList();
111+
=> _properties ??= [.. EntityType.GetFlattenedProperties()];
112112

113113
/// <summary>
114114
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to

src/EFCore/ChangeTracking/Internal/ChangeDetector.cs

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public ChangeDetector(
3838
/// any release. You should only use it directly in your code with extreme caution and knowing that
3939
/// doing so can result in application failures when updating to a new Entity Framework Core release.
4040
/// </summary>
41-
public virtual void PropertyChanged(InternalEntityEntry entry, IPropertyBase propertyBase, bool setModified)
41+
public virtual void PropertyChanged(IInternalEntry entry, IPropertyBase propertyBase, bool setModified)
4242
{
4343
if (entry.EntityState == EntityState.Detached
4444
|| propertyBase is IServiceProperty)
@@ -62,16 +62,16 @@ public virtual void PropertyChanged(InternalEntityEntry entry, IPropertyBase pro
6262
else if (propertyBase.GetRelationshipIndex() != -1
6363
&& propertyBase is INavigationBase navigation)
6464
{
65-
DetectNavigationChange(entry, navigation);
65+
DetectNavigationChange((InternalEntityEntry)entry, navigation);
6666
}
6767
}
6868

69-
private static void ThrowIfKeyChanged(InternalEntityEntry entry, IProperty property)
69+
private static void ThrowIfKeyChanged(IInternalEntry entry, IProperty property)
7070
{
7171
if (property.IsKey()
7272
&& property.GetAfterSaveBehavior() == PropertySaveBehavior.Throw)
7373
{
74-
throw new InvalidOperationException(CoreStrings.KeyReadOnly(property.Name, entry.EntityType.DisplayName()));
74+
throw new InvalidOperationException(CoreStrings.KeyReadOnly(property.Name, entry.StructuralType.DisplayName()));
7575
}
7676
}
7777

@@ -81,15 +81,15 @@ private static void ThrowIfKeyChanged(InternalEntityEntry entry, IProperty prope
8181
/// any release. You should only use it directly in your code with extreme caution and knowing that
8282
/// doing so can result in application failures when updating to a new Entity Framework Core release.
8383
/// </summary>
84-
public virtual void PropertyChanging(InternalEntityEntry entry, IPropertyBase propertyBase)
84+
public virtual void PropertyChanging(IInternalEntry entry, IPropertyBase propertyBase)
8585
{
8686
if (entry.EntityState == EntityState.Detached
8787
|| propertyBase is IServiceProperty)
8888
{
8989
return;
9090
}
9191

92-
if (!entry.EntityType.UseEagerSnapshots())
92+
if (!entry.StructuralType.UseEagerSnapshots())
9393
{
9494
if (propertyBase is IProperty asProperty
9595
&& asProperty.GetOriginalValueIndex() != -1)
@@ -99,7 +99,7 @@ public virtual void PropertyChanging(InternalEntityEntry entry, IPropertyBase pr
9999

100100
if (propertyBase.GetRelationshipIndex() != -1)
101101
{
102-
entry.EnsureRelationshipSnapshot();
102+
((InternalEntityEntry)entry).EnsureRelationshipSnapshot();
103103
}
104104
}
105105
}
@@ -183,35 +183,27 @@ public virtual void DetectChanges(InternalEntityEntry entry)
183183
}
184184
}
185185

186-
private bool DetectChanges(InternalEntityEntry entry, HashSet<InternalEntityEntry> visited)
186+
private void DetectChanges(InternalEntityEntry entry, HashSet<InternalEntityEntry> visited)
187187
{
188-
var changesFound = false;
189-
190-
if (entry.EntityState != EntityState.Detached)
188+
if (entry.EntityState == EntityState.Detached)
191189
{
192-
foreach (var foreignKey in entry.EntityType.GetForeignKeys())
193-
{
194-
var principalEntry = entry.StateManager.FindPrincipal(entry, foreignKey);
195-
196-
if (principalEntry != null
197-
&& !visited.Contains(principalEntry))
198-
{
199-
visited.Add(principalEntry);
190+
return;
191+
}
200192

201-
if (DetectChanges(principalEntry, visited))
202-
{
203-
changesFound = true;
204-
}
205-
}
206-
}
193+
foreach (var foreignKey in entry.EntityType.GetForeignKeys())
194+
{
195+
var principalEntry = entry.StateManager.FindPrincipal(entry, foreignKey);
207196

208-
if (LocalDetectChanges(entry))
197+
if (principalEntry != null
198+
&& !visited.Contains(principalEntry))
209199
{
210-
changesFound = true;
200+
visited.Add(principalEntry);
201+
202+
DetectChanges(principalEntry, visited);
211203
}
212204
}
213205

214-
return changesFound;
206+
LocalDetectChanges(entry);
215207
}
216208

217209
private bool LocalDetectChanges(InternalEntityEntry entry)
@@ -274,7 +266,7 @@ private bool LocalDetectChanges(InternalEntityEntry entry)
274266
/// any release. You should only use it directly in your code with extreme caution and knowing that
275267
/// doing so can result in application failures when updating to a new Entity Framework Core release.
276268
/// </summary>
277-
public bool DetectValueChange(InternalEntityEntry entry, IProperty property)
269+
public bool DetectValueChange(IInternalEntry entry, IProperty property)
278270
{
279271
var current = entry[property];
280272
var original = entry.GetOriginalValue(property);
@@ -296,26 +288,33 @@ public bool DetectValueChange(InternalEntityEntry entry, IProperty property)
296288
return false;
297289
}
298290

299-
private void LogChangeDetected(InternalEntityEntry entry, IProperty property, object? original, object? current)
291+
private void LogChangeDetected(IInternalEntry entry, IProperty property, object? original, object? current)
300292
{
301293
if (_loggingOptions.IsSensitiveDataLoggingEnabled)
302294
{
303-
_logger.PropertyChangeDetectedSensitive(entry, property, original, current);
295+
if (entry is InternalEntityEntry entityEntry)
296+
{
297+
_logger.PropertyChangeDetectedSensitive(entityEntry, property, original, current);
298+
}
304299
}
305300
else
306301
{
307-
_logger.PropertyChangeDetected(entry, property, original, current);
302+
if (entry is InternalEntityEntry entityEntry)
303+
{
304+
_logger.PropertyChangeDetected(entityEntry, property, original, current);
305+
}
308306
}
309307
}
310308

311-
private bool DetectKeyChange(InternalEntityEntry entry, IProperty property)
309+
private bool DetectKeyChange(IInternalEntry entry, IProperty property)
312310
{
313311
if (property.GetRelationshipIndex() < 0)
314312
{
315313
return false;
316314
}
317315

318-
var snapshotValue = entry.GetRelationshipSnapshotValue(property);
316+
var entityEntry = (InternalEntityEntry)entry;
317+
var snapshotValue = entityEntry.GetRelationshipSnapshotValue(property);
319318
var currentValue = entry[property];
320319

321320
var comparer = property.GetKeyValueComparer();
@@ -326,19 +325,19 @@ private bool DetectKeyChange(InternalEntityEntry entry, IProperty property)
326325
{
327326
var keys = property.GetContainingKeys();
328327
var foreignKeys = property.GetContainingForeignKeys()
329-
.Where(fk => fk.DeclaringEntityType.IsAssignableFrom(entry.EntityType));
328+
.Where(fk => fk.DeclaringEntityType.IsAssignableFrom(entityEntry.EntityType));
330329

331330
if (_loggingOptions.IsSensitiveDataLoggingEnabled)
332331
{
333-
_logger.ForeignKeyChangeDetectedSensitive(entry, property, snapshotValue, currentValue);
332+
_logger.ForeignKeyChangeDetectedSensitive(entityEntry, property, snapshotValue, currentValue);
334333
}
335334
else
336335
{
337-
_logger.ForeignKeyChangeDetected(entry, property, snapshotValue, currentValue);
336+
_logger.ForeignKeyChangeDetected(entityEntry, property, snapshotValue, currentValue);
338337
}
339338

340-
entry.StateManager.InternalEntityEntryNotifier.KeyPropertyChanged(
341-
entry, property, keys, foreignKeys, snapshotValue, currentValue);
339+
entityEntry.StateManager.InternalEntityEntryNotifier.KeyPropertyChanged(
340+
entityEntry, property, keys, foreignKeys, snapshotValue, currentValue);
342341

343342
return true;
344343
}
@@ -487,7 +486,6 @@ public virtual void SetEvents(
487486
public virtual void OnDetectingEntityChanges(InternalEntityEntry internalEntityEntry)
488487
{
489488
var @event = DetectingEntityChanges;
490-
491489
if (@event != null)
492490
{
493491
var changeTracker = internalEntityEntry.StateManager.Context.ChangeTracker;

0 commit comments

Comments
 (0)