Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/8.0-rc1] Change tracking API for complex types #31472

Merged
merged 1 commit into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 152 additions & 0 deletions src/EFCore/ChangeTracking/ComplexPropertyEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

namespace Microsoft.EntityFrameworkCore.ChangeTracking;

/// <summary>
/// Provides access to change tracking information and operations for a given property of a complex type.
/// </summary>
/// <remarks>
/// <para>
/// Instances of this class are returned from methods when using the <see cref="ChangeTracker" /> API and it is
/// not designed to be directly constructed in your application code.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </para>
/// </remarks>
public class ComplexPropertyEntry : MemberEntry
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal]
public ComplexPropertyEntry(InternalEntityEntry internalEntry, IComplexProperty complexProperty)
: base(internalEntry, complexProperty)
{
}

/// <summary>
/// Gets or sets a value indicating whether any of the properties of the complex type have been modified
/// and should be updated in the database when <see cref="DbContext.SaveChanges()" /> is called.
/// </summary>
/// <remarks>
/// <para>
/// Setting this value causes all of the properties of the complex type to be marked as modified or not as appropriate.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </para>
/// </remarks>
public override bool IsModified
{
get => Metadata.ComplexType.GetFlattenedProperties().Any(property => InternalEntry.IsModified(property));
set
{
foreach (var property in Metadata.ComplexType.GetFlattenedProperties())
{
InternalEntry.SetPropertyModified(property, isModified: value);
}
}
}

/// <summary>
/// Gets the metadata that describes the facets of this property and how it maps to the database.
/// </summary>
public new virtual IComplexProperty Metadata
=> (IComplexProperty)base.Metadata;

/// <summary>
/// Provides access to change tracking information and operations for a given property of this complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
/// <param name="property">The property to access information and operations for.</param>
/// <returns>An object that exposes change tracking information and operations for the given property.</returns>
public virtual PropertyEntry Property(IProperty property)
{
Check.NotNull(property, nameof(property));

return new PropertyEntry(InternalEntry, property);
}

/// <summary>
/// Provides access to change tracking information and operations for a given property of this complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
/// <param name="propertyName">The property to access information and operations for.</param>
/// <returns>An object that exposes change tracking information and operations for the given property.</returns>
public virtual PropertyEntry Property(string propertyName)
{
Check.NotEmpty(propertyName, nameof(propertyName));

return new PropertyEntry(InternalEntry, Metadata.ComplexType.GetProperty(propertyName));
}

/// <summary>
/// Provides access to change tracking information and operations for all properties of this complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
public virtual IEnumerable<PropertyEntry> Properties
=> Metadata.ComplexType.GetProperties().Select(property => new PropertyEntry(InternalEntry, property));

/// <summary>
/// Provides access to change tracking information and operations for a given property of a nested complex type on this
/// complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
/// <param name="property">The property to access information and operations for.</param>
/// <returns>An object that exposes change tracking information and operations for the given property.</returns>
public virtual ComplexPropertyEntry ComplexProperty(IComplexProperty property)
{
Check.NotNull(property, nameof(property));

return new ComplexPropertyEntry(InternalEntry, property);
}

/// <summary>
/// Provides access to change tracking information and operations for a given property of a nested complex type on this
/// complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
/// <param name="propertyName">The property to access information and operations for.</param>
/// <returns>An object that exposes change tracking information and operations for the given property.</returns>
public virtual ComplexPropertyEntry ComplexProperty(string propertyName)
{
Check.NotEmpty(propertyName, nameof(propertyName));

return new ComplexPropertyEntry(InternalEntry, Metadata.ComplexType.GetComplexProperty(propertyName));
}

/// <summary>
/// Provides access to change tracking information and operations for all properties of nested complex types on this complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
public virtual IEnumerable<ComplexPropertyEntry> ComplexProperties
=> Metadata.ComplexType.GetComplexProperties().Select(property => new ComplexPropertyEntry(InternalEntry, property));
}
198 changes: 198 additions & 0 deletions src/EFCore/ChangeTracking/ComplexPropertyEntry`.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

namespace Microsoft.EntityFrameworkCore.ChangeTracking;

/// <summary>
/// Provides access to change tracking information and operations for a given property of a complex type.
/// </summary>
/// <remarks>
/// <para>
/// Instances of this class are returned from methods when using the <see cref="ChangeTracker" /> API and it is
/// not designed to be directly constructed in your application code.
/// </para>
/// <para>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </para>
/// </remarks>
/// <typeparam name="TEntity">The type of the entity type that contains the property.</typeparam>
/// <typeparam name="TComplexProperty">The type of the property.</typeparam>
public class ComplexPropertyEntry<TEntity, TComplexProperty> : ComplexPropertyEntry
where TEntity : class
{
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal]
public ComplexPropertyEntry(InternalEntityEntry internalEntry, IComplexProperty complexProperty)
: base(internalEntry, complexProperty)
{
}

/// <summary>
/// The <see cref="EntityEntry{TEntity}" /> to which this member belongs.
/// </summary>
/// <value> An entry for the entity that owns this member. </value>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
public new virtual EntityEntry<TEntity> EntityEntry
=> new(InternalEntry);

/// <summary>
/// Gets or sets the value currently assigned to this property. If the current value is set using this property,
/// the change tracker is aware of the change and <see cref="ChangeTracker.DetectChanges" /> is not required
/// for the context to detect the change.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
public new virtual TComplexProperty CurrentValue
{
get => InternalEntry.GetCurrentValue<TComplexProperty>(Metadata);
set => base.CurrentValue = value;
}

/// <summary>
/// Provides access to change tracking information and operations for a given property of this complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
/// <param name="propertyExpression">
/// A lambda expression representing the property to access information and operations for.
/// </param>
/// <returns>An object that exposes change tracking information and operations for the given property.</returns>
public virtual PropertyEntry<TEntity, TProperty> Property<TProperty>(
Expression<Func<TComplexProperty, TProperty>> propertyExpression)
{
Check.NotNull(propertyExpression, nameof(propertyExpression));

return new PropertyEntry<TEntity, TProperty>(
InternalEntry,
Metadata.ComplexType.GetProperty(propertyExpression.GetMemberAccess().GetSimpleMemberName()));
}

/// <summary>
/// Provides access to change tracking information and operations for a given complex type property of this complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
/// <param name="propertyExpression">
/// A lambda expression representing the property to access information and operations for.
/// </param>
/// <returns>An object that exposes change tracking information and operations for the given property.</returns>
public virtual ComplexPropertyEntry<TEntity, TNestedComplexProperty> ComplexProperty<TNestedComplexProperty>(
Expression<Func<TComplexProperty, TNestedComplexProperty>> propertyExpression)
{
Check.NotNull(propertyExpression, nameof(propertyExpression));

return new ComplexPropertyEntry<TEntity, TNestedComplexProperty>(
InternalEntry,
Metadata.ComplexType.GetComplexProperty(propertyExpression.GetMemberAccess().GetSimpleMemberName()));
}

/// <summary>
/// Provides access to change tracking information and operations for a given property of this complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="property">The property to access information and operations for.</param>
/// <returns>An object that exposes change tracking information and operations for the given property.</returns>
public virtual PropertyEntry<TEntity, TProperty> Property<TProperty>(IProperty property)
{
Check.NotNull(property, nameof(property));

ValidateType<TProperty>(property);

return new PropertyEntry<TEntity, TProperty>(InternalEntry, property);
}

/// <summary>
/// Provides access to change tracking information and operations for a given complex type property of this complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
/// <typeparam name="TNestedComplexProperty">The type of the property.</typeparam>
/// <param name="complexProperty">The property to access information and operations for.</param>
/// <returns>An object that exposes change tracking information and operations for the given property.</returns>
public virtual ComplexPropertyEntry<TEntity, TNestedComplexProperty> ComplexProperty<TNestedComplexProperty>(
IComplexProperty complexProperty)
{
Check.NotNull(complexProperty, nameof(complexProperty));

ValidateType<TNestedComplexProperty>(complexProperty);

return new ComplexPropertyEntry<TEntity, TNestedComplexProperty>(InternalEntry, complexProperty);
}

/// <summary>
/// Provides access to change tracking information and operations for a given property of this complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="propertyName">The property to access information and operations for.</param>
/// <returns>An object that exposes change tracking information and operations for the given property.</returns>
public virtual PropertyEntry<TEntity, TProperty> Property<TProperty>(string propertyName)
{
Check.NotEmpty(propertyName, nameof(propertyName));

ValidateType<TProperty>(Metadata.ComplexType.FindProperty(propertyName));

return new PropertyEntry<TEntity, TProperty>(InternalEntry, Metadata.ComplexType.GetProperty(propertyName));
}

/// <summary>
/// Provides access to change tracking information and operations for a given complex type property of this complex type.
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-entity-entries">Accessing tracked entities in EF Core</see> for more information and
/// examples.
/// </remarks>
/// <typeparam name="TNestedComplexProperty">The type of the property.</typeparam>
/// <param name="propertyName">The property to access information and operations for.</param>
/// <returns>An object that exposes change tracking information and operations for the given property.</returns>
public virtual ComplexPropertyEntry<TEntity, TNestedComplexProperty> ComplexProperty<TNestedComplexProperty>(string propertyName)
{
Check.NotEmpty(propertyName, nameof(propertyName));

ValidateType<TNestedComplexProperty>(Metadata.ComplexType.FindComplexProperty(propertyName));

return new ComplexPropertyEntry<TEntity, TNestedComplexProperty>(
InternalEntry, Metadata.ComplexType.GetComplexProperty(propertyName));
}

private static void ValidateType<TProperty>(IPropertyBase? property)
{
if (property != null
&& property.ClrType != typeof(TProperty))
{
throw new ArgumentException(
CoreStrings.WrongGenericPropertyType(
property.Name,
property.DeclaringType.ClrType.ShortDisplayName(),
property.ClrType.ShortDisplayName(),
typeof(TProperty).ShortDisplayName()));
}
}
}
Loading
Loading