diff --git a/src/EFCore/ChangeTracking/ComplexPropertyEntry.cs b/src/EFCore/ChangeTracking/ComplexPropertyEntry.cs new file mode 100644 index 00000000000..4ca3ec5c003 --- /dev/null +++ b/src/EFCore/ChangeTracking/ComplexPropertyEntry.cs @@ -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; + +/// +/// Provides access to change tracking information and operations for a given property of a complex type. +/// +/// +/// +/// Instances of this class are returned from methods when using the API and it is +/// not designed to be directly constructed in your application code. +/// +/// +/// See Accessing tracked entities in EF Core for more information and +/// examples. +/// +/// +public class ComplexPropertyEntry : MemberEntry +{ + /// + /// 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. + /// + [EntityFrameworkInternal] + public ComplexPropertyEntry(InternalEntityEntry internalEntry, IComplexProperty complexProperty) + : base(internalEntry, complexProperty) + { + } + + /// + /// 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 is called. + /// + /// + /// + /// Setting this value causes all of the properties of the complex type to be marked as modified or not as appropriate. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// + 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); + } + } + } + + /// + /// Gets the metadata that describes the facets of this property and how it maps to the database. + /// + public new virtual IComplexProperty Metadata + => (IComplexProperty)base.Metadata; + + /// + /// Provides access to change tracking information and operations for a given property of this complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual PropertyEntry Property(IProperty property) + { + Check.NotNull(property, nameof(property)); + + return new PropertyEntry(InternalEntry, property); + } + + /// + /// Provides access to change tracking information and operations for a given property of this complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual PropertyEntry Property(string propertyName) + { + Check.NotEmpty(propertyName, nameof(propertyName)); + + return new PropertyEntry(InternalEntry, Metadata.ComplexType.GetProperty(propertyName)); + } + + /// + /// Provides access to change tracking information and operations for all properties of this complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + public virtual IEnumerable Properties + => Metadata.ComplexType.GetProperties().Select(property => new PropertyEntry(InternalEntry, property)); + + /// + /// Provides access to change tracking information and operations for a given property of a nested complex type on this + /// complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual ComplexPropertyEntry ComplexProperty(IComplexProperty property) + { + Check.NotNull(property, nameof(property)); + + return new ComplexPropertyEntry(InternalEntry, property); + } + + /// + /// Provides access to change tracking information and operations for a given property of a nested complex type on this + /// complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual ComplexPropertyEntry ComplexProperty(string propertyName) + { + Check.NotEmpty(propertyName, nameof(propertyName)); + + return new ComplexPropertyEntry(InternalEntry, Metadata.ComplexType.GetComplexProperty(propertyName)); + } + + /// + /// Provides access to change tracking information and operations for all properties of nested complex types on this complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + public virtual IEnumerable ComplexProperties + => Metadata.ComplexType.GetComplexProperties().Select(property => new ComplexPropertyEntry(InternalEntry, property)); +} diff --git a/src/EFCore/ChangeTracking/ComplexPropertyEntry`.cs b/src/EFCore/ChangeTracking/ComplexPropertyEntry`.cs new file mode 100644 index 00000000000..aacc6f56626 --- /dev/null +++ b/src/EFCore/ChangeTracking/ComplexPropertyEntry`.cs @@ -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; + +/// +/// Provides access to change tracking information and operations for a given property of a complex type. +/// +/// +/// +/// Instances of this class are returned from methods when using the API and it is +/// not designed to be directly constructed in your application code. +/// +/// +/// See Accessing tracked entities in EF Core for more information and +/// examples. +/// +/// +/// The type of the entity type that contains the property. +/// The type of the property. +public class ComplexPropertyEntry : ComplexPropertyEntry + where TEntity : class +{ + /// + /// 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. + /// + [EntityFrameworkInternal] + public ComplexPropertyEntry(InternalEntityEntry internalEntry, IComplexProperty complexProperty) + : base(internalEntry, complexProperty) + { + } + + /// + /// The to which this member belongs. + /// + /// An entry for the entity that owns this member. + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + public new virtual EntityEntry EntityEntry + => new(InternalEntry); + + /// + /// 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 is not required + /// for the context to detect the change. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + public new virtual TComplexProperty CurrentValue + { + get => InternalEntry.GetCurrentValue(Metadata); + set => base.CurrentValue = value; + } + + /// + /// Provides access to change tracking information and operations for a given property of this complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// + /// A lambda expression representing the property to access information and operations for. + /// + /// An object that exposes change tracking information and operations for the given property. + public virtual PropertyEntry Property( + Expression> propertyExpression) + { + Check.NotNull(propertyExpression, nameof(propertyExpression)); + + return new PropertyEntry( + InternalEntry, + Metadata.ComplexType.GetProperty(propertyExpression.GetMemberAccess().GetSimpleMemberName())); + } + + /// + /// Provides access to change tracking information and operations for a given complex type property of this complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// + /// A lambda expression representing the property to access information and operations for. + /// + /// An object that exposes change tracking information and operations for the given property. + public virtual ComplexPropertyEntry ComplexProperty( + Expression> propertyExpression) + { + Check.NotNull(propertyExpression, nameof(propertyExpression)); + + return new ComplexPropertyEntry( + InternalEntry, + Metadata.ComplexType.GetComplexProperty(propertyExpression.GetMemberAccess().GetSimpleMemberName())); + } + + /// + /// Provides access to change tracking information and operations for a given property of this complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The type of the property. + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual PropertyEntry Property(IProperty property) + { + Check.NotNull(property, nameof(property)); + + ValidateType(property); + + return new PropertyEntry(InternalEntry, property); + } + + /// + /// Provides access to change tracking information and operations for a given complex type property of this complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The type of the property. + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual ComplexPropertyEntry ComplexProperty( + IComplexProperty complexProperty) + { + Check.NotNull(complexProperty, nameof(complexProperty)); + + ValidateType(complexProperty); + + return new ComplexPropertyEntry(InternalEntry, complexProperty); + } + + /// + /// Provides access to change tracking information and operations for a given property of this complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The type of the property. + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual PropertyEntry Property(string propertyName) + { + Check.NotEmpty(propertyName, nameof(propertyName)); + + ValidateType(Metadata.ComplexType.FindProperty(propertyName)); + + return new PropertyEntry(InternalEntry, Metadata.ComplexType.GetProperty(propertyName)); + } + + /// + /// Provides access to change tracking information and operations for a given complex type property of this complex type. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The type of the property. + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual ComplexPropertyEntry ComplexProperty(string propertyName) + { + Check.NotEmpty(propertyName, nameof(propertyName)); + + ValidateType(Metadata.ComplexType.FindComplexProperty(propertyName)); + + return new ComplexPropertyEntry( + InternalEntry, Metadata.ComplexType.GetComplexProperty(propertyName)); + } + + private static void ValidateType(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())); + } + } +} diff --git a/src/EFCore/ChangeTracking/EntityEntry.cs b/src/EFCore/ChangeTracking/EntityEntry.cs index 5c4aed5fd98..70fd20516f0 100644 --- a/src/EFCore/ChangeTracking/EntityEntry.cs +++ b/src/EFCore/ChangeTracking/EntityEntry.cs @@ -145,6 +145,7 @@ public virtual MemberEntry Member(IPropertyBase propertyBase) return propertyBase switch { IProperty property => new PropertyEntry(InternalEntry, property), + IComplexProperty complexProperty => new ComplexPropertyEntry(InternalEntry, complexProperty), INavigationBase navigation => navigation.IsCollection ? new CollectionEntry(InternalEntry, navigation) : new ReferenceEntry(InternalEntry, (INavigation)navigation), @@ -170,7 +171,13 @@ public virtual MemberEntry Member(string propertyName) var property = InternalEntry.EntityType.FindProperty(propertyName); if (property != null) { - return new PropertyEntry(InternalEntry, propertyName); + return new PropertyEntry(InternalEntry, property); + } + + var complexProperty = InternalEntry.EntityType.FindComplexProperty(propertyName); + if (complexProperty != null) + { + return new ComplexPropertyEntry(InternalEntry, complexProperty); } var navigation = (INavigationBase?)InternalEntry.EntityType.FindNavigation(propertyName) @@ -178,8 +185,8 @@ public virtual MemberEntry Member(string propertyName) if (navigation != null) { return navigation.IsCollection - ? new CollectionEntry(InternalEntry, propertyName) - : new ReferenceEntry(InternalEntry, propertyName); + ? new CollectionEntry(InternalEntry, navigation) + : new ReferenceEntry(InternalEntry, (INavigation)navigation); } throw new InvalidOperationException( @@ -194,7 +201,7 @@ public virtual MemberEntry Member(string propertyName) /// examples. /// public virtual IEnumerable Members - => Properties.Cast().Concat(Navigations); + => Properties.Cast().Concat(ComplexProperties).Concat(Navigations); /// /// Provides access to change tracking information and operations for a given navigation of this entity. @@ -239,7 +246,8 @@ public virtual NavigationEntry Navigation(string propertyName) : new ReferenceEntry(InternalEntry, propertyName); } - if (InternalEntry.EntityType.FindProperty(propertyName) != null) + if (InternalEntry.EntityType.FindProperty(propertyName) != null + || InternalEntry.EntityType.FindComplexProperty(propertyName) != null) { throw new InvalidOperationException( CoreStrings.NavigationIsProperty( @@ -303,7 +311,7 @@ public virtual PropertyEntry Property(string propertyName) { Check.NotEmpty(propertyName, nameof(propertyName)); - return new PropertyEntry(InternalEntry, propertyName); + return new PropertyEntry(InternalEntry, Metadata.GetProperty(propertyName)); } /// @@ -317,6 +325,48 @@ public virtual PropertyEntry Property(string propertyName) public virtual IEnumerable Properties => InternalEntry.EntityType.GetProperties().Select(property => new PropertyEntry(InternalEntry, property)); + /// + /// Provides access to change tracking information and operations for a given property of a complex type on this entity. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual ComplexPropertyEntry ComplexProperty(IComplexProperty property) + { + Check.NotNull(property, nameof(property)); + + return new ComplexPropertyEntry(InternalEntry, property); + } + + /// + /// Provides access to change tracking information and operations for a given property of a complex type on this entity. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual ComplexPropertyEntry ComplexProperty(string propertyName) + { + Check.NotEmpty(propertyName, nameof(propertyName)); + + return new ComplexPropertyEntry(InternalEntry, Metadata.GetComplexProperty(propertyName)); + } + + /// + /// Provides access to change tracking information and operations for all properties of complex type on this entity. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + public virtual IEnumerable ComplexProperties + => Metadata.GetComplexProperties().Select(property => new ComplexPropertyEntry(InternalEntry, property)); + /// /// Provides access to change tracking and loading information for a reference (i.e. non-collection) /// navigation that associates this entity to another entity. diff --git a/src/EFCore/ChangeTracking/EntityEntry`.cs b/src/EFCore/ChangeTracking/EntityEntry`.cs index 8901fd76d78..05393062e8c 100644 --- a/src/EFCore/ChangeTracking/EntityEntry`.cs +++ b/src/EFCore/ChangeTracking/EntityEntry`.cs @@ -2,6 +2,7 @@ // 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; @@ -56,7 +57,30 @@ public virtual PropertyEntry Property( { Check.NotNull(propertyExpression, nameof(propertyExpression)); - return new PropertyEntry(InternalEntry, propertyExpression.GetMemberAccess().GetSimpleMemberName()); + return new PropertyEntry( + InternalEntry, + Metadata.GetProperty(propertyExpression.GetMemberAccess().GetSimpleMemberName())); + } + + /// + /// Provides access to change tracking information and operations for a given complex type property of this entity. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// + /// A lambda expression representing the property to access information and operations for. + /// + /// An object that exposes change tracking information and operations for the given property. + public virtual ComplexPropertyEntry ComplexProperty( + Expression> propertyExpression) + { + Check.NotNull(propertyExpression, nameof(propertyExpression)); + + return new ComplexPropertyEntry( + InternalEntry, + Metadata.GetComplexProperty(propertyExpression.GetMemberAccess().GetSimpleMemberName())); } /// @@ -126,6 +150,25 @@ public virtual PropertyEntry Property(IProperty p return new PropertyEntry(InternalEntry, property); } + /// + /// Provides access to change tracking information and operations for a given complex type property of this entity. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The type of the property. + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual ComplexPropertyEntry ComplexProperty(IComplexProperty complexProperty) + { + Check.NotNull(complexProperty, nameof(complexProperty)); + + ValidateType(complexProperty); + + return new ComplexPropertyEntry(InternalEntry, complexProperty); + } + /// /// Provides access to change tracking and loading information for a reference (i.e. non-collection) /// navigation that associates this entity to another entity. @@ -226,10 +269,29 @@ public virtual PropertyEntry Property(string prop ValidateType(InternalEntry.EntityType.FindProperty(propertyName)); - return new PropertyEntry(InternalEntry, propertyName); + return new PropertyEntry(InternalEntry, Metadata.GetProperty(propertyName)); + } + + /// + /// Provides access to change tracking information and operations for a given complex type property of this entity. + /// + /// + /// See Accessing tracked entities in EF Core for more information and + /// examples. + /// + /// The type of the property. + /// The property to access information and operations for. + /// An object that exposes change tracking information and operations for the given property. + public virtual ComplexPropertyEntry ComplexProperty(string propertyName) + { + Check.NotEmpty(propertyName, nameof(propertyName)); + + ValidateType(InternalEntry.EntityType.FindComplexProperty(propertyName)); + + return new ComplexPropertyEntry(InternalEntry, Metadata.GetComplexProperty(propertyName)); } - private static void ValidateType(IProperty? property) + private static void ValidateType(IPropertyBase? property) { if (property != null && property.ClrType != typeof(TProperty)) diff --git a/src/EFCore/ChangeTracking/NavigationEntry.cs b/src/EFCore/ChangeTracking/NavigationEntry.cs index b411e0c5da7..bf4bee10548 100644 --- a/src/EFCore/ChangeTracking/NavigationEntry.cs +++ b/src/EFCore/ChangeTracking/NavigationEntry.cs @@ -69,7 +69,8 @@ private static INavigationBase GetNavigation(InternalEntityEntry internalEntry, if (navigation == null) { - if (internalEntry.EntityType.FindProperty(name) != null) + if (internalEntry.EntityType.FindProperty(name) != null + || internalEntry.EntityType.FindComplexProperty(name) != null) { throw new InvalidOperationException( CoreStrings.NavigationIsProperty( diff --git a/src/EFCore/ChangeTracking/PropertyEntry.cs b/src/EFCore/ChangeTracking/PropertyEntry.cs index df0d4971046..c62c229c7e6 100644 --- a/src/EFCore/ChangeTracking/PropertyEntry.cs +++ b/src/EFCore/ChangeTracking/PropertyEntry.cs @@ -20,18 +20,6 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking; /// public class PropertyEntry : MemberEntry { - /// - /// 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. - /// - [EntityFrameworkInternal] - public PropertyEntry(InternalEntityEntry internalEntry, string name) - : this(internalEntry, internalEntry.EntityType.GetProperty(name)) - { - } - /// /// 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 diff --git a/src/EFCore/ChangeTracking/PropertyEntry`.cs b/src/EFCore/ChangeTracking/PropertyEntry`.cs index 750521f9b29..054c97e494d 100644 --- a/src/EFCore/ChangeTracking/PropertyEntry`.cs +++ b/src/EFCore/ChangeTracking/PropertyEntry`.cs @@ -23,18 +23,6 @@ namespace Microsoft.EntityFrameworkCore.ChangeTracking; public class PropertyEntry : PropertyEntry where TEntity : class { - /// - /// 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. - /// - [EntityFrameworkInternal] - public PropertyEntry(InternalEntityEntry internalEntry, string name) - : base(internalEntry, name) - { - } - /// /// 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 diff --git a/src/EFCore/Metadata/Internal/PropertyBase.cs b/src/EFCore/Metadata/Internal/PropertyBase.cs index 7efd21b75d4..90f25bb2ac4 100644 --- a/src/EFCore/Metadata/Internal/PropertyBase.cs +++ b/src/EFCore/Metadata/Internal/PropertyBase.cs @@ -455,20 +455,30 @@ public static Expression CreateMemberAccess( complexType.ComplexProperty.GetMemberInfo(forMaterialization: false, forSet: false), fromStructuralType); - if (!instanceExpression.Type.IsValueType - || instanceExpression.Type.IsNullableValueType()) - { - var instanceVariable = Expression.Variable(instanceExpression.Type, "instance"); - var block = Expression.Block( - new[] { instanceVariable }, - Expression.Assign(instanceVariable, instanceExpression), - Expression.Condition( - Expression.Equal(instanceVariable, Expression.Constant(null)), - Expression.Default(memberInfo.GetMemberType()), - Expression.MakeMemberAccess(instanceVariable, memberInfo))); - - return block; - } + // TODO: Handle null/default complex types #31376 + // if (!instanceExpression.Type.IsValueType) + // { + // var instanceVariable = Expression.Variable(instanceExpression.Type, "instance"); + // return Expression.Block( + // new[] { instanceVariable }, + // Expression.Assign(instanceVariable, instanceExpression), + // Expression.Condition( + // Expression.Equal(instanceVariable, Expression.Constant(null)), + // forWrite + // ? Expression.Block( + // Expression.Throw(Expression.Constant(new InvalidOperationException())), + // Expression.Default(memberInfo.GetMemberType())) + // : Expression.Default(memberInfo.GetMemberType()), + // Expression.MakeMemberAccess(instanceExpression, memberInfo))); + // } + + // if (instanceExpression.Type.IsNullableValueType()) + // { + // return Expression.Condition( + // Expression.Equal(instanceExpression, Expression.Constant(null)), + // Expression.Default(memberInfo.GetMemberType()), + // Expression.MakeMemberAccess(instanceExpression, memberInfo)); + // } } return Expression.MakeMemberAccess(instanceExpression, memberInfo); diff --git a/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs b/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs index 6f35fb8f09c..d1e061d3de8 100644 --- a/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs +++ b/src/EFCore/Metadata/Internal/TypeBaseExtensions.cs @@ -56,4 +56,18 @@ public static IReadOnlyDictionary GetRuntimeProperties(thi /// public static IReadOnlyDictionary GetRuntimeFields(this IReadOnlyTypeBase type) => ((TypeBase)type).GetRuntimeFields(); + + /// + /// 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. + /// + public static IComplexProperty GetComplexProperty(this ITypeBase type, string name) + { + Check.NotEmpty(name, nameof(name)); + + return type.FindComplexProperty(name) + ?? throw new InvalidOperationException(CoreStrings.ComplexPropertyNotFound(type.DisplayName(), name)); + } } diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs index 5bc400d52b1..563afdfe7df 100644 --- a/src/EFCore/Properties/CoreStrings.Designer.cs +++ b/src/EFCore/Properties/CoreStrings.Designer.cs @@ -512,6 +512,14 @@ public static string ComplexPropertyIndexer(object? type, object? property) GetString("ComplexPropertyIndexer", nameof(type), nameof(property)), type, property); + /// + /// The complex property '{type}.{property}' could not be found. Ensure that the property exists and has been included in the model as a complex property. + /// + public static string ComplexPropertyNotFound(object? type, object? property) + => string.Format( + GetString("ComplexPropertyNotFound", nameof(type), nameof(property)), + type, property); + /// /// Configuring the complex property '{type}.{property}' as optional is not supported, call 'IsRequired()'. See https://github.com/dotnet/efcore/issues/31376 for more information. /// diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx index 02b81723a70..b9cf6b9e15f 100644 --- a/src/EFCore/Properties/CoreStrings.resx +++ b/src/EFCore/Properties/CoreStrings.resx @@ -300,6 +300,9 @@ Adding the complex property '{type}.{property}' as an indexer property isn't supported. See https://github.com/dotnet/efcore/issues/31244 for more information. + + The complex property '{type}.{property}' could not be found. Ensure that the property exists and has been included in the model as a complex property. + Configuring the complex property '{type}.{property}' as optional is not supported, call 'IsRequired()'. See https://github.com/dotnet/efcore/issues/31376 for more information. diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs index e1f3ccccd02..ab5552d9fa5 100644 --- a/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs +++ b/test/EFCore.Design.Tests/Scaffolding/Internal/CSharpRuntimeModelCodeGeneratorTest.cs @@ -7921,6 +7921,9 @@ public static void CreateAnnotations(RuntimeEntityType runtimeEntityType) AlternateId = new Guid(), Dependent = new DependentBase(1), Owned = new OwnedType(c) + { + Principal = new PrincipalBase() + } }); //c.SaveChanges(); diff --git a/test/EFCore.Specification.Tests/ComplexTypesTrackingTestBase.cs b/test/EFCore.Specification.Tests/ComplexTypesTrackingTestBase.cs index c976b910e0f..e20ca73be5f 100644 --- a/test/EFCore.Specification.Tests/ComplexTypesTrackingTestBase.cs +++ b/test/EFCore.Specification.Tests/ComplexTypesTrackingTestBase.cs @@ -26,12 +26,230 @@ protected ComplexTypesTrackingTestBase(TFixture fixture) [InlineData(EntityState.Modified, true)] [InlineData(EntityState.Deleted, false)] [InlineData(EntityState.Deleted, true)] - public virtual async Task Can_track_entity_with_complex_objects(EntityState state, bool async) + public virtual Task Can_track_entity_with_complex_objects(EntityState state, bool async) + => TrackAndSaveTest(state, async, CreatePub()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_mark_complex_type_properties_modified(bool trackFromQuery) + => MarkModifiedTest(trackFromQuery, CreatePub()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_read_original_values_for_properties_of_complex_types(bool trackFromQuery) + => ReadOriginalValuesTest(trackFromQuery, CreatePub()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_write_original_values_for_properties_of_complex_types(bool trackFromQuery) + => WriteOriginalValuesTest(trackFromQuery, CreatePub()); + + [ConditionalTheory] + [InlineData(EntityState.Added, false)] + [InlineData(EntityState.Added, true)] + [InlineData(EntityState.Unchanged, false)] + [InlineData(EntityState.Unchanged, true)] + [InlineData(EntityState.Modified, false)] + [InlineData(EntityState.Modified, true)] + [InlineData(EntityState.Deleted, false)] + [InlineData(EntityState.Deleted, true)] + public virtual Task Can_track_entity_with_complex_structs(EntityState state, bool async) + => TrackAndSaveTest(state, async, CreatePubWithStructs()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_mark_complex_readonly_struct_properties_modified(bool trackFromQuery) + => MarkModifiedTest(trackFromQuery, CreatePubWithStructs()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_read_original_values_for_properties_of_structs(bool trackFromQuery) + => ReadOriginalValuesTest(trackFromQuery, CreatePubWithStructs()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_write_original_values_for_properties_of_structs(bool trackFromQuery) + => WriteOriginalValuesTest(trackFromQuery, CreatePubWithStructs()); + + [ConditionalTheory] + [InlineData(EntityState.Added, false)] + [InlineData(EntityState.Added, true)] + [InlineData(EntityState.Unchanged, false)] + [InlineData(EntityState.Unchanged, true)] + [InlineData(EntityState.Modified, false)] + [InlineData(EntityState.Modified, true)] + [InlineData(EntityState.Deleted, false)] + [InlineData(EntityState.Deleted, true)] + public virtual Task Can_track_entity_with_complex_readonly_structs(EntityState state, bool async) + => TrackAndSaveTest(state, async, CreatePubWithReadonlyStructs()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_mark_complex_readonly_readonly_struct_properties_modified(bool trackFromQuery) + => MarkModifiedTest(trackFromQuery, CreatePubWithReadonlyStructs()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_write_original_values_for_properties_of_readonly_structs(bool trackFromQuery) + => WriteOriginalValuesTest(trackFromQuery, CreatePubWithReadonlyStructs()); + + [ConditionalTheory] + [InlineData(EntityState.Added, false)] + [InlineData(EntityState.Added, true)] + [InlineData(EntityState.Unchanged, false)] + [InlineData(EntityState.Unchanged, true)] + [InlineData(EntityState.Modified, false)] + [InlineData(EntityState.Modified, true)] + [InlineData(EntityState.Deleted, false)] + [InlineData(EntityState.Deleted, true)] + public virtual Task Can_track_entity_with_complex_record_objects(EntityState state, bool async) + => TrackAndSaveTest(state, async, CreatePubWithRecords()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_mark_complex_record_type_properties_modified(bool trackFromQuery) + => MarkModifiedTest(trackFromQuery, CreatePubWithRecords()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_read_original_values_for_properties_of_record_complex_types(bool trackFromQuery) + => ReadOriginalValuesTest(trackFromQuery, CreatePubWithRecords()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_write_original_values_for_properties_of_record_complex_types(bool trackFromQuery) + => WriteOriginalValuesTest(trackFromQuery, CreatePubWithRecords()); + + [ConditionalTheory] + [InlineData(EntityState.Added, false)] + [InlineData(EntityState.Added, true)] + [InlineData(EntityState.Unchanged, false)] + [InlineData(EntityState.Unchanged, true)] + [InlineData(EntityState.Modified, false)] + [InlineData(EntityState.Modified, true)] + [InlineData(EntityState.Deleted, false)] + [InlineData(EntityState.Deleted, true)] + public virtual Task Can_track_entity_with_complex_objects_with_fields(EntityState state, bool async) + => TrackAndSaveTest(state, async, CreateFieldPub()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_mark_complex_type_properties_modified_with_fields(bool trackFromQuery) + => MarkModifiedTest(trackFromQuery, CreateFieldPub()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_read_original_values_for_properties_of_complex_types_with_fields(bool trackFromQuery) + => ReadOriginalValuesTest(trackFromQuery, CreateFieldPub()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_write_original_values_for_properties_of_complex_types_with_fields(bool trackFromQuery) + => WriteOriginalValuesTest(trackFromQuery, CreateFieldPub()); + + [ConditionalTheory] + [InlineData(EntityState.Added, false)] + [InlineData(EntityState.Added, true)] + [InlineData(EntityState.Unchanged, false)] + [InlineData(EntityState.Unchanged, true)] + [InlineData(EntityState.Modified, false)] + [InlineData(EntityState.Modified, true)] + [InlineData(EntityState.Deleted, false)] + [InlineData(EntityState.Deleted, true)] + public virtual Task Can_track_entity_with_complex_structs_with_fields(EntityState state, bool async) + => TrackAndSaveTest(state, async, CreateFieldPubWithStructs()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_mark_complex_readonly_struct_properties_modified_with_fields(bool trackFromQuery) + => MarkModifiedTest(trackFromQuery, CreateFieldPubWithStructs()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_read_original_values_for_properties_of_structs_with_fields(bool trackFromQuery) + => ReadOriginalValuesTest(trackFromQuery, CreateFieldPubWithStructs()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_write_original_values_for_properties_of_structs_with_fields(bool trackFromQuery) + => WriteOriginalValuesTest(trackFromQuery, CreateFieldPubWithStructs()); + + [ConditionalTheory(Skip = "Constructor binding")] + [InlineData(EntityState.Added, false)] + [InlineData(EntityState.Added, true)] + [InlineData(EntityState.Unchanged, false)] + [InlineData(EntityState.Unchanged, true)] + [InlineData(EntityState.Modified, false)] + [InlineData(EntityState.Modified, true)] + [InlineData(EntityState.Deleted, false)] + [InlineData(EntityState.Deleted, true)] + public virtual Task Can_track_entity_with_complex_readonly_structs_with_fields(EntityState state, bool async) + => TrackAndSaveTest(state, async, CreateFieldPubWithReadonlyStructs()); + + [ConditionalTheory(Skip = "Constructor binding")] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_mark_complex_readonly_readonly_struct_properties_modified_with_fields(bool trackFromQuery) + => MarkModifiedTest(trackFromQuery, CreateFieldPubWithReadonlyStructs()); + + [ConditionalTheory(Skip = "Constructor binding")] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_write_original_values_for_properties_of_readonly_structs_with_fields(bool trackFromQuery) + => WriteOriginalValuesTest(trackFromQuery, CreateFieldPubWithReadonlyStructs()); + + [ConditionalTheory] + [InlineData(EntityState.Added, false)] + [InlineData(EntityState.Added, true)] + [InlineData(EntityState.Unchanged, false)] + [InlineData(EntityState.Unchanged, true)] + [InlineData(EntityState.Modified, false)] + [InlineData(EntityState.Modified, true)] + [InlineData(EntityState.Deleted, false)] + [InlineData(EntityState.Deleted, true)] + public virtual Task Can_track_entity_with_complex_record_objects_with_fields(EntityState state, bool async) + => TrackAndSaveTest(state, async, CreateFieldPubWithRecords()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_mark_complex_record_type_properties_modified_with_fields(bool trackFromQuery) + => MarkModifiedTest(trackFromQuery, CreateFieldPubWithRecords()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_read_original_values_for_properties_of_record_complex_types_with_fields(bool trackFromQuery) + => ReadOriginalValuesTest(trackFromQuery, CreateFieldPubWithRecords()); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Can_write_original_values_for_properties_of_record_complex_types_with_fields(bool trackFromQuery) + => WriteOriginalValuesTest(trackFromQuery, CreateFieldPubWithRecords()); + + private async Task TrackAndSaveTest(EntityState state, bool async, TEntity pub) + where TEntity : class => await ExecuteWithStrategyInTransactionAsync( async context => { - var pub = CreatePub(); - var entry = state switch { EntityState.Unchanged => context.Attach(pub), @@ -54,13 +272,10 @@ public virtual async Task Can_track_entity_with_complex_objects(EntityState stat } }); - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public virtual void Can_mark_complex_type_properties_modified(bool trackFromQuery) + private void MarkModifiedTest(bool trackFromQuery, TEntity pub) + where TEntity : class { using var context = CreateContext(); - var pub = CreatePub(); var entry = trackFromQuery ? TrackFromQuery(context, pub) : context.Attach(pub); @@ -68,61 +283,74 @@ public virtual void Can_mark_complex_type_properties_modified(bool trackFromQuer AssertPropertyValues(entry); AssertPropertiesModified(entry, false); - MarkModified(entry, "EveningActivity.RunnersUp.Members", true); + var membersEntry = entry.ComplexProperty("LunchtimeActivity").ComplexProperty("RunnersUp").Property("Members"); + membersEntry.IsModified = true; Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "EveningActivity.RunnersUp.Members")); + Assert.True(membersEntry.IsModified); - MarkModified(entry, "LunchtimeActivity.Day", true); + var dayEntry = entry.ComplexProperty("LunchtimeActivity").Property("Day"); + dayEntry.IsModified = true; Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "LunchtimeActivity.Day")); + Assert.True(dayEntry.IsModified); - MarkModified(entry, "EveningActivity.CoverCharge", true); + var coverChargeEntry = entry.ComplexProperty("EveningActivity").Property("CoverCharge"); + coverChargeEntry.IsModified = true; Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "EveningActivity.CoverCharge")); - - Assert.False(IsModified(entry, "LunchtimeActivity.Name")); - Assert.False(IsModified(entry, "LunchtimeActivity.Description")); - Assert.False(IsModified(entry, "LunchtimeActivity.Notes")); - Assert.False(IsModified(entry, "LunchtimeActivity.CoverCharge")); - Assert.False(IsModified(entry, "LunchtimeActivity.IsTeamBased")); - Assert.False(IsModified(entry, "LunchtimeActivity.Champions.Name")); - Assert.False(IsModified(entry, "LunchtimeActivity.Champions.Members")); - Assert.False(IsModified(entry, "LunchtimeActivity.RunnersUp.Name")); - Assert.False(IsModified(entry, "LunchtimeActivity.RunnersUp.Members")); - Assert.False(IsModified(entry, "EveningActivity.Name")); - Assert.False(IsModified(entry, "EveningActivity.Day")); - Assert.False(IsModified(entry, "EveningActivity.Description")); - Assert.False(IsModified(entry, "EveningActivity.Notes")); - Assert.False(IsModified(entry, "EveningActivity.IsTeamBased")); - Assert.False(IsModified(entry, "EveningActivity.Champions.Name")); - Assert.False(IsModified(entry, "EveningActivity.Champions.Members")); - Assert.False(IsModified(entry, "EveningActivity.RunnersUp.Name")); - Assert.False(IsModified(entry, "FeaturedTeam.Name")); - Assert.False(IsModified(entry, "FeaturedTeam.Members")); - - MarkModified(entry, "EveningActivity.RunnersUp.Members", false); + Assert.True(coverChargeEntry.IsModified); + + var lunchtimeEntry = entry.ComplexProperty("LunchtimeActivity"); + var lunchtimeChampionsEntry = lunchtimeEntry.ComplexProperty("Champions"); + var lunchtimeRunnersUpEntry = lunchtimeEntry.ComplexProperty("RunnersUp"); + var eveningEntry = entry.ComplexProperty("EveningActivity"); + var eveningChampionsEntry = eveningEntry.ComplexProperty("Champions"); + var eveningRunnersUpEntry = eveningEntry.ComplexProperty("RunnersUp"); + var teamEntry = entry.ComplexProperty("FeaturedTeam"); + + Assert.False(lunchtimeEntry.Property("Name").IsModified); + Assert.False(lunchtimeEntry.Property("Description").IsModified); + Assert.True(lunchtimeEntry.Property("Day").IsModified); + Assert.False(lunchtimeEntry.Property("Notes").IsModified); + Assert.False(lunchtimeEntry.Property("CoverCharge").IsModified); + Assert.False(lunchtimeEntry.Property("IsTeamBased").IsModified); + Assert.False(lunchtimeChampionsEntry.Property("Name").IsModified); + Assert.False(lunchtimeChampionsEntry.Property("Members").IsModified); + Assert.False(lunchtimeRunnersUpEntry.Property("Name").IsModified); + Assert.True(lunchtimeRunnersUpEntry.Property("Members").IsModified); + + Assert.False(eveningEntry.Property("Name").IsModified); + Assert.False(eveningEntry.Property("Description").IsModified); + Assert.False(eveningEntry.Property("Day").IsModified); + Assert.False(eveningEntry.Property("Notes").IsModified); + Assert.True(eveningEntry.Property("CoverCharge").IsModified); + Assert.False(eveningEntry.Property("IsTeamBased").IsModified); + Assert.False(eveningChampionsEntry.Property("Name").IsModified); + Assert.False(eveningChampionsEntry.Property("Members").IsModified); + Assert.False(eveningRunnersUpEntry.Property("Name").IsModified); + Assert.False(eveningRunnersUpEntry.Property("Members").IsModified); + + Assert.False(teamEntry.Property("Name").IsModified); + Assert.False(teamEntry.Property("Members").IsModified); + + membersEntry.IsModified = false; Assert.Equal(EntityState.Modified, entry.State); - Assert.False(IsModified(entry, "EveningActivity.RunnersUp.Members")); + Assert.False(membersEntry.IsModified); - MarkModified(entry, "LunchtimeActivity.Day", false); + dayEntry.IsModified = false; Assert.Equal(EntityState.Modified, entry.State); - Assert.False(IsModified(entry, "LunchtimeActivity.Day")); + Assert.False(dayEntry.IsModified); - MarkModified(entry, "EveningActivity.CoverCharge", false); + coverChargeEntry.IsModified = false; Assert.Equal(EntityState.Unchanged, entry.State); - Assert.False(IsModified(entry, "EveningActivity.CoverCharge")); + Assert.False(coverChargeEntry.IsModified); AssertPropertyValues(entry); AssertPropertiesModified(entry, false); } - [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] - public virtual void Can_read_original_values_for_properties_of_complex_types(bool trackFromQuery) + private void ReadOriginalValuesTest(bool trackFromQuery, TEntity pub) + where TEntity : class { using var context = CreateContext(); - var pub = CreatePub(); var entry = trackFromQuery ? TrackFromQuery(context, pub) : context.Attach(pub); @@ -130,30 +358,64 @@ public virtual void Can_read_original_values_for_properties_of_complex_types(boo AssertPropertyValues(entry); AssertPropertiesModified(entry, false); - WriteCurrentValue(entry, "EveningActivity.Champions.Members", new List { "1", "2", "3" }); + var membersEntry = entry.ComplexProperty("LunchtimeActivity").ComplexProperty("Champions").Property("Members"); + membersEntry.CurrentValue = new List { "1", "2", "3" }; + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(membersEntry.IsModified); + Assert.Equal(new[] { "1", "2", "3" }, membersEntry.CurrentValue); + Assert.Equal(new[] { "Boris", "David", "Theresa" }, membersEntry.OriginalValue); + + var dayEntry = entry.ComplexProperty("LunchtimeActivity").Property("Day"); + dayEntry.CurrentValue = DayOfWeek.Wednesday; + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(dayEntry.IsModified); + Assert.Equal(DayOfWeek.Wednesday, dayEntry.CurrentValue); + Assert.Equal(DayOfWeek.Monday, dayEntry.OriginalValue); + + var coverChargeEntry = entry.ComplexProperty("EveningActivity").Property("CoverCharge"); + coverChargeEntry.CurrentValue = 3.0m; + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(coverChargeEntry.IsModified); + Assert.Equal(3.0m, coverChargeEntry.CurrentValue); + Assert.Equal(5.0m, coverChargeEntry.OriginalValue); + } + + private void WriteOriginalValuesTest(bool trackFromQuery, TEntity pub) + where TEntity : class + { + using var context = CreateContext(); + var entry = trackFromQuery ? TrackFromQuery(context, pub) : context.Attach(pub); + + Assert.Equal(EntityState.Unchanged, entry.State); + AssertPropertyValues(entry); + AssertPropertiesModified(entry, false); + + var membersEntry = entry.ComplexProperty("EveningActivity").ComplexProperty("Champions").Property("Members"); + membersEntry.OriginalValue = new List { "1", "2", "3" }; Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "EveningActivity.Champions.Members")); - Assert.Equal(new[] { "1", "2", "3" }, ReadCurrentValue>(entry, "EveningActivity.Champions.Members")); - Assert.Equal( - new[] { "Robert", "Jimmy", "John", "Jason" }, ReadOriginalValue>(entry, "EveningActivity.Champions.Members")); + Assert.True(membersEntry.IsModified); + Assert.Equal(new[] { "Robert", "Jimmy", "John", "Jason" }, membersEntry.CurrentValue); + Assert.Equal(new[] { "1", "2", "3" }, membersEntry.OriginalValue); - WriteCurrentValue(entry, "LunchtimeActivity.Day", DayOfWeek.Wednesday); + var dayEntry = entry.ComplexProperty("LunchtimeActivity").Property("Day"); + dayEntry.OriginalValue = DayOfWeek.Wednesday; Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "LunchtimeActivity.Day")); - Assert.Equal(DayOfWeek.Wednesday, ReadCurrentValue(entry, "LunchtimeActivity.Day")); - Assert.Equal(DayOfWeek.Monday, ReadOriginalValue(entry, "LunchtimeActivity.Day")); + Assert.True(dayEntry.IsModified); + Assert.Equal(DayOfWeek.Monday, dayEntry.CurrentValue); + Assert.Equal(DayOfWeek.Wednesday, dayEntry.OriginalValue); - WriteCurrentValue(entry, "EveningActivity.CoverCharge", 3.0m); + var coverChargeEntry = entry.ComplexProperty("EveningActivity").Property("CoverCharge"); + coverChargeEntry.OriginalValue = 3.0m; Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "EveningActivity.CoverCharge")); - Assert.Equal(3.0m, ReadCurrentValue(entry, "EveningActivity.CoverCharge")); - Assert.Equal(5.0m, ReadOriginalValue(entry, "EveningActivity.CoverCharge")); + Assert.True(coverChargeEntry.IsModified); + Assert.Equal(5.0m, coverChargeEntry.CurrentValue); + Assert.Equal(3.0m, coverChargeEntry.OriginalValue); } [ConditionalTheory] [InlineData(false)] [InlineData(true)] - public virtual void Can_write_original_values_for_properties_of_complex_types(bool trackFromQuery) + public virtual void Detect_changes_in_complex_type_properties(bool trackFromQuery) { using var context = CreateContext(); var pub = CreatePub(); @@ -164,32 +426,46 @@ public virtual void Can_write_original_values_for_properties_of_complex_types(bo AssertPropertyValues(entry); AssertPropertiesModified(entry, false); - WriteOriginalValue(entry, "EveningActivity.Champions.Members", new List { "1", "2", "3" }); + pub.EveningActivity.Champions.Members = new List + { + "1", + "2", + "3" + }; + context.ChangeTracker.DetectChanges(); + + var membersEntry = entry.ComplexProperty(e => e.EveningActivity).ComplexProperty(e => e.Champions).Property(e => e.Members); Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "EveningActivity.Champions.Members")); - Assert.Equal(new[] { "Robert", "Jimmy", "John", "Jason" }, ReadCurrentValue>(entry, "EveningActivity.Champions.Members")); - Assert.Equal(new[] { "1", "2", "3" }, ReadOriginalValue>(entry, "EveningActivity.Champions.Members")); + Assert.True(membersEntry.IsModified); + Assert.Equal(new[] { "1", "2", "3" }, membersEntry.CurrentValue); + Assert.Equal(new[] { "Robert", "Jimmy", "John", "Jason" }, membersEntry.OriginalValue); + + pub.LunchtimeActivity.Day = DayOfWeek.Wednesday; + context.ChangeTracker.DetectChanges(); - WriteOriginalValue(entry, "LunchtimeActivity.Day", DayOfWeek.Wednesday); + var dayEntry = entry.ComplexProperty(e => e.LunchtimeActivity).Property(e => e.Day); Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "LunchtimeActivity.Day")); - Assert.Equal(DayOfWeek.Monday, ReadCurrentValue(entry, "LunchtimeActivity.Day")); - Assert.Equal(DayOfWeek.Wednesday, ReadOriginalValue(entry, "LunchtimeActivity.Day")); + Assert.True(dayEntry.IsModified); + Assert.Equal(DayOfWeek.Wednesday, dayEntry.CurrentValue); + Assert.Equal(DayOfWeek.Monday, dayEntry.OriginalValue); + + pub.EveningActivity.CoverCharge = 3.0m; + context.ChangeTracker.DetectChanges(); - WriteOriginalValue(entry, "EveningActivity.CoverCharge", 3.0m); + var coverChargeEntry = entry.ComplexProperty(e => e.EveningActivity).Property(e => e.CoverCharge); Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "EveningActivity.CoverCharge")); - Assert.Equal(5.0m, ReadCurrentValue(entry, "EveningActivity.CoverCharge")); - Assert.Equal(3.0m, ReadOriginalValue(entry, "EveningActivity.CoverCharge")); + Assert.True(coverChargeEntry.IsModified); + Assert.Equal(3.0m, coverChargeEntry.CurrentValue); + Assert.Equal(5.0m, coverChargeEntry.OriginalValue); } [ConditionalTheory] [InlineData(false)] [InlineData(true)] - public virtual void Detect_changes_detects_changes_in_complex_type_properties(bool trackFromQuery) + public virtual void Detect_changes_in_complex_struct_type_properties(bool trackFromQuery) { using var context = CreateContext(); - var pub = CreatePub(); + var pub = CreatePubWithStructs(); var entry = trackFromQuery ? TrackFromQuery(context, pub) : context.Attach(pub); @@ -197,217 +473,303 @@ public virtual void Detect_changes_detects_changes_in_complex_type_properties(bo AssertPropertyValues(entry); AssertPropertiesModified(entry, false); - pub.EveningActivity.Champions.Members = new List + var eveningActivity = pub.EveningActivity; + var champions = eveningActivity.Champions; + champions.Members = new() { "1", "2", "3" }; + eveningActivity.Champions = champions; + pub.EveningActivity = eveningActivity; + context.ChangeTracker.DetectChanges(); + var membersEntry = entry.ComplexProperty(e => e.EveningActivity).ComplexProperty(e => e.Champions).Property(e => e.Members); Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "EveningActivity.Champions.Members")); - Assert.Equal(new[] { "1", "2", "3" }, ReadCurrentValue>(entry, "EveningActivity.Champions.Members")); - Assert.Equal( - new[] { "Robert", "Jimmy", "John", "Jason" }, ReadOriginalValue>(entry, "EveningActivity.Champions.Members")); + Assert.True(membersEntry.IsModified); + Assert.Equal(new[] { "1", "2", "3" }, membersEntry.CurrentValue); + Assert.Equal(new[] { "Robert", "Jimmy", "John", "Jason" }, membersEntry.OriginalValue); + + var lunchtimeActivity = pub.LunchtimeActivity; + lunchtimeActivity.Day = DayOfWeek.Wednesday; + pub.LunchtimeActivity = lunchtimeActivity; - pub.LunchtimeActivity.Day = DayOfWeek.Wednesday; context.ChangeTracker.DetectChanges(); + var dayEntry = entry.ComplexProperty(e => e.LunchtimeActivity).Property(e => e.Day); Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "LunchtimeActivity.Day")); - Assert.Equal(DayOfWeek.Wednesday, ReadCurrentValue(entry, "LunchtimeActivity.Day")); - Assert.Equal(DayOfWeek.Monday, ReadOriginalValue(entry, "LunchtimeActivity.Day")); + Assert.True(dayEntry.IsModified); + Assert.Equal(DayOfWeek.Wednesday, dayEntry.CurrentValue); + Assert.Equal(DayOfWeek.Monday, dayEntry.OriginalValue); + + eveningActivity = pub.EveningActivity; + eveningActivity.CoverCharge = 3.0m; + pub.EveningActivity = eveningActivity; - pub.EveningActivity.CoverCharge = 3.0m; context.ChangeTracker.DetectChanges(); + var coverChargeEntry = entry.ComplexProperty(e => e.EveningActivity).Property(e => e.CoverCharge); Assert.Equal(EntityState.Modified, entry.State); - Assert.True(IsModified(entry, "EveningActivity.CoverCharge")); - Assert.Equal(3.0m, ReadCurrentValue(entry, "EveningActivity.CoverCharge")); - Assert.Equal(5.0m, ReadOriginalValue(entry, "EveningActivity.CoverCharge")); + Assert.True(coverChargeEntry.IsModified); + Assert.Equal(3.0m, coverChargeEntry.CurrentValue); + Assert.Equal(5.0m, coverChargeEntry.OriginalValue); } - protected static Pub CreatePub() - => new() + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Detects_changes_in_complex_readonly_struct_type_properties(bool trackFromQuery) + { + using var context = CreateContext(); + var pub = CreatePubWithReadonlyStructs(); + + var entry = trackFromQuery ? TrackFromQuery(context, pub) : context.Attach(pub); + + Assert.Equal(EntityState.Unchanged, entry.State); + AssertPropertyValues(entry); + AssertPropertiesModified(entry, false); + + pub.EveningActivity = new() { - Id = Guid.NewGuid(), - Name = "The FBI", - LunchtimeActivity = new() + Name = "Music Quiz", + Day = DayOfWeek.Friday, + Description = "A music pub quiz.", + Notes = Array.Empty(), + CoverCharge = 5.0m, + IsTeamBased = true, + Champions = new() { - Name = "Pub Quiz", - Day = DayOfWeek.Monday, - Description = "A general knowledge pub quiz.", - Notes = new[] { "One", "Two", "Three" }, - CoverCharge = 2.0m, - IsTeamBased = true, - Champions = new() - { - Name = "Clueless", - Members = - { - "Boris", - "David", - "Theresa" - } - }, - RunnersUp = new() + Name = "Dazed and Confused", + Members = new() { - Name = "ZZ", - Members = - { - "Has Beard", - "Has Beard", - "Is Called Beard" - } - }, + "1", + "2", + "3" + } }, - EveningActivity = new() - { - Name = "Music Quiz", - Day = DayOfWeek.Friday, - Description = "A music pub quiz.", - Notes = Array.Empty(), - CoverCharge = 5.0m, - IsTeamBased = true, - Champions = new() + RunnersUp = new() { Name = "Banksy", Members = new() } + }; + + context.ChangeTracker.DetectChanges(); + + var membersEntry = entry.ComplexProperty(e => e.EveningActivity).ComplexProperty(e => e.Champions).Property(e => e.Members); + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(membersEntry.IsModified); + Assert.Equal(new[] { "1", "2", "3" }, membersEntry.CurrentValue); + Assert.Equal(new[] { "Robert", "Jimmy", "John", "Jason" }, membersEntry.OriginalValue); + + pub.LunchtimeActivity = new() + { + Name = "Pub Quiz", + Day = DayOfWeek.Wednesday, + Description = "A general knowledge pub quiz.", + Notes = new[] { "One", "Two", "Three" }, + CoverCharge = 2.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Clueless", + Members = new() { - Name = "Dazed and Confused", - Members = - { - "Robert", - "Jimmy", - "John", - "Jason" - } - }, - RunnersUp = new() + "Boris", + "David", + "Theresa" + } + }, + RunnersUp = new() + { + Name = "ZZ", + Members = new() { - Name = "Banksy" - }, + "Has Beard", + "Has Beard", + "Is Called Beard" + } }, - FeaturedTeam = new() + }; + + context.ChangeTracker.DetectChanges(); + + var dayEntry = entry.ComplexProperty(e => e.LunchtimeActivity).Property(e => e.Day); + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(dayEntry.IsModified); + Assert.Equal(DayOfWeek.Wednesday, dayEntry.CurrentValue); + Assert.Equal(DayOfWeek.Monday, dayEntry.OriginalValue); + + pub.EveningActivity = new() + { + Name = "Music Quiz", + Day = DayOfWeek.Friday, + Description = "A music pub quiz.", + Notes = Array.Empty(), + CoverCharge = 3.0m, + IsTeamBased = true, + Champions = new() { - Name = "Not In This Lifetime", - Members = + Name = "Dazed and Confused", + Members = new() { - "Slash", - "Axl" + "1", + "2", + "3" } - } + }, + RunnersUp = new() { Name = "Banksy", Members = new() } }; - protected void AssertPropertyValues(EntityEntry entry) - { - Assert.Equal("The FBI", ReadCurrentValue(entry, "Name")); - Assert.NotNull(ReadCurrentValue(entry, "LunchtimeActivity")); - Assert.Equal("Pub Quiz", ReadCurrentValue(entry, "LunchtimeActivity.Name")); - Assert.Equal(DayOfWeek.Monday, ReadCurrentValue(entry, "LunchtimeActivity.Day")); - Assert.Equal("A general knowledge pub quiz.", ReadCurrentValue(entry, "LunchtimeActivity.Description")); - Assert.Equal(new[] { "One", "Two", "Three" }, ReadCurrentValue(entry, "LunchtimeActivity.Notes")); - Assert.Equal(2.0m, ReadCurrentValue(entry, "LunchtimeActivity.CoverCharge")); - Assert.True(ReadCurrentValue(entry, "LunchtimeActivity.IsTeamBased")); - Assert.NotNull(ReadCurrentValue(entry, "LunchtimeActivity.Champions")); - Assert.Equal("Clueless", ReadCurrentValue(entry, "LunchtimeActivity.Champions.Name")); - Assert.Equal(new[] { "Boris", "David", "Theresa" }, ReadCurrentValue>(entry, "LunchtimeActivity.Champions.Members")); - Assert.NotNull(ReadCurrentValue(entry, "LunchtimeActivity.RunnersUp")); - Assert.Equal("ZZ", ReadCurrentValue(entry, "LunchtimeActivity.RunnersUp.Name")); - Assert.Equal( - new[] { "Has Beard", "Has Beard", "Is Called Beard" }, - ReadCurrentValue>(entry, "LunchtimeActivity.RunnersUp.Members")); - Assert.NotNull(ReadCurrentValue(entry, "EveningActivity")); - Assert.Equal("Music Quiz", ReadCurrentValue(entry, "EveningActivity.Name")); - Assert.Equal(DayOfWeek.Friday, ReadCurrentValue(entry, "EveningActivity.Day")); - Assert.Equal("A music pub quiz.", ReadCurrentValue(entry, "EveningActivity.Description")); - Assert.Empty(ReadCurrentValue(entry, "EveningActivity.Notes")); - Assert.Equal(5.0m, ReadCurrentValue(entry, "EveningActivity.CoverCharge")); - Assert.True(ReadCurrentValue(entry, "EveningActivity.IsTeamBased")); - Assert.NotNull(ReadCurrentValue(entry, "EveningActivity.Champions")); - Assert.Equal("Dazed and Confused", ReadCurrentValue(entry, "EveningActivity.Champions.Name")); - Assert.Equal( - new[] { "Robert", "Jimmy", "John", "Jason" }, ReadCurrentValue>(entry, "EveningActivity.Champions.Members")); - Assert.NotNull(ReadCurrentValue(entry, "EveningActivity.RunnersUp")); - Assert.Equal("Banksy", ReadCurrentValue(entry, "EveningActivity.RunnersUp.Name")); - Assert.Empty(ReadCurrentValue>(entry, "EveningActivity.RunnersUp.Members")); - Assert.NotNull(ReadCurrentValue(entry, "FeaturedTeam")); - Assert.Equal("Not In This Lifetime", ReadCurrentValue(entry, "FeaturedTeam.Name")); - Assert.Equal(new[] { "Slash", "Axl" }, ReadCurrentValue>(entry, "FeaturedTeam.Members")); - } - - protected void AssertPropertiesModified(EntityEntry entry, bool expected) - { - Assert.Equal(expected, IsModified(entry, "Name")); - Assert.Equal(expected, IsModified(entry, "LunchtimeActivity.Name")); - Assert.Equal(expected, IsModified(entry, "LunchtimeActivity.Day")); - Assert.Equal(expected, IsModified(entry, "LunchtimeActivity.Description")); - Assert.Equal(expected, IsModified(entry, "LunchtimeActivity.Notes")); - Assert.Equal(expected, IsModified(entry, "LunchtimeActivity.CoverCharge")); - Assert.Equal(expected, IsModified(entry, "LunchtimeActivity.IsTeamBased")); - Assert.Equal(expected, IsModified(entry, "LunchtimeActivity.Champions.Name")); - Assert.Equal(expected, IsModified(entry, "LunchtimeActivity.Champions.Members")); - Assert.Equal(expected, IsModified(entry, "LunchtimeActivity.RunnersUp.Name")); - Assert.Equal(expected, IsModified(entry, "LunchtimeActivity.RunnersUp.Members")); - Assert.Equal(expected, IsModified(entry, "EveningActivity.Name")); - Assert.Equal(expected, IsModified(entry, "EveningActivity.Day")); - Assert.Equal(expected, IsModified(entry, "EveningActivity.Description")); - Assert.Equal(expected, IsModified(entry, "EveningActivity.Notes")); - Assert.Equal(expected, IsModified(entry, "EveningActivity.CoverCharge")); - Assert.Equal(expected, IsModified(entry, "EveningActivity.IsTeamBased")); - Assert.Equal(expected, IsModified(entry, "EveningActivity.Champions.Name")); - Assert.Equal(expected, IsModified(entry, "EveningActivity.Champions.Members")); - Assert.Equal(expected, IsModified(entry, "EveningActivity.RunnersUp.Name")); - Assert.Equal(expected, IsModified(entry, "EveningActivity.RunnersUp.Members")); - Assert.Equal(expected, IsModified(entry, "FeaturedTeam.Name")); - Assert.Equal(expected, IsModified(entry, "FeaturedTeam.Members")); - } - - protected static TValue ReadCurrentValue(EntityEntry entry, string propertyChain) - => entry.GetInfrastructure().GetCurrentValue(FindProperty(entry, propertyChain)); - - protected static TValue ReadOriginalValue(EntityEntry entry, string propertyChain) - => entry.GetInfrastructure().GetOriginalValue((IProperty)FindProperty(entry, propertyChain)); - - protected static void WriteCurrentValue(EntityEntry entry, string propertyChain, object? value) - => entry.GetInfrastructure().SetProperty(FindProperty(entry, propertyChain), value, isMaterialization: false); - - protected static void WriteOriginalValue(EntityEntry entry, string propertyChain, object? value) - => entry.GetInfrastructure().SetOriginalValue(FindProperty(entry, propertyChain), value); - - protected static bool IsModified(EntityEntry entry, string propertyChain) - => entry.GetInfrastructure().IsModified((IProperty)FindProperty(entry, propertyChain)); - - protected static EntityEntry TrackFromQuery(DbContext context, Pub pub) - => new( - context.GetService().StartTrackingFromQuery( - context.Model.FindEntityType(typeof(Pub))!, pub, new ValueBuffer())); + context.ChangeTracker.DetectChanges(); - protected static void MarkModified(EntityEntry entry, string propertyChain, bool modified) - => entry.GetInfrastructure().SetPropertyModified((IProperty)FindProperty(entry, propertyChain), isModified: modified); + var coverChargeEntry = entry.ComplexProperty(e => e.EveningActivity).Property(e => e.CoverCharge); + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(coverChargeEntry.IsModified); + Assert.Equal(3.0m, coverChargeEntry.CurrentValue); + Assert.Equal(5.0m, coverChargeEntry.OriginalValue); + } - protected static IPropertyBase FindProperty(EntityEntry entry, string propertyChain) + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + public virtual void Detects_changes_in_complex_record_type_properties(bool trackFromQuery) { - var internalEntry = entry.GetInfrastructure(); - var names = propertyChain.Split("."); - var currentType = (ITypeBase)internalEntry.EntityType; + using var context = CreateContext(); + var pub = CreatePubWithRecords(); + + var entry = trackFromQuery ? TrackFromQuery(context, pub) : context.Attach(pub); + + Assert.Equal(EntityState.Unchanged, entry.State); + AssertPropertyValues(entry); + AssertPropertiesModified(entry, false); - IPropertyBase property = null!; - foreach (var name in names) + pub.EveningActivity = pub.EveningActivity with { - var complexProperty = currentType.FindComplexProperty(name); - if (complexProperty != null) - { - currentType = complexProperty.ComplexType; - property = complexProperty; - } - else + Champions = pub.EveningActivity.Champions with { - property = currentType.FindProperty(name)!; + Members = new() + { + "1", + "2", + "3" + } } - } + }; + + context.ChangeTracker.DetectChanges(); + + var membersEntry = entry.ComplexProperty(e => e.EveningActivity).ComplexProperty(e => e.Champions).Property(e => e.Members); + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(membersEntry.IsModified); + Assert.Equal(new[] { "1", "2", "3" }, membersEntry.CurrentValue); + Assert.Equal(new[] { "Robert", "Jimmy", "John", "Jason" }, membersEntry.OriginalValue); + + pub.LunchtimeActivity = pub.LunchtimeActivity with { Day = DayOfWeek.Wednesday }; + context.ChangeTracker.DetectChanges(); + + var dayEntry = entry.ComplexProperty(e => e.LunchtimeActivity).Property(e => e.Day); + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(dayEntry.IsModified); + Assert.Equal(DayOfWeek.Wednesday, dayEntry.CurrentValue); + Assert.Equal(DayOfWeek.Monday, dayEntry.OriginalValue); + + pub.EveningActivity = pub.EveningActivity with { CoverCharge = 3.0m }; + context.ChangeTracker.DetectChanges(); + + var coverChargeEntry = entry.ComplexProperty(e => e.EveningActivity).Property(e => e.CoverCharge); + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(coverChargeEntry.IsModified); + Assert.Equal(3.0m, coverChargeEntry.CurrentValue); + Assert.Equal(5.0m, coverChargeEntry.OriginalValue); + } + + protected void AssertPropertyValues(EntityEntry entry) + { + Assert.Equal("The FBI", entry.Property("Name").CurrentValue); + + var lunchtimeEntry = entry.ComplexProperty("LunchtimeActivity"); + Assert.Equal("Pub Quiz", lunchtimeEntry.Property("Name").CurrentValue); + Assert.Equal(DayOfWeek.Monday, lunchtimeEntry.Property("Day").CurrentValue); + Assert.Equal("A general knowledge pub quiz.", lunchtimeEntry.Property("Description").CurrentValue); + Assert.Equal(new[] { "One", "Two", "Three" }, lunchtimeEntry.Property("Notes").CurrentValue); + Assert.Equal(2.0m, lunchtimeEntry.Property("CoverCharge").CurrentValue); + Assert.True((bool)lunchtimeEntry.Property("IsTeamBased").CurrentValue!); + + var lunchtimeChampionsEntry = lunchtimeEntry.ComplexProperty("Champions"); + Assert.Equal("Clueless", lunchtimeChampionsEntry.Property("Name").CurrentValue); + Assert.Equal(new[] { "Boris", "David", "Theresa" }, lunchtimeChampionsEntry.Property("Members").CurrentValue); + + var lunchtimeRunnersUpEntry = lunchtimeEntry.ComplexProperty("RunnersUp"); + Assert.Equal("ZZ", lunchtimeRunnersUpEntry.Property("Name").CurrentValue); + Assert.Equal(new[] { "Has Beard", "Has Beard", "Is Called Beard" }, lunchtimeRunnersUpEntry.Property("Members").CurrentValue); + + var eveningEntry = entry.ComplexProperty("EveningActivity"); + Assert.Equal("Music Quiz", eveningEntry.Property("Name").CurrentValue); + Assert.Equal(DayOfWeek.Friday, eveningEntry.Property("Day").CurrentValue); + Assert.Equal("A music pub quiz.", eveningEntry.Property("Description").CurrentValue); + Assert.Empty((IEnumerable)eveningEntry.Property("Notes").CurrentValue!); + Assert.Equal(5.0m, eveningEntry.Property("CoverCharge").CurrentValue); + Assert.True((bool)eveningEntry.Property("IsTeamBased").CurrentValue!); + + var eveningChampionsEntry = eveningEntry.ComplexProperty("Champions"); + Assert.Equal("Dazed and Confused", eveningChampionsEntry.Property("Name").CurrentValue); + Assert.Equal(new[] { "Robert", "Jimmy", "John", "Jason" }, eveningChampionsEntry.Property("Members").CurrentValue); + + var eveningRunnersUpEntry = eveningEntry.ComplexProperty("RunnersUp"); + Assert.Equal("Banksy", eveningRunnersUpEntry.Property("Name").CurrentValue); + Assert.Empty((IEnumerable)eveningRunnersUpEntry.Property("Members").CurrentValue!); + + var teamEntry = entry.ComplexProperty("FeaturedTeam"); + Assert.Equal("Not In This Lifetime", teamEntry.Property("Name").CurrentValue); + Assert.Equal(new[] { "Slash", "Axl" }, teamEntry.Property("Members").CurrentValue); + } - return property; + protected void AssertPropertiesModified(EntityEntry entry, bool expected) + { + Assert.Equal("The FBI", entry.Property("Name").CurrentValue); + + var lunchtimeEntry = entry.ComplexProperty("LunchtimeActivity"); + Assert.Equal(expected, lunchtimeEntry.Property("Name").IsModified); + Assert.Equal(expected, lunchtimeEntry.Property("Day").IsModified); + Assert.Equal(expected, lunchtimeEntry.Property("Description").IsModified); + Assert.Equal(expected, lunchtimeEntry.Property("Notes").IsModified); + Assert.Equal(expected, lunchtimeEntry.Property("CoverCharge").IsModified); + Assert.Equal(expected, lunchtimeEntry.Property("IsTeamBased").IsModified); + + var lunchtimeChampionsEntry = lunchtimeEntry.ComplexProperty("Champions"); + Assert.Equal(expected, lunchtimeChampionsEntry.Property("Name").IsModified); + Assert.Equal(expected, lunchtimeChampionsEntry.Property("Members").IsModified); + + var lunchtimeRunnersUpEntry = lunchtimeEntry.ComplexProperty("RunnersUp"); + Assert.Equal(expected, lunchtimeRunnersUpEntry.Property("Name").IsModified); + Assert.Equal(expected, lunchtimeRunnersUpEntry.Property("Members").IsModified); + + var eveningEntry = entry.ComplexProperty("EveningActivity"); + Assert.Equal(expected, eveningEntry.Property("Name").IsModified); + Assert.Equal(expected, eveningEntry.Property("Day").IsModified); + Assert.Equal(expected, eveningEntry.Property("Description").IsModified); + Assert.Equal(expected, eveningEntry.Property("Notes").IsModified); + Assert.Equal(expected, eveningEntry.Property("CoverCharge").IsModified); + Assert.Equal(expected, eveningEntry.Property("IsTeamBased").IsModified); + + var eveningChampionsEntry = eveningEntry.ComplexProperty("Champions"); + Assert.Equal(expected, eveningChampionsEntry.Property("Name").IsModified); + Assert.Equal(expected, eveningChampionsEntry.Property("Members").IsModified); + + var eveningRunnersUpEntry = eveningEntry.ComplexProperty("RunnersUp"); + Assert.Equal(expected, eveningRunnersUpEntry.Property("Name").IsModified); + Assert.Equal(expected, eveningRunnersUpEntry.Property("Members").IsModified!); + + var teamEntry = entry.ComplexProperty("FeaturedTeam"); + Assert.Equal(expected, teamEntry.Property("Name").IsModified); + Assert.Equal(expected, teamEntry.Property("Members").IsModified); } + protected static EntityEntry TrackFromQuery(DbContext context, TEntity pub) + where TEntity : class + => new( + context.GetService().StartTrackingFromQuery( + context.Model.FindEntityType(typeof(TEntity))!, pub, new ValueBuffer())); + protected virtual void ExecuteWithStrategyInTransaction( Action testOperation, Action? nestedTestOperation1 = null, @@ -456,36 +818,828 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con b.ComplexProperty(e => e.FeaturedTeam); b.ComplexProperty(e => e.FeaturedTeam); }); - } - } - protected class Pub - { - public Guid Id { get; set; } - public string Name { get; set; } = null!; - public Activity LunchtimeActivity { get; set; } = null!; - public Activity EveningActivity { get; set; } = null!; - public Team FeaturedTeam { get; set; } = null!; - // Complex collections: - // public List Activities { get; set; } = null!; - // public List? Teams { get; set; } - } + modelBuilder.Entity( + b => + { + b.ComplexProperty( + e => e.LunchtimeActivity, b => + { + b.ComplexProperty(e => e!.Champions); + b.ComplexProperty(e => e!.RunnersUp); + }); + b.ComplexProperty( + e => e.EveningActivity, b => + { + b.ComplexProperty(e => e.Champions); + b.ComplexProperty(e => e.RunnersUp); + }); + b.ComplexProperty(e => e.FeaturedTeam); + b.ComplexProperty(e => e.FeaturedTeam); + }); - protected class Activity - { - public string Name { get; set; } = null!; - public decimal CoverCharge { get; set; } - public bool IsTeamBased { get; set; } - public string? Description { get; set; } - public string[]? Notes { get; set; } - public DayOfWeek Day { get; set; } - public Team Champions { get; set; } = null!; - public Team RunnersUp { get; set; } = null!; - } + modelBuilder.Entity( + b => + { + b.ComplexProperty( + e => e.LunchtimeActivity, b => + { + b.ComplexProperty(e => e!.Champions); + b.ComplexProperty(e => e!.RunnersUp); + }); + b.ComplexProperty( + e => e.EveningActivity, b => + { + b.ComplexProperty(e => e.Champions); + b.ComplexProperty(e => e.RunnersUp); + }); + b.ComplexProperty(e => e.FeaturedTeam); + b.ComplexProperty(e => e.FeaturedTeam); + }); - protected class Team - { - public string Name { get; set; } = null!; - public List Members { get; set; } = new(); + modelBuilder.Entity( + b => + { + b.ComplexProperty( + e => e.LunchtimeActivity, b => + { + b.ComplexProperty(e => e!.Champions); + b.ComplexProperty(e => e!.RunnersUp); + }); + b.ComplexProperty( + e => e.EveningActivity, b => + { + b.ComplexProperty(e => e.Champions); + b.ComplexProperty(e => e.RunnersUp); + }); + b.ComplexProperty(e => e.FeaturedTeam); + b.ComplexProperty(e => e.FeaturedTeam); + }); + + modelBuilder.Entity( + b => + { + b.ComplexProperty( + e => e.LunchtimeActivity, b => + { + b.ComplexProperty(e => e!.Champions); + b.ComplexProperty(e => e!.RunnersUp); + }); + b.ComplexProperty( + e => e.EveningActivity, b => + { + b.ComplexProperty(e => e.Champions); + b.ComplexProperty(e => e.RunnersUp); + }); + b.ComplexProperty(e => e.FeaturedTeam); + b.ComplexProperty(e => e.FeaturedTeam); + }); + + modelBuilder.Entity( + b => + { + b.ComplexProperty( + e => e.LunchtimeActivity, b => + { + b.ComplexProperty(e => e!.Champions); + b.ComplexProperty(e => e!.RunnersUp); + }); + b.ComplexProperty( + e => e.EveningActivity, b => + { + b.ComplexProperty(e => e.Champions); + b.ComplexProperty(e => e.RunnersUp); + }); + b.ComplexProperty(e => e.FeaturedTeam); + b.ComplexProperty(e => e.FeaturedTeam); + }); + + // TODO: Allow binding of complex properties to constructors + // modelBuilder.Entity( + // b => + // { + // b.ComplexProperty( + // e => e.LunchtimeActivity, b => + // { + // b.ComplexProperty(e => e!.Champions); + // b.ComplexProperty(e => e!.RunnersUp); + // }); + // b.ComplexProperty( + // e => e.EveningActivity, b => + // { + // b.ComplexProperty(e => e.Champions); + // b.ComplexProperty(e => e.RunnersUp); + // }); + // b.ComplexProperty(e => e.FeaturedTeam); + // b.ComplexProperty(e => e.FeaturedTeam); + // }); + + modelBuilder.Entity( + b => + { + b.ComplexProperty( + e => e.LunchtimeActivity, b => + { + b.ComplexProperty(e => e!.Champions); + b.ComplexProperty(e => e!.RunnersUp); + }); + b.ComplexProperty( + e => e.EveningActivity, b => + { + b.ComplexProperty(e => e.Champions); + b.ComplexProperty(e => e.RunnersUp); + }); + b.ComplexProperty(e => e.FeaturedTeam); + b.ComplexProperty(e => e.FeaturedTeam); + }); + } } -} + + protected static Pub CreatePub() + => new() + { + Id = Guid.NewGuid(), + Name = "The FBI", + LunchtimeActivity = new() + { + Name = "Pub Quiz", + Day = DayOfWeek.Monday, + Description = "A general knowledge pub quiz.", + Notes = new[] { "One", "Two", "Three" }, + CoverCharge = 2.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Clueless", + Members = + { + "Boris", + "David", + "Theresa" + } + }, + RunnersUp = new() + { + Name = "ZZ", + Members = + { + "Has Beard", + "Has Beard", + "Is Called Beard" + } + }, + }, + EveningActivity = new() + { + Name = "Music Quiz", + Day = DayOfWeek.Friday, + Description = "A music pub quiz.", + Notes = Array.Empty(), + CoverCharge = 5.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Dazed and Confused", + Members = + { + "Robert", + "Jimmy", + "John", + "Jason" + } + }, + RunnersUp = new() + { + Name = "Banksy", Members = new() + }, + }, + FeaturedTeam = new() + { + Name = "Not In This Lifetime", + Members = + { + "Slash", + "Axl" + } + } + }; + + protected class Pub + { + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public Activity LunchtimeActivity { get; set; } = null!; + public Activity EveningActivity { get; set; } = null!; + public Team FeaturedTeam { get; set; } = null!; + } + + protected class Activity + { + public string Name { get; set; } = null!; + public decimal? CoverCharge { get; set; } + public bool IsTeamBased { get; set; } + public string? Description { get; set; } + public string[]? Notes { get; set; } + public DayOfWeek Day { get; set; } + public Team Champions { get; set; } = null!; + public Team RunnersUp { get; set; } = null!; + } + + protected class Team + { + public string Name { get; set; } = null!; + public List Members { get; set; } = new(); + } + + protected static PubWithStructs CreatePubWithStructs() + => new() + { + Id = Guid.NewGuid(), + Name = "The FBI", + LunchtimeActivity = new() + { + Name = "Pub Quiz", + Day = DayOfWeek.Monday, + Description = "A general knowledge pub quiz.", + Notes = new[] { "One", "Two", "Three" }, + CoverCharge = 2.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Clueless", + Members = new() + { + "Boris", + "David", + "Theresa" + } + }, + RunnersUp = new() + { + Name = "ZZ", + Members = new() + { + "Has Beard", + "Has Beard", + "Is Called Beard" + } + }, + }, + EveningActivity = new() + { + Name = "Music Quiz", + Day = DayOfWeek.Friday, + Description = "A music pub quiz.", + Notes = Array.Empty(), + CoverCharge = 5.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Dazed and Confused", + Members = new() + { + "Robert", + "Jimmy", + "John", + "Jason" + } + }, + RunnersUp = new() { Name = "Banksy", Members = new() } + }, + FeaturedTeam = new() { Name = "Not In This Lifetime", Members = new() { "Slash", "Axl" } } + }; + + protected class PubWithStructs + { + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public ActivityStruct LunchtimeActivity { get; set; } + public ActivityStruct EveningActivity { get; set; } + public TeamStruct FeaturedTeam { get; set; } + } + + protected struct ActivityStruct + { + public string Name { get; set; } + public decimal? CoverCharge { get; set; } + public bool IsTeamBased { get; set; } + public string? Description { get; set; } + public string[]? Notes { get; set; } + public DayOfWeek Day { get; set; } + public TeamStruct Champions { get; set; } + public TeamStruct RunnersUp { get; set; } + } + + protected struct TeamStruct + { + public string Name { get; set; } + public List Members { get; set; } + } + + protected static PubWithReadonlyStructs CreatePubWithReadonlyStructs() + => new() + { + Id = Guid.NewGuid(), + Name = "The FBI", + LunchtimeActivity = new() + { + Name = "Pub Quiz", + Day = DayOfWeek.Monday, + Description = "A general knowledge pub quiz.", + Notes = new[] { "One", "Two", "Three" }, + CoverCharge = 2.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Clueless", + Members = new() + { + "Boris", + "David", + "Theresa" + } + }, + RunnersUp = new() + { + Name = "ZZ", + Members = new() + { + "Has Beard", + "Has Beard", + "Is Called Beard" + } + }, + }, + EveningActivity = new() + { + Name = "Music Quiz", + Day = DayOfWeek.Friday, + Description = "A music pub quiz.", + Notes = Array.Empty(), + CoverCharge = 5.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Dazed and Confused", + Members = new() + { + "Robert", + "Jimmy", + "John", + "Jason" + } + }, + RunnersUp = new() { Name = "Banksy", Members = new() } + }, + FeaturedTeam = new() { Name = "Not In This Lifetime", Members = new() { "Slash", "Axl" } } + }; + + protected class PubWithReadonlyStructs + { + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public ActivityReadonlyStruct LunchtimeActivity { get; set; } + public ActivityReadonlyStruct EveningActivity { get; set; } + public TeamReadonlyStruct FeaturedTeam { get; set; } + } + + protected readonly struct ActivityReadonlyStruct + { + public string Name { get; init; } + public decimal? CoverCharge { get; init; } + public bool IsTeamBased { get; init; } + public string? Description { get; init; } + public string[]? Notes { get; init; } + public DayOfWeek Day { get; init; } + public TeamReadonlyStruct Champions { get; init; } + public TeamReadonlyStruct RunnersUp { get; init; } + } + + protected readonly struct TeamReadonlyStruct + { + public string Name { get; init; } + public List Members { get; init; } + } + + protected static PubWithRecords CreatePubWithRecords() + => new() + { + Id = Guid.NewGuid(), + Name = "The FBI", + LunchtimeActivity = new() + { + Name = "Pub Quiz", + Day = DayOfWeek.Monday, + Description = "A general knowledge pub quiz.", + Notes = new[] { "One", "Two", "Three" }, + CoverCharge = 2.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Clueless", + Members = new() + { + "Boris", + "David", + "Theresa" + } + }, + RunnersUp = new() + { + Name = "ZZ", + Members = new() + { + "Has Beard", + "Has Beard", + "Is Called Beard" + } + }, + }, + EveningActivity = new() + { + Name = "Music Quiz", + Day = DayOfWeek.Friday, + Description = "A music pub quiz.", + Notes = Array.Empty(), + CoverCharge = 5.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Dazed and Confused", + Members = new() + { + "Robert", + "Jimmy", + "John", + "Jason" + } + }, + RunnersUp = new() { Name = "Banksy", Members = new() } + }, + FeaturedTeam = new() { Name = "Not In This Lifetime", Members = new() { "Slash", "Axl" } } + }; + + protected class PubWithRecords + { + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public ActivityRecord LunchtimeActivity { get; set; } = null!; + public ActivityRecord EveningActivity { get; set; } = null!; + public TeamRecord FeaturedTeam { get; set; } = null!; + } + + protected record ActivityRecord + { + public string Name { get; init; } = null!; + public decimal? CoverCharge { get; init; } + public bool IsTeamBased { get; init; } + public string? Description { get; init; } + public string[]? Notes { get; init; } + public DayOfWeek Day { get; init; } + public TeamRecord Champions { get; init; } = null!; + public TeamRecord RunnersUp { get; init; } = null!; + } + + protected record TeamRecord + { + public string Name { get; init; } = null!; + public List Members { get; init; } = null!; + } + + protected static FieldPub CreateFieldPub() + => new() + { + Id = Guid.NewGuid(), + Name = "The FBI", + LunchtimeActivity = new() + { + Name = "Pub Quiz", + Day = DayOfWeek.Monday, + Description = "A general knowledge pub quiz.", + Notes = new[] { "One", "Two", "Three" }, + CoverCharge = 2.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Clueless", + Members = + { + "Boris", + "David", + "Theresa" + } + }, + RunnersUp = new() + { + Name = "ZZ", + Members = + { + "Has Beard", + "Has Beard", + "Is Called Beard" + } + }, + }, + EveningActivity = new() + { + Name = "Music Quiz", + Day = DayOfWeek.Friday, + Description = "A music pub quiz.", + Notes = Array.Empty(), + CoverCharge = 5.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Dazed and Confused", + Members = + { + "Robert", + "Jimmy", + "John", + "Jason" + } + }, + RunnersUp = new() + { + Name = "Banksy", Members = new() + }, + }, + FeaturedTeam = new() + { + Name = "Not In This Lifetime", + Members = + { + "Slash", + "Axl" + } + } + }; + + protected class FieldPub + { + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public FieldActivity LunchtimeActivity = null!; + public FieldActivity EveningActivity = null!; + public FieldTeam FeaturedTeam = null!; + } + + protected class FieldActivity + { + public string Name = null!; + public decimal? CoverCharge; + public bool IsTeamBased; + public string? Description; + public string[]? Notes; + public DayOfWeek Day; + public Team Champions = null!; + public Team RunnersUp = null!; + } + + protected class FieldTeam + { + public string Name = null!; + public List Members = new(); + } + + protected static FieldPubWithStructs CreateFieldPubWithStructs() + => new() + { + Id = Guid.NewGuid(), + Name = "The FBI", + LunchtimeActivity = new() + { + Name = "Pub Quiz", + Day = DayOfWeek.Monday, + Description = "A general knowledge pub quiz.", + Notes = new[] { "One", "Two", "Three" }, + CoverCharge = 2.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Clueless", + Members = new() + { + "Boris", + "David", + "Theresa" + } + }, + RunnersUp = new() + { + Name = "ZZ", + Members = new() + { + "Has Beard", + "Has Beard", + "Is Called Beard" + } + }, + }, + EveningActivity = new() + { + Name = "Music Quiz", + Day = DayOfWeek.Friday, + Description = "A music pub quiz.", + Notes = Array.Empty(), + CoverCharge = 5.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Dazed and Confused", + Members = new() + { + "Robert", + "Jimmy", + "John", + "Jason" + } + }, + RunnersUp = new() { Name = "Banksy", Members = new() } + }, + FeaturedTeam = new() { Name = "Not In This Lifetime", Members = new() { "Slash", "Axl" } } + }; + + protected class FieldPubWithStructs + { + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public FieldActivityStruct LunchtimeActivity; + public FieldActivityStruct EveningActivity; + public FieldTeamStruct FeaturedTeam; + } + + protected struct FieldActivityStruct + { + public string Name; + public decimal? CoverCharge; + public bool IsTeamBased; + public string? Description; + public string[]? Notes; + public DayOfWeek Day; + public FieldTeamStruct Champions; + public FieldTeamStruct RunnersUp; + } + + protected struct FieldTeamStruct + { + public string Name; + public List Members; + } + + protected static FieldPubWithReadonlyStructs CreateFieldPubWithReadonlyStructs() + => new() + { + Id = Guid.NewGuid(), + Name = "The FBI", + LunchtimeActivity = + new( + "Pub Quiz", 2.0m, true, "A general knowledge pub quiz.", new[] { "One", "Two", "Three" }, DayOfWeek.Monday, + new( + "Clueless", new() + { + "Boris", + "David", + "Theresa" + }), new( + "ZZ", new() + { + "Has Beard", + "Has Beard", + "Is Called Beard" + })), + EveningActivity = + new( + "Music Quiz", 5.0m, true, "A music pub quiz.", Array.Empty(), DayOfWeek.Friday, + new( + "Dazed and Confused", new() + { + "Robert", + "Jimmy", + "John", + "Jason" + }), new("Banksy", new())), + FeaturedTeam = new("Not In This Lifetime", new() { "Slash", "Axl" }) + }; + + protected class FieldPubWithReadonlyStructs + { + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public FieldActivityReadonlyStruct LunchtimeActivity; + public FieldActivityReadonlyStruct EveningActivity; + public FieldTeamReadonlyStruct FeaturedTeam; + } + + protected readonly struct FieldActivityReadonlyStruct( + string name, + decimal? coverCharge, + bool isTeamBased, + string? description, + string[]? notes, + DayOfWeek day, + FieldTeamReadonlyStruct champions, + FieldTeamReadonlyStruct runnersUp) + { + public readonly string Name = name; + public readonly decimal? CoverCharge = coverCharge; + public readonly bool IsTeamBased = isTeamBased; + public readonly string? Description = description; + public readonly string[]? Notes = notes; + public readonly DayOfWeek Day = day; + public readonly FieldTeamReadonlyStruct Champions = champions; + public readonly FieldTeamReadonlyStruct RunnersUp = runnersUp; + } + + protected readonly struct FieldTeamReadonlyStruct(string name, List members) + { + public readonly string Name = name; + public readonly List Members = members; + } + + protected static FieldPubWithRecords CreateFieldPubWithRecords() + => new() + { + Id = Guid.NewGuid(), + Name = "The FBI", + LunchtimeActivity = new() + { + Name = "Pub Quiz", + Day = DayOfWeek.Monday, + Description = "A general knowledge pub quiz.", + Notes = new[] { "One", "Two", "Three" }, + CoverCharge = 2.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Clueless", + Members = new() + { + "Boris", + "David", + "Theresa" + } + }, + RunnersUp = new() + { + Name = "ZZ", + Members = new() + { + "Has Beard", + "Has Beard", + "Is Called Beard" + } + }, + }, + EveningActivity = new() + { + Name = "Music Quiz", + Day = DayOfWeek.Friday, + Description = "A music pub quiz.", + Notes = Array.Empty(), + CoverCharge = 5.0m, + IsTeamBased = true, + Champions = new() + { + Name = "Dazed and Confused", + Members = new() + { + "Robert", + "Jimmy", + "John", + "Jason" + } + }, + RunnersUp = new() { Name = "Banksy", Members = new() } + }, + FeaturedTeam = new() { Name = "Not In This Lifetime", Members = new() { "Slash", "Axl" } } + }; + + protected class FieldPubWithRecords + { + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public FieldActivityRecord LunchtimeActivity = null!; + public FieldActivityRecord EveningActivity = null!; + public FieldTeamRecord FeaturedTeam = null!; + } + + protected record FieldActivityRecord + { + public string Name = null!; + public decimal? CoverCharge; + public bool IsTeamBased; + public string? Description; + public string[]? Notes; + public DayOfWeek Day; + public FieldTeamRecord Champions = null!; + public FieldTeamRecord RunnersUp = null!; + } + + protected record FieldTeamRecord + { + public string Name = null!; + public List Members = null!; + } +} + diff --git a/test/EFCore.Tests/ChangeTracking/ComplexPropertyEntryTest.cs b/test/EFCore.Tests/ChangeTracking/ComplexPropertyEntryTest.cs new file mode 100644 index 00000000000..74c6f5b6d78 --- /dev/null +++ b/test/EFCore.Tests/ChangeTracking/ComplexPropertyEntryTest.cs @@ -0,0 +1,527 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +#nullable enable +using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; + +namespace Microsoft.EntityFrameworkCore.ChangeTracking; + +#pragma warning disable CS0649, CS0414 +public class ComplexPropertyEntryTest +{ + [ConditionalFact] + public void Can_obtain_underlying_state_entry() + { + using var context = new YogurtContext(); + var entity = context.Add(CreateYogurt()).Entity; + var entry = context.GetService().GetOrCreateEntry(entity); + + Assert.Same(entry, context.Entry(entity).ComplexProperty(e => e.Culture).GetInfrastructure()); + Assert.Same(entry, context.Entry(entity).ComplexProperty("Culture").GetInfrastructure()); + Assert.Same(entry, context.Entry(entity).ComplexProperty("Culture").GetInfrastructure()); + Assert.Same(entry, context.Entry((object)entity).ComplexProperty("Culture").GetInfrastructure()); + Assert.Same(entry, context.Entry((object)entity).ComplexProperty("Culture").GetInfrastructure()); + + Assert.Same(entry, context.Entry(entity).ComplexProperty(e => e.Culture).ComplexProperty(e => e.License).GetInfrastructure()); + Assert.Same(entry, context.Entry(entity).ComplexProperty("Culture").ComplexProperty(e => e.License).GetInfrastructure()); + Assert.Same(entry, context.Entry(entity).ComplexProperty("Culture").ComplexProperty("License").GetInfrastructure()); + Assert.Same(entry, context.Entry((object)entity).ComplexProperty("Culture").ComplexProperty("License").GetInfrastructure()); + Assert.Same(entry, context.Entry((object)entity).ComplexProperty("Culture").ComplexProperty("License").GetInfrastructure()); + } + + [ConditionalFact] + public void Can_get_metadata() + { + using var context = new YogurtContext(); + var entity = context.Add(CreateYogurt()).Entity; + + Assert.Equal("Culture", context.Entry(entity).ComplexProperty(e => e.Culture).Metadata.Name); + Assert.Equal("Culture", context.Entry(entity).ComplexProperty("Culture").Metadata.Name); + Assert.Equal("Culture", context.Entry(entity).ComplexProperty("Culture").Metadata.Name); + Assert.Equal("Culture", context.Entry((object)entity).ComplexProperty("Culture").Metadata.Name); + Assert.Equal("Culture", context.Entry((object)entity).ComplexProperty("Culture").Metadata.Name); + + Assert.Equal("License", context.Entry(entity).ComplexProperty(e => e.Culture).ComplexProperty(e => e.License).Metadata.Name); + Assert.Equal("License", context.Entry(entity).ComplexProperty("Culture").ComplexProperty(e => e.License).Metadata.Name); + Assert.Equal("License", context.Entry(entity).ComplexProperty("Culture").ComplexProperty("License").Metadata.Name); + Assert.Equal("License", context.Entry((object)entity).ComplexProperty("Culture").ComplexProperty("License").Metadata.Name); + Assert.Equal("License", context.Entry((object)entity).ComplexProperty("Culture").ComplexProperty("License").Metadata.Name); + } + + [ConditionalFact] + public void Can_get_property_entry_by_name() + { + using var context = new YogurtContext(); + var entity = context.Add(CreateYogurt()).Entity; + + Assert.Equal("Rating", context.Entry(entity).ComplexProperty(e => e.Culture).Property(e => e.Rating).Metadata.Name); + Assert.Equal("Rating", context.Entry(entity).ComplexProperty("Culture").Property(e => e.Rating).Metadata.Name); + Assert.Equal("Rating", context.Entry(entity).ComplexProperty("Culture").Property("Rating").Metadata.Name); + Assert.Equal("Rating", context.Entry((object)entity).ComplexProperty("Culture").Property("Rating").Metadata.Name); + Assert.Equal("Rating", context.Entry((object)entity).ComplexProperty("Culture").Property("Rating").Metadata.Name); + + Assert.Equal("Charge", context.Entry(entity).ComplexProperty(e => e.Culture).ComplexProperty(e => e.License).Property(e => e.Charge).Metadata.Name); + Assert.Equal("Charge", context.Entry(entity).ComplexProperty("Culture").ComplexProperty(e => e.License).Property(e => e.Charge).Metadata.Name); + Assert.Equal("Charge", context.Entry(entity).ComplexProperty("Culture").ComplexProperty("License").Property("Charge").Metadata.Name); + Assert.Equal("Charge", context.Entry((object)entity).ComplexProperty("Culture").ComplexProperty("License").Property("Charge").Metadata.Name); + Assert.Equal("Charge", context.Entry((object)entity).ComplexProperty("Culture").ComplexProperty("License").Property("Charge").Metadata.Name); + } + + [ConditionalFact] + public void Throws_when_wrong_generic_type_is_used_while_getting_property_entry_by_name() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.Culture); + + Assert.Equal( + CoreStrings.WrongGenericPropertyType("Rating", nameof(Culture), "int", "string"), + Assert.Throws(() => complexEntry.Property("Rating")).Message); + } + + [ConditionalFact] + public void Throws_when_wrong_property_name_is_used_while_getting_property_entry_by_name() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.Culture); + + Assert.Equal( + CoreStrings.PropertyNotFound("Chimp", complexEntry.Metadata.ComplexType.DisplayName()), + Assert.Throws(() => complexEntry.Property("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.PropertyNotFound("Chimp", complexEntry.Metadata.ComplexType.DisplayName()), + Assert.Throws(() => complexEntry.Property("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.PropertyNotFound("Chimp", complexEntry.Metadata.ComplexType.DisplayName()), + Assert.Throws(() => complexEntry.Property("Chimp").Metadata.Name).Message); + } + + [ConditionalFact] + public void Can_get_all_modified_properties() + { + using var context = new YogurtContext(); + var complexEntry = context.Attach(CreateYogurt()).ComplexProperty(e => e.Culture); + + var modified = complexEntry.Properties.Where(e => e.IsModified).Select(e => e.Metadata.Name).ToList(); + + Assert.Empty(modified); + + complexEntry.Property(e => e.Species).CurrentValue = "S"; + complexEntry.Property(e => e.Subspecies).CurrentValue = "SS"; + + modified = complexEntry.Properties.Where(e => e.IsModified).Select(e => e.Metadata.Name).ToList(); + + Assert.Equal(new List { "Species", "Subspecies" }, modified); + } + + [ConditionalFact] + public void Can_get_all_property_entries() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.Culture); + + Assert.Equal( + new List + { + "Rating", + "Species", + "Subspecies", + "Validation" + }, + complexEntry.Properties.Select(e => e.Metadata.Name).ToList()); + } + + [ConditionalFact] + public void Throws_when_wrong_generic_type_is_used_while_getting_complex_property_entry_by_name() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.Culture); + + Assert.Equal( + CoreStrings.WrongGenericPropertyType("License", "Culture", "License", "string"), + Assert.Throws(() => complexEntry.ComplexProperty("License")).Message); + + var nestedComplexEntry = complexEntry.ComplexProperty(e => e.License); + + Assert.Equal( + CoreStrings.WrongGenericPropertyType("Tag", "License", "Tag", "string"), + Assert.Throws(() => nestedComplexEntry.ComplexProperty("Tag")).Message); + } + + [ConditionalFact] + public void Throws_when_wrong_complex_property_name_is_used_while_getting_property_entry_by_name() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.Culture); + + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(complexEntry.Metadata.ComplexType.DisplayName(), "Chimp"), + Assert.Throws(() => complexEntry.ComplexProperty("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(complexEntry.Metadata.ComplexType.DisplayName(), "Chimp"), + Assert.Throws(() => complexEntry.ComplexProperty("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(complexEntry.Metadata.ComplexType.DisplayName(), "Chimp"), + Assert.Throws(() => complexEntry.ComplexProperty("Chimp").Metadata.Name).Message); + } + + [ConditionalFact] + public void Can_get_all_complex_property_entries() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.Culture); + + Assert.Equal( + new List { "License", "Manufacturer" }, + complexEntry.ComplexProperties.Select(e => e.Metadata.Name).ToList()); + + var nestedComplexEntry = complexEntry.ComplexProperty(e => e.License); + + Assert.Equal( + new List { "Tag", "Tog" }, + nestedComplexEntry.ComplexProperties.Select(e => e.Metadata.Name).ToList()); + } + + [ConditionalFact] + public void Can_obtain_underlying_state_entry_with_fields() + { + using var context = new YogurtContext(); + var entity = context.Add(CreateYogurt()).Entity; + var entry = context.GetService().GetOrCreateEntry(entity); + + Assert.Same(entry, context.Entry(entity).ComplexProperty(e => e.FieldCulture).GetInfrastructure()); + Assert.Same(entry, context.Entry(entity).ComplexProperty("FieldCulture").GetInfrastructure()); + Assert.Same(entry, context.Entry(entity).ComplexProperty("FieldCulture").GetInfrastructure()); + Assert.Same(entry, context.Entry((object)entity).ComplexProperty("FieldCulture").GetInfrastructure()); + Assert.Same(entry, context.Entry((object)entity).ComplexProperty("FieldCulture").GetInfrastructure()); + + Assert.Same(entry, context.Entry(entity).ComplexProperty(e => e.FieldCulture).ComplexProperty(e => e.License).GetInfrastructure()); + Assert.Same(entry, context.Entry(entity).ComplexProperty("FieldCulture").ComplexProperty(e => e.License).GetInfrastructure()); + Assert.Same(entry, context.Entry(entity).ComplexProperty("FieldCulture").ComplexProperty("License").GetInfrastructure()); + Assert.Same(entry, context.Entry((object)entity).ComplexProperty("FieldCulture").ComplexProperty("License").GetInfrastructure()); + Assert.Same(entry, context.Entry((object)entity).ComplexProperty("FieldCulture").ComplexProperty("License").GetInfrastructure()); + } + + [ConditionalFact] + public void Can_get_metadata_with_fields() + { + using var context = new YogurtContext(); + var entity = context.Add(CreateYogurt()).Entity; + + Assert.Equal("FieldCulture", context.Entry(entity).ComplexProperty(e => e.FieldCulture).Metadata.Name); + Assert.Equal("FieldCulture", context.Entry(entity).ComplexProperty("FieldCulture").Metadata.Name); + Assert.Equal("FieldCulture", context.Entry(entity).ComplexProperty("FieldCulture").Metadata.Name); + Assert.Equal("FieldCulture", context.Entry((object)entity).ComplexProperty("FieldCulture").Metadata.Name); + Assert.Equal("FieldCulture", context.Entry((object)entity).ComplexProperty("FieldCulture").Metadata.Name); + + Assert.Equal("License", context.Entry(entity).ComplexProperty(e => e.FieldCulture).ComplexProperty(e => e.License).Metadata.Name); + Assert.Equal("License", context.Entry(entity).ComplexProperty("FieldCulture").ComplexProperty(e => e.License).Metadata.Name); + Assert.Equal("License", context.Entry(entity).ComplexProperty("FieldCulture").ComplexProperty("License").Metadata.Name); + Assert.Equal("License", context.Entry((object)entity).ComplexProperty("FieldCulture").ComplexProperty("License").Metadata.Name); + Assert.Equal("License", context.Entry((object)entity).ComplexProperty("FieldCulture").ComplexProperty("License").Metadata.Name); + } + + [ConditionalFact] + public void Can_get_property_entry_by_name_with_fields() + { + using var context = new YogurtContext(); + var entity = context.Add(CreateYogurt()).Entity; + + Assert.Equal("Rating", context.Entry(entity).ComplexProperty(e => e.FieldCulture).Property(e => e.Rating).Metadata.Name); + Assert.Equal("Rating", context.Entry(entity).ComplexProperty("FieldCulture").Property(e => e.Rating).Metadata.Name); + Assert.Equal("Rating", context.Entry(entity).ComplexProperty("FieldCulture").Property("Rating").Metadata.Name); + Assert.Equal("Rating", context.Entry((object)entity).ComplexProperty("FieldCulture").Property("Rating").Metadata.Name); + Assert.Equal("Rating", context.Entry((object)entity).ComplexProperty("FieldCulture").Property("Rating").Metadata.Name); + + Assert.Equal("Charge", context.Entry(entity).ComplexProperty(e => e.FieldCulture).ComplexProperty(e => e.License).Property(e => e.Charge).Metadata.Name); + Assert.Equal("Charge", context.Entry(entity).ComplexProperty("FieldCulture").ComplexProperty(e => e.License).Property(e => e.Charge).Metadata.Name); + Assert.Equal("Charge", context.Entry(entity).ComplexProperty("FieldCulture").ComplexProperty("License").Property("Charge").Metadata.Name); + Assert.Equal("Charge", context.Entry((object)entity).ComplexProperty("FieldCulture").ComplexProperty("License").Property("Charge").Metadata.Name); + Assert.Equal("Charge", context.Entry((object)entity).ComplexProperty("FieldCulture").ComplexProperty("License").Property("Charge").Metadata.Name); + } + + [ConditionalFact] + public void Throws_when_wrong_generic_type_is_used_while_getting_property_entry_by_name_with_fields() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.FieldCulture); + + Assert.Equal( + CoreStrings.WrongGenericPropertyType("Rating", nameof(FieldCulture), "int", "string"), + Assert.Throws(() => complexEntry.Property("Rating")).Message); + } + + [ConditionalFact] + public void Throws_when_wrong_property_name_is_used_while_getting_property_entry_by_name_with_fields() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.FieldCulture); + + Assert.Equal( + CoreStrings.PropertyNotFound("Chimp", complexEntry.Metadata.ComplexType.DisplayName()), + Assert.Throws(() => complexEntry.Property("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.PropertyNotFound("Chimp", complexEntry.Metadata.ComplexType.DisplayName()), + Assert.Throws(() => complexEntry.Property("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.PropertyNotFound("Chimp", complexEntry.Metadata.ComplexType.DisplayName()), + Assert.Throws(() => complexEntry.Property("Chimp").Metadata.Name).Message); + } + + [ConditionalFact] + public void Can_get_all_modified_properties_with_fields() + { + using var context = new YogurtContext(); + var complexEntry = context.Attach(CreateYogurt()).ComplexProperty(e => e.FieldCulture); + + var modified = complexEntry.Properties.Where(e => e.IsModified).Select(e => e.Metadata.Name).ToList(); + + Assert.Empty(modified); + + complexEntry.Property(e => e.Species).CurrentValue = "S"; + complexEntry.Property(e => e.Subspecies).CurrentValue = "SS"; + + modified = complexEntry.Properties.Where(e => e.IsModified).Select(e => e.Metadata.Name).ToList(); + + Assert.Equal(new List { "Species", "Subspecies" }, modified); + } + + [ConditionalFact] + public void Can_get_all_property_entries_with_fields() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.FieldCulture); + + Assert.Equal( + new List + { + "Rating", + "Species", + "Subspecies", + "Validation" + }, + complexEntry.Properties.Select(e => e.Metadata.Name).ToList()); + } + + [ConditionalFact] + public void Throws_when_wrong_generic_type_is_used_while_getting_complex_property_entry_by_name_with_fields() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.FieldCulture); + + Assert.Equal( + CoreStrings.WrongGenericPropertyType("License", "FieldCulture", "FieldLicense", "string"), + Assert.Throws(() => complexEntry.ComplexProperty("License")).Message); + + var nestedComplexEntry = complexEntry.ComplexProperty(e => e.License); + + Assert.Equal( + CoreStrings.WrongGenericPropertyType("Tag", "FieldLicense", "FieldTag", "string"), + Assert.Throws(() => nestedComplexEntry.ComplexProperty("Tag")).Message); + } + + [ConditionalFact] + public void Throws_when_wrong_complex_property_name_is_used_while_getting_property_entry_by_name_with_fields() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.FieldCulture); + + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(complexEntry.Metadata.ComplexType.DisplayName(), "Chimp"), + Assert.Throws(() => complexEntry.ComplexProperty("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(complexEntry.Metadata.ComplexType.DisplayName(), "Chimp"), + Assert.Throws(() => complexEntry.ComplexProperty("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(complexEntry.Metadata.ComplexType.DisplayName(), "Chimp"), + Assert.Throws(() => complexEntry.ComplexProperty("Chimp").Metadata.Name).Message); + } + + [ConditionalFact] + public void Can_get_all_complex_property_entries_with_fields() + { + using var context = new YogurtContext(); + var complexEntry = context.Add(CreateYogurt()).ComplexProperty(e => e.FieldCulture); + + Assert.Equal( + new List { "License", "Manufacturer" }, + complexEntry.ComplexProperties.Select(e => e.Metadata.Name).ToList()); + + var nestedComplexEntry = complexEntry.ComplexProperty(e => e.License); + + Assert.Equal( + new List { "Tag", "Tog" }, + nestedComplexEntry.ComplexProperties.Select(e => e.Metadata.Name).ToList()); + } + + private static Yogurt CreateYogurt() + => new() + { + Id = Guid.NewGuid(), + Culture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldCulture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + }; + + private class Yogurt + { + public Guid Id { get; set; } + public Culture Culture { get; set; } + public FieldCulture FieldCulture; + } + + private class YogurtContext : DbContext + { + protected internal override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder + .UseInternalServiceProvider(InMemoryFixture.DefaultServiceProvider) + .UseInMemoryDatabase(GetType().FullName!); + + protected internal override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity( + b => + { + b.ComplexProperty( + e => e.Culture, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + + b.ComplexProperty( + e => e.FieldCulture, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + }); + } + + private struct Culture + { + public string Species { get; set; } + public string? Subspecies { get; set; } + public int Rating { get; set; } + public bool? Validation { get; set; } + public Manufacturer Manufacturer { get; set; } + public License License { get; set; } + } + + private class Manufacturer + { + public string? Name { get; set; } + public int Rating { get; set; } + public Tag Tag { get; set; } = null!; + public Tog Tog { get; set; } + } + + private struct License + { + public string Title { get; set; } + public decimal Charge { get; set; } + public Tag Tag { get; set; } + public Tog Tog { get; set; } + } + + private class Tag + { + public string? Text { get; set; } + } + + private struct Tog + { + public string? Text { get; set; } + } + + private struct FieldCulture + { + public string Species; + public string? Subspecies; + public int Rating; + public bool? Validation; + public FieldManufacturer Manufacturer; + public FieldLicense License; + } + + private class FieldManufacturer + { + public string? Name; + public int Rating; + public FieldTag Tag = null!; + public FieldTog Tog; + } + + private struct FieldLicense + { + public string Title; + public decimal Charge; + public FieldTag Tag; + public FieldTog Tog; + } + + private class FieldTag + { + public string? Text; + } + + private struct FieldTog + { + public string? Text; + } +} diff --git a/test/EFCore.Tests/ChangeTracking/EntityEntryTest.cs b/test/EFCore.Tests/ChangeTracking/EntityEntryTest.cs index 0ee200bf1cd..b561a7221d1 100644 --- a/test/EFCore.Tests/ChangeTracking/EntityEntryTest.cs +++ b/test/EFCore.Tests/ChangeTracking/EntityEntryTest.cs @@ -1,6 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value +#pragma warning disable CS0414 // Field is assigned but its value is never used + using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; namespace Microsoft.EntityFrameworkCore.ChangeTracking; @@ -85,14 +89,14 @@ private class StoreGenerated { public int Id { get; set; } - public Dependent Dependent { get; set; } + public Dependent? Dependent { get; set; } } private class StoreGeneratedWithSentinel { public int Id { get; set; } - public DependentWithSentinel Dependent { get; set; } + public DependentWithSentinel? Dependent { get; set; } } private class NotStoreGenerated @@ -122,14 +126,14 @@ private class Dependent { public int Id { get; set; } - public StoreGenerated Principal { get; set; } + public StoreGenerated? Principal { get; set; } } private class DependentWithSentinel { public int Id { get; set; } - public StoreGeneratedWithSentinel Principal { get; set; } + public StoreGeneratedWithSentinel? Principal { get; set; } } private class KeySetContext : DbContext @@ -139,13 +143,26 @@ protected internal override void OnConfiguring(DbContextOptionsBuilder optionsBu .UseInternalServiceProvider(InMemoryFixture.DefaultServiceProvider) .UseInMemoryDatabase(nameof(KeySetContext)); - public DbSet StoreGenerated { get; set; } - public DbSet StoreGeneratedWithSentinel { get; set; } - public DbSet NotStoreGenerated { get; set; } - public DbSet CompositeStoreGenerated { get; set; } - public DbSet CompositeStoreGeneratedWithSentinel { get; set; } - public DbSet CompositeNotStoreGenerated { get; set; } - public DbSet Dependent { get; set; } + public DbSet StoreGenerated + => Set(); + + public DbSet StoreGeneratedWithSentinel + => Set(); + + public DbSet NotStoreGenerated + => Set(); + + public DbSet CompositeStoreGenerated + => Set(); + + public DbSet CompositeStoreGeneratedWithSentinel + => Set(); + + public DbSet CompositeNotStoreGenerated + => Set(); + + public DbSet Dependent + => Set(); protected internal override void OnModelCreating(ModelBuilder modelBuilder) { @@ -190,7 +207,7 @@ protected internal override void OnModelCreating(ModelBuilder modelBuilder) public void Detached_entities_are_not_returned_from_the_change_tracker() { using var context = new FreezerContext(); - var entity = new Chunky { Id = 808 }; + var entity = CreateChunky(808); context.Attach(entity); Assert.Single(context.ChangeTracker.Entries()); @@ -212,7 +229,7 @@ public void Detached_entities_are_not_returned_from_the_change_tracker() public void Can_obtain_entity_instance() { using var context = new FreezerContext(); - var entity = new Chunky(); + var entity = CreateChunky(); context.Add(entity); Assert.Same(entity, context.Entry(entity).Entity); @@ -223,7 +240,7 @@ public void Can_obtain_entity_instance() public void Can_obtain_context() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Same(context, context.Entry(entity).Context); Assert.Same(context, context.Entry((object)entity).Context); @@ -233,7 +250,7 @@ public void Can_obtain_context() public void Can_obtain_underlying_state_entry() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var entry = context.GetService().GetOrCreateEntry(entity); Assert.Same(entry, context.Entry(entity).GetInfrastructure()); @@ -244,7 +261,7 @@ public void Can_obtain_underlying_state_entry() public void Can_get_metadata() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var entityType = context.Model.FindEntityType(typeof(Chunky)); Assert.Same(entityType, context.Entry(entity).Metadata); @@ -255,7 +272,7 @@ public void Can_get_metadata() public void Can_get_and_change_state() { using var context = new FreezerContext(); - var entity = new Chunky(); + var entity = CreateChunky(); var entry = context.Add(entity).GetInfrastructure(); context.Entry(entity).State = EntityState.Modified; @@ -271,7 +288,7 @@ public void Can_get_and_change_state() public void Cannot_set_invalid_state() { using var context = new FreezerContext(); - var entity = new Chunky(); + var entity = CreateChunky(); Assert.Equal( CoreStrings.InvalidEnumValue("-1", "value", typeof(EntityState).FullName), @@ -335,7 +352,7 @@ public void Can_use_entry_to_change_state_to_Unknown() private void ChangeStateOnEntry(EntityState initialState, EntityState expectedState) { using var context = new FreezerContext(); - var entry = context.Add(new Chunky()); + var entry = context.Add(CreateChunky()); entry.State = initialState; entry.State = expectedState; @@ -347,7 +364,7 @@ private void ChangeStateOnEntry(EntityState initialState, EntityState expectedSt public void Can_get_property_entry_by_name() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal("Monkey", context.Entry(entity).Property("Monkey").Metadata.Name); Assert.Equal("Monkey", context.Entry((object)entity).Property("Monkey").Metadata.Name); @@ -357,7 +374,7 @@ public void Can_get_property_entry_by_name() public void Can_get_generic_property_entry_by_name() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal("Monkey", context.Entry(entity).Property("Monkey").Metadata.Name); } @@ -366,7 +383,7 @@ public void Can_get_generic_property_entry_by_name() public void Can_get_property_entry_by_IProperty() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var property = context.Entry(entity).Metadata.FindProperty("Monkey")!; Assert.Same(property, context.Entry(entity).Property(property).Metadata); @@ -377,7 +394,7 @@ public void Can_get_property_entry_by_IProperty() public void Can_get_generic_property_entry_by_IProperty() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var property = context.Entry(entity).Metadata.FindProperty("Monkey")!; Assert.Same(property, context.Entry(entity).Property(property).Metadata); @@ -387,7 +404,7 @@ public void Can_get_generic_property_entry_by_IProperty() public void Throws_when_wrong_generic_type_is_used_while_getting_property_entry_by_name() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal( CoreStrings.WrongGenericPropertyType("Monkey", entity.GetType().ShortDisplayName(), "int", "string"), @@ -398,7 +415,7 @@ public void Throws_when_wrong_generic_type_is_used_while_getting_property_entry_ public void Can_get_generic_property_entry_by_lambda() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal("Monkey", context.Entry(entity).Property(e => e.Monkey).Metadata.Name); } @@ -407,7 +424,7 @@ public void Can_get_generic_property_entry_by_lambda() public void Throws_when_wrong_property_name_is_used_while_getting_property_entry_by_name() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal( CoreStrings.PropertyNotFound("Chimp", entity.GetType().Name), @@ -424,7 +441,7 @@ public void Throws_when_wrong_property_name_is_used_while_getting_property_entry public void Can_get_reference_entry_by_name() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal("Garcia", context.Entry(entity).Reference("Garcia").Metadata.Name); Assert.Equal("Garcia", context.Entry((object)entity).Reference("Garcia").Metadata.Name); @@ -434,7 +451,7 @@ public void Can_get_reference_entry_by_name() public void Can_get_generic_reference_entry_by_name() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal("Garcia", context.Entry(entity).Reference("Garcia").Metadata.Name); } @@ -443,7 +460,7 @@ public void Can_get_generic_reference_entry_by_name() public void Can_get_generic_reference_entry_by_lambda() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal("Garcia", context.Entry(entity).Reference(e => e.Garcia).Metadata.Name); } @@ -452,7 +469,7 @@ public void Can_get_generic_reference_entry_by_lambda() public void Can_get_reference_entry_by_INavigationBase() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var navigationBase = (INavigationBase)context.Entry(entity).Metadata.FindNavigation("Garcia")!; Assert.Same(navigationBase, context.Entry(entity).Reference(navigationBase).Metadata); @@ -463,7 +480,7 @@ public void Can_get_reference_entry_by_INavigationBase() public void Can_get_generic_reference_entry_by_INavigationBase() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var navigationBase = (INavigationBase)context.Entry(entity).Metadata.FindNavigation("Garcia")!; Assert.Same(navigationBase, context.Entry(entity).Reference(navigationBase).Metadata); @@ -473,7 +490,7 @@ public void Can_get_generic_reference_entry_by_INavigationBase() public void Throws_when_wrong_reference_name_is_used_while_getting_property_entry_by_name() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal( CoreStrings.PropertyNotFound("Chimp", entity.GetType().Name), @@ -490,7 +507,7 @@ public void Throws_when_wrong_reference_name_is_used_while_getting_property_entr public void Throws_when_accessing_property_as_reference() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal( CoreStrings.NavigationIsProperty( @@ -520,7 +537,7 @@ public void Throws_when_accessing_property_as_reference() public void Throws_when_accessing_collection_as_reference() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; Assert.Equal( CoreStrings.ReferenceIsCollection( @@ -571,7 +588,7 @@ public void Throws_when_accessing_collection_as_reference() public void Can_get_collection_entry_by_name() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; Assert.Equal("Monkeys", context.Entry(entity).Collection("Monkeys").Metadata.Name); Assert.Equal("Monkeys", context.Entry((object)entity).Collection("Monkeys").Metadata.Name); @@ -581,7 +598,7 @@ public void Can_get_collection_entry_by_name() public void Can_get_generic_collection_entry_by_name() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; Assert.Equal("Monkeys", context.Entry(entity).Collection("Monkeys").Metadata.Name); } @@ -590,16 +607,16 @@ public void Can_get_generic_collection_entry_by_name() public void Can_get_generic_collection_entry_by_lambda() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; - Assert.Equal("Monkeys", context.Entry(entity).Collection(e => e.Monkeys).Metadata.Name); + Assert.Equal("Monkeys", context.Entry(entity).Collection(e => e.Monkeys!).Metadata.Name); } [ConditionalFact] public void Can_get_collection_entry_by_INavigationBase() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; var navigationBase = (INavigationBase)context.Entry(entity).Metadata.FindNavigation("Monkeys")!; Assert.Same(navigationBase, context.Entry(entity).Collection(navigationBase).Metadata); @@ -610,7 +627,7 @@ public void Can_get_collection_entry_by_INavigationBase() public void Can_get_generic_collection_entry_by_INavigationBase() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; var navigationBase = (INavigationBase)context.Entry(entity).Metadata.FindNavigation("Monkeys")!; Assert.Same(navigationBase, context.Entry(entity).Collection(navigationBase).Metadata); @@ -620,7 +637,7 @@ public void Can_get_generic_collection_entry_by_INavigationBase() public void Throws_when_wrong_collection_name_is_used_while_getting_property_entry_by_name() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; Assert.Equal( CoreStrings.PropertyNotFound("Chimp", entity.GetType().Name), @@ -639,7 +656,7 @@ public void Throws_when_wrong_collection_name_is_used_while_getting_property_ent public void Throws_when_accessing_property_as_collection() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; Assert.Equal( CoreStrings.NavigationIsProperty( @@ -664,7 +681,7 @@ public void Throws_when_accessing_property_as_collection() public void Throws_when_accessing_reference_as_collection() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal( CoreStrings.CollectionIsReference( @@ -709,7 +726,7 @@ public void Throws_when_accessing_reference_as_collection() public void Can_get_property_entry_by_name_using_Member() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var entry = context.Entry(entity).Member("Monkey"); Assert.Equal("Monkey", entry.Metadata.Name); @@ -724,7 +741,7 @@ public void Can_get_property_entry_by_name_using_Member() public void Throws_when_wrong_property_name_is_used_while_getting_property_entry_by_name_using_Member() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal( CoreStrings.PropertyNotFound("Chimp", entity.GetType().Name), @@ -738,7 +755,7 @@ public void Throws_when_wrong_property_name_is_used_while_getting_property_entry public void Can_get_reference_entry_by_name_using_Member() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var entry = context.Entry(entity).Member("Garcia"); Assert.Equal("Garcia", entry.Metadata.Name); @@ -753,7 +770,7 @@ public void Can_get_reference_entry_by_name_using_Member() public void Can_get_collection_entry_by_name_using_Member() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; var entry = context.Entry(entity).Member("Monkeys"); Assert.Equal("Monkeys", entry.Metadata.Name); @@ -768,7 +785,7 @@ public void Can_get_collection_entry_by_name_using_Member() public void Can_get_property_entry_by_IPropertyBase_using_Member() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var propertyBase = (IPropertyBase)context.Entry(entity).Metadata.FindProperty("Monkey")!; var entry = context.Entry(entity).Member(propertyBase); @@ -784,7 +801,7 @@ public void Can_get_property_entry_by_IPropertyBase_using_Member() public void Can_get_reference_entry_by_IPropertyBase_using_Member() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var propertyBase = (IPropertyBase)context.Entry(entity).Metadata.FindNavigation("Garcia")!; var entry = context.Entry(entity).Member(propertyBase); @@ -800,7 +817,7 @@ public void Can_get_reference_entry_by_IPropertyBase_using_Member() public void Can_get_collection_entry_by_IPropertyBase_using_Member() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; var propertyBase = (IPropertyBase)context.Entry(entity).Metadata.FindNavigation("Monkeys")!; var entry = context.Entry(entity).Member(propertyBase); @@ -816,7 +833,7 @@ public void Can_get_collection_entry_by_IPropertyBase_using_Member() public void Throws_when_wrong_property_name_is_used_while_getting_property_entry_by_name_using_Navigation() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal( CoreStrings.PropertyNotFound("Chimp", entity.GetType().Name), @@ -831,7 +848,7 @@ public void Throws_when_wrong_property_name_is_used_while_getting_property_entry public void Can_get_reference_entry_by_name_using_Navigation() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var entry = context.Entry(entity).Navigation("Garcia"); Assert.Equal("Garcia", entry.Metadata.Name); @@ -846,7 +863,7 @@ public void Can_get_reference_entry_by_name_using_Navigation() public void Can_get_collection_entry_by_name_using_Navigation() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; var entry = context.Entry(entity).Navigation("Monkeys"); Assert.Equal("Monkeys", entry.Metadata.Name); @@ -861,7 +878,7 @@ public void Can_get_collection_entry_by_name_using_Navigation() public void Can_get_reference_entry_by_INavigationBase_using_Navigation() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; var navigationBase = (INavigationBase)context.Entry(entity).Metadata.FindNavigation("Garcia")!; var entry = context.Entry(entity).Navigation(navigationBase); @@ -877,7 +894,7 @@ public void Can_get_reference_entry_by_INavigationBase_using_Navigation() public void Can_get_collection_entry_by_INavigationBase_using_Navigation() { using var context = new FreezerContext(); - var entity = context.Add(new Cherry()).Entity; + var entity = context.Add(CreateCherry()).Entity; var navigationBase = (INavigationBase)context.Entry(entity).Metadata.FindNavigation("Monkeys")!; var entry = context.Entry(entity).Navigation(navigationBase); @@ -893,7 +910,7 @@ public void Can_get_collection_entry_by_INavigationBase_using_Navigation() public void Throws_when_accessing_property_as_navigation() { using var context = new FreezerContext(); - var entity = context.Add(new Chunky()).Entity; + var entity = context.Add(CreateChunky()).Entity; Assert.Equal( CoreStrings.NavigationIsProperty( @@ -912,7 +929,7 @@ public void Throws_when_accessing_property_as_navigation() public void Can_get_all_modified_properties() { using var context = new FreezerContext(); - var entity = context.Attach(new Chunky()).Entity; + var entity = context.Attach(CreateChunky()).Entity; var modified = context.Entry(entity).Properties .Where(e => e.IsModified).Select(e => e.Metadata.Name).ToList(); @@ -942,19 +959,23 @@ public void Can_get_all_member_entries() "GarciaId", "Monkey", "Nonkey", + "Culture", + "Milk", "Garcia" }, - context.Attach(new Chunky()).Members.Select(e => e.Metadata.Name).ToList()); + context.Attach(CreateChunky()).Members.Select(e => e.Metadata.Name).ToList()); Assert.Equal( new List { "Id", "Garcia", + "Culture", + "Milk", "Baked", "Monkeys" }, - context.Attach(new Cherry()).Members.Select(e => e.Metadata.Name).ToList()); + context.Attach(CreateCherry()).Members.Select(e => e.Metadata.Name).ToList()); Assert.Equal( new List @@ -979,11 +1000,11 @@ public void Can_get_all_property_entries() "Monkey", "Nonkey" }, - context.Attach(new Chunky()).Properties.Select(e => e.Metadata.Name).ToList()); + context.Attach(CreateChunky()).Properties.Select(e => e.Metadata.Name).ToList()); Assert.Equal( new List { "Id", "Garcia" }, - context.Attach(new Cherry()).Properties.Select(e => e.Metadata.Name).ToList()); + context.Attach(CreateCherry()).Properties.Select(e => e.Metadata.Name).ToList()); Assert.Equal( new List @@ -1001,11 +1022,11 @@ public void Can_get_all_navigation_entries() using var context = new FreezerContext(); Assert.Equal( new List { "Garcia" }, - context.Attach(new Chunky()).Navigations.Select(e => e.Metadata.Name).ToList()); + context.Attach(CreateChunky()).Navigations.Select(e => e.Metadata.Name).ToList()); Assert.Equal( new List { "Baked", "Monkeys" }, - context.Attach(new Cherry()).Navigations.Select(e => e.Metadata.Name).ToList()); + context.Attach(CreateCherry()).Navigations.Select(e => e.Metadata.Name).ToList()); Assert.Equal( new List { "Garcia" }, @@ -1018,11 +1039,11 @@ public void Can_get_all_reference_entries() using var context = new FreezerContext(); Assert.Equal( new List { "Garcia" }, - context.Attach(new Chunky()).References.Select(e => e.Metadata.Name).ToList()); + context.Attach(CreateChunky()).References.Select(e => e.Metadata.Name).ToList()); Assert.Equal( new List { "Baked" }, - context.Attach(new Cherry()).References.Select(e => e.Metadata.Name).ToList()); + context.Attach(CreateCherry()).References.Select(e => e.Metadata.Name).ToList()); Assert.Equal( new List { "Garcia" }, @@ -1033,33 +1054,562 @@ public void Can_get_all_reference_entries() public void Can_get_all_collection_entries() { using var context = new FreezerContext(); - Assert.Empty(context.Attach(new Chunky()).Collections.Select(e => e.Metadata.Name).ToList()); + Assert.Empty(context.Attach(CreateChunky()).Collections.Select(e => e.Metadata.Name).ToList()); Assert.Equal( new List { "Monkeys" }, - context.Attach(new Cherry()).Collections.Select(e => e.Metadata.Name).ToList()); + context.Attach(CreateCherry()).Collections.Select(e => e.Metadata.Name).ToList()); Assert.Empty(context.Attach(new Half()).Collections.Select(e => e.Metadata.Name).ToList()); } + [ConditionalFact] + public void Can_get_complex_property_entry_by_name() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateChunky()).Entity; + + Assert.Equal("Culture", context.Entry(entity).ComplexProperty("Culture").Metadata.Name); + Assert.Equal("Milk", context.Entry(entity).ComplexProperty("Milk").Metadata.Name); + Assert.Equal("Culture", context.Entry((object)entity).ComplexProperty("Culture").Metadata.Name); + Assert.Equal("Milk", context.Entry((object)entity).ComplexProperty("Milk").Metadata.Name); + } + + [ConditionalFact] + public void Can_get_generic_complex_property_entry_by_name() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateChunky()).Entity; + + Assert.Equal("Culture", context.Entry(entity).ComplexProperty("Culture").Metadata.Name); + Assert.Equal("Milk", context.Entry(entity).ComplexProperty("Milk").Metadata.Name); + } + + [ConditionalFact] + public void Can_get_property_complex_entry_by_IComplexProperty() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateChunky()).Entity; + var cultureProperty = context.Entry(entity).Metadata.FindComplexProperty("Culture")!; + var milkProperty = context.Entry(entity).Metadata.FindComplexProperty("Milk")!; + + Assert.Same(cultureProperty, context.Entry(entity).ComplexProperty(cultureProperty).Metadata); + Assert.Same(milkProperty, context.Entry((object)entity).ComplexProperty(milkProperty).Metadata); + } + + [ConditionalFact] + public void Can_get_generic_complex_property_entry_by_IComplexProperty() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateChunky()).Entity; + var cultureProperty = context.Entry(entity).Metadata.FindComplexProperty("Culture")!; + var milkProperty = context.Entry(entity).Metadata.FindComplexProperty("Milk")!; + + Assert.Same(cultureProperty, context.Entry(entity).ComplexProperty(cultureProperty).Metadata); + Assert.Same(milkProperty, context.Entry(entity).ComplexProperty(milkProperty).Metadata); + } + + [ConditionalFact] + public void Throws_when_wrong_generic_type_is_used_while_getting_complex_property_entry_by_name() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateChunky()).Entity; + + Assert.Equal( + CoreStrings.WrongGenericPropertyType("Culture", entity.GetType().ShortDisplayName(), "Culture", "string"), + Assert.Throws(() => context.Entry(entity).ComplexProperty("Culture")).Message); + + Assert.Equal( + CoreStrings.WrongGenericPropertyType("Milk", entity.GetType().ShortDisplayName(), "Milk", "string"), + Assert.Throws(() => context.Entry(entity).ComplexProperty("Milk")).Message); + } + + [ConditionalFact] + public void Can_get_generic_complex_property_entry_by_lambda() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateChunky()).Entity; + + Assert.Equal("Culture", context.Entry(entity).ComplexProperty(e => e.Culture).Metadata.Name); + Assert.Equal("Milk", context.Entry(entity).ComplexProperty(e => e.Milk).Metadata.Name); + } + + [ConditionalFact] + public void Throws_when_wrong_complex_property_name_is_used_while_getting_property_entry_by_name() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateChunky()).Entity; + + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(entity.GetType().Name, "Chimp"), + Assert.Throws(() => context.Entry(entity).ComplexProperty("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(entity.GetType().Name, "Chimp"), + Assert.Throws(() => context.Entry((object)entity).ComplexProperty("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(entity.GetType().Name, "Chimp"), + Assert.Throws(() => context.Entry(entity).ComplexProperty("Chimp").Metadata.Name).Message); + } + + [ConditionalFact] + public void Throws_when_accessing_complex_property_as_reference() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateChunky()).Entity; + + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Reference("Culture").Metadata.Name).Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry((object)entity).Reference("Culture").Metadata.Name) + .Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Reference("Culture").Metadata.Name) + .Message); + + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Milk", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Reference("Milk").Metadata.Name).Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Milk", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry((object)entity).Reference("Milk").Metadata.Name) + .Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Milk", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Reference("Milk").Metadata.Name) + .Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Milk", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Reference(e => e.Milk).Metadata.Name).Message); + } + + [ConditionalFact] + public void Throws_when_accessing_complex_property_as_collection() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Collection("Culture").Metadata.Name).Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry((object)entity).Collection("Culture").Metadata.Name) + .Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Collection("Culture").Metadata.Name) + .Message); + + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Milk", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Collection("Milk").Metadata.Name).Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Milk", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry((object)entity).Collection("Milk").Metadata.Name) + .Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Milk", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Collection("Milk").Metadata.Name) + .Message); + } + + [ConditionalFact] + public void Can_get_complex_property_entry_by_name_using_Member() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateChunky()).Entity; + + var entry = context.Entry(entity).Member("Culture"); + Assert.Equal("Culture", entry.Metadata.Name); + Assert.IsType(entry); + + entry = context.Entry((object)entity).Member("Culture"); + Assert.Equal("Culture", entry.Metadata.Name); + Assert.IsType(entry); + + entry = context.Entry(entity).Member("Milk"); + Assert.Equal("Milk", entry.Metadata.Name); + Assert.IsType(entry); + + entry = context.Entry((object)entity).Member("Milk"); + Assert.Equal("Milk", entry.Metadata.Name); + Assert.IsType(entry); + } + + [ConditionalFact] + public void Can_get_complex_property_entry_by_IPropertyBase_using_Member() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateChunky()).Entity; + var cultureBase = (IPropertyBase)context.Entry(entity).Metadata.FindComplexProperty("Culture")!; + var milkBase = (IPropertyBase)context.Entry(entity).Metadata.FindComplexProperty("Milk")!; + + var entry = context.Entry(entity).Member(cultureBase); + Assert.Same(cultureBase, entry.Metadata); + Assert.IsType(entry); + + entry = context.Entry((object)entity).Member(cultureBase); + Assert.Same(cultureBase, entry.Metadata); + Assert.IsType(entry); + + entry = context.Entry(entity).Member(milkBase); + Assert.Same(milkBase, entry.Metadata); + Assert.IsType(entry); + + entry = context.Entry((object)entity).Member(milkBase); + Assert.Same(milkBase, entry.Metadata); + Assert.IsType(entry); + } + + [ConditionalFact] + public void Throws_when_accessing_complex_property_as_navigation() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateChunky()).Entity; + + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Navigation("Culture").Metadata.Name).Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry((object)entity).Navigation("Culture").Metadata.Name) + .Message); + } + + [ConditionalFact] + public void Can_get_all_complex_property_entries() + { + using var context = new FreezerContext(); + Assert.Equal( + new List { "Culture", "Milk" }, + context.Attach(CreateChunky()).ComplexProperties.Select(e => e.Metadata.Name).ToList()); + + Assert.Equal( + new List { "Culture", "Milk" }, + context.Attach(CreateCherry()).ComplexProperties.Select(e => e.Metadata.Name).ToList()); + + Assert.Empty(context.Attach(new Half()).ComplexProperties); + } + + [ConditionalFact] + public void Can_get_complex_property_entry_by_name_using_fields() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + + Assert.Equal("Culture", context.Entry(entity).ComplexProperty("Culture").Metadata.Name); + Assert.Equal("Milk", context.Entry(entity).ComplexProperty("Milk").Metadata.Name); + Assert.Equal("Culture", context.Entry((object)entity).ComplexProperty("Culture").Metadata.Name); + Assert.Equal("Milk", context.Entry((object)entity).ComplexProperty("Milk").Metadata.Name); + } + + [ConditionalFact] + public void Can_get_generic_complex_property_entry_by_name_using_fields() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + + Assert.Equal("Culture", context.Entry(entity).ComplexProperty("Culture").Metadata.Name); + Assert.Equal("Milk", context.Entry(entity).ComplexProperty("Milk").Metadata.Name); + } + + [ConditionalFact] + public void Can_get_property_complex_entry_by_IComplexProperty_using_fields() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + var cultureProperty = context.Entry(entity).Metadata.FindComplexProperty("Culture")!; + var milkProperty = context.Entry(entity).Metadata.FindComplexProperty("Milk")!; + + Assert.Same(cultureProperty, context.Entry(entity).ComplexProperty(cultureProperty).Metadata); + Assert.Same(milkProperty, context.Entry((object)entity).ComplexProperty(milkProperty).Metadata); + } + + [ConditionalFact] + public void Can_get_generic_complex_property_entry_by_IComplexProperty_using_fields() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + var cultureProperty = context.Entry(entity).Metadata.FindComplexProperty("Culture")!; + var milkProperty = context.Entry(entity).Metadata.FindComplexProperty("Milk")!; + + Assert.Same(cultureProperty, context.Entry(entity).ComplexProperty(cultureProperty).Metadata); + Assert.Same(milkProperty, context.Entry(entity).ComplexProperty(milkProperty).Metadata); + } + + [ConditionalFact] + public void Throws_when_wrong_generic_type_is_used_while_getting_complex_property_entry_by_name_using_fields() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + + Assert.Equal( + CoreStrings.WrongGenericPropertyType("Culture", entity.GetType().ShortDisplayName(), "FieldCulture", "string"), + Assert.Throws(() => context.Entry(entity).ComplexProperty("Culture")).Message); + + Assert.Equal( + CoreStrings.WrongGenericPropertyType("Milk", entity.GetType().ShortDisplayName(), "FieldMilk", "string"), + Assert.Throws(() => context.Entry(entity).ComplexProperty("Milk")).Message); + } + + [ConditionalFact] + public void Can_get_generic_complex_property_entry_by_lambda_using_fields() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + + Assert.Equal("Culture", context.Entry(entity).ComplexProperty(e => e.Culture).Metadata.Name); + Assert.Equal("Milk", context.Entry(entity).ComplexProperty(e => e.Milk).Metadata.Name); + } + + [ConditionalFact] + public void Throws_when_wrong_complex_property_name_is_used_while_getting_property_entry_by_name_using_fields() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(entity.GetType().Name, "Chimp"), + Assert.Throws(() => context.Entry(entity).ComplexProperty("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(entity.GetType().Name, "Chimp"), + Assert.Throws(() => context.Entry((object)entity).ComplexProperty("Chimp").Metadata.Name).Message); + Assert.Equal( + CoreStrings.ComplexPropertyNotFound(entity.GetType().Name, "Chimp"), + Assert.Throws(() => context.Entry(entity).ComplexProperty("Chimp").Metadata.Name).Message); + } + + [ConditionalFact] + public void Throws_when_accessing_complex_property_as_reference_using_fields() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Reference("Culture").Metadata.Name).Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry((object)entity).Reference("Culture").Metadata.Name) + .Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Reference("Culture").Metadata.Name) + .Message); + + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Milk", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Reference("Milk").Metadata.Name).Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Milk", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry((object)entity).Reference("Milk").Metadata.Name) + .Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Milk", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Reference("Milk").Metadata.Name) + .Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Milk", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Reference(e => e.Milk).Metadata.Name).Message); + } + + [ConditionalFact] + public void Can_get_complex_property_entry_by_name_using_Member_using_fields() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + + var entry = context.Entry(entity).Member("Culture"); + Assert.Equal("Culture", entry.Metadata.Name); + Assert.IsType(entry); + + entry = context.Entry((object)entity).Member("Culture"); + Assert.Equal("Culture", entry.Metadata.Name); + Assert.IsType(entry); + + entry = context.Entry(entity).Member("Milk"); + Assert.Equal("Milk", entry.Metadata.Name); + Assert.IsType(entry); + + entry = context.Entry((object)entity).Member("Milk"); + Assert.Equal("Milk", entry.Metadata.Name); + Assert.IsType(entry); + } + + [ConditionalFact] + public void Can_get_complex_property_entry_by_IPropertyBase_using_Member_using_fields() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + var cultureBase = (IPropertyBase)context.Entry(entity).Metadata.FindComplexProperty("Culture")!; + var milkBase = (IPropertyBase)context.Entry(entity).Metadata.FindComplexProperty("Milk")!; + + var entry = context.Entry(entity).Member(cultureBase); + Assert.Same(cultureBase, entry.Metadata); + Assert.IsType(entry); + + entry = context.Entry((object)entity).Member(cultureBase); + Assert.Same(cultureBase, entry.Metadata); + Assert.IsType(entry); + + entry = context.Entry(entity).Member(milkBase); + Assert.Same(milkBase, entry.Metadata); + Assert.IsType(entry); + + entry = context.Entry((object)entity).Member(milkBase); + Assert.Same(milkBase, entry.Metadata); + Assert.IsType(entry); + } + + [ConditionalFact] + public void Throws_when_accessing_complex_property_as_navigation_using_fields() + { + using var context = new FreezerContext(); + var entity = context.Add(CreateCherry()).Entity; + + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry(entity).Navigation("Culture").Metadata.Name).Message); + Assert.Equal( + CoreStrings.NavigationIsProperty( + "Culture", entity.GetType().Name, + nameof(EntityEntry.Reference), nameof(EntityEntry.Collection), nameof(EntityEntry.Property)), + Assert.Throws(() => context.Entry((object)entity).Navigation("Culture").Metadata.Name) + .Message); + } + + [ConditionalFact] + public void Can_get_all_complex_property_entries_using_fields() + { + using var context = new FreezerContext(); + Assert.Equal( + new List { "Culture", "Milk" }, + context.Attach(CreateCherry(76)).ComplexProperties.Select(e => e.Metadata.Name).ToList()); + + Assert.Equal( + new List { "Culture", "Milk" }, + context.Attach(CreateCherry(77)).ComplexProperties.Select(e => e.Metadata.Name).ToList()); + + Assert.Empty(context.Attach(new Half()).ComplexProperties); + } + private class Chunky { public int Monkey { get; set; } - public string Nonkey { get; set; } + public string? Nonkey { get; set; } public int Id { get; set; } public int GarciaId { get; set; } - public Cherry Garcia { get; set; } + public Cherry? Garcia { get; set; } + + public Culture Culture { get; set; } + public Milk Milk { get; set; } = null!; } + private static Chunky CreateChunky(int id = 0) + => new() + { + Id = id, + Culture = new() + { + License = new() { Charge = 1.0m, Tag = new() { Text = "Ta1" }, Title = "Ti1", Tog = new() { Text = "To1" } }, + Manufacturer = new() { Name = "M1", Rating = 7, Tag = new() { Text = "Ta2" }, Tog = new() { Text = "To2"} }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() { Charge = 1.0m, Tag = new() { Text = "Ta1" }, Title = "Ti1", Tog = new() { Text = "To1" } }, + Manufacturer = new() { Name = "M1", Rating = 7, Tag = new() { Text = "Ta2" }, Tog = new() { Text = "To2"} }, + Rating = 8, + Species = "S1", + Validation = false + } + }; + + private static Cherry CreateCherry(int id = 0) + => new() + { + Id = id, + Culture = new() + { + License = new() { Charge = 1.0m, Tag = new() { Text = "Ta1" }, Title = "Ti1", Tog = new() { Text = "To1" } }, + Manufacturer = new() { Name = "M1", Rating = 7, Tag = new() { Text = "Ta2" }, Tog = new() { Text = "To2"} }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() { Charge = 1.0m, Tag = new() { Text = "Ta1" }, Title = "Ti1", Tog = new() { Text = "To1" } }, + Manufacturer = new() { Name = "M1", Rating = 7, Tag = new() { Text = "Ta2" }, Tog = new() { Text = "To2"} }, + Rating = 8, + Species = "S1", + Validation = false + } + }; + private class Cherry { public int Garcia { get; set; } public int Id { get; set; } - public ICollection Monkeys { get; set; } + public ICollection? Monkeys { get; set; } + + public Half? Baked { get; set; } - public Half Baked { get; set; } + public FieldCulture Culture; + public FieldMilk Milk = null!; } private class Half @@ -1068,7 +1618,99 @@ private class Half public int Id { get; set; } public int? GarciaId { get; set; } - public Cherry Garcia { get; set; } + public Cherry? Garcia { get; set; } + } + + private struct Culture + { + public string Species { get; set; } + public string? Subspecies { get; set; } + public int Rating { get; set; } + public bool? Validation { get; set; } + public Manufacturer Manufacturer { get; set; } + public License License { get; set; } + } + + private class Milk + { + public string Species { get; set; } = null!; + public string? Subspecies { get; set; } + public int Rating { get; set; } + public bool? Validation { get; set; } + public Manufacturer Manufacturer { get; set; } = null!; + public License License { get; set; } + } + + private class Manufacturer + { + public string? Name { get; set; } + public int Rating { get; set; } + public Tag Tag { get; set; } = null!; + public Tog Tog { get; set; } + } + + private struct License + { + public string Title { get; set; } + public decimal Charge { get; set; } + public Tag Tag { get; set; } + public Tog Tog { get; set; } + } + + private class Tag + { + public string? Text { get; set; } + } + + private struct Tog + { + public string? Text { get; set; } + } + + private struct FieldCulture + { + public string Species; + public string? Subspecies; + public int Rating; + public bool? Validation; + public FieldManufacturer Manufacturer; + public FieldLicense License; + } + + private class FieldMilk + { + public string Species = null!; + public string? Subspecies; + public int Rating; + public bool? Validation ; + public FieldManufacturer Manufacturer = null!; + public FieldLicense License; + } + + private class FieldManufacturer + { + public string? Name; + public int Rating; + public FieldTag Tag = null!; + public FieldTog Tog; + } + + private struct FieldLicense + { + public string Title; + public decimal Charge; + public FieldTag Tag; + public FieldTog Tog; + } + + private class FieldTag + { + public string? Text; + } + + private struct FieldTog + { + public string? Text; } private class FreezerContext : DbContext @@ -1078,12 +1720,90 @@ protected internal override void OnConfiguring(DbContextOptionsBuilder optionsBu .UseInternalServiceProvider(InMemoryFixture.DefaultServiceProvider) .UseInMemoryDatabase(nameof(FreezerContext)); - public DbSet Icecream { get; set; } + public DbSet Icecream + => Set(); protected internal override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity().Property(e => e.Id).ValueGeneratedNever(); - modelBuilder.Entity().Property(e => e.Id).ValueGeneratedNever(); + modelBuilder.Entity( + b => + { + b.Property(e => e.Id).ValueGeneratedNever(); + + b.ComplexProperty( + e => e.Culture, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + + b.ComplexProperty( + e => e.Milk, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + }); + + modelBuilder.Entity( + b => + { + b.Property(e => e.Id).ValueGeneratedNever(); + + b.ComplexProperty( + e => e.Culture, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + + b.ComplexProperty( + e => e.Milk, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + }); } } } diff --git a/test/EFCore.Tests/ChangeTracking/MemberEntryTest.cs b/test/EFCore.Tests/ChangeTracking/MemberEntryTest.cs index 433fdd08fe5..c5c518f2be5 100644 --- a/test/EFCore.Tests/ChangeTracking/MemberEntryTest.cs +++ b/test/EFCore.Tests/ChangeTracking/MemberEntryTest.cs @@ -1,6 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value +#pragma warning disable CS0414 // Field is assigned but its value is never used + namespace Microsoft.EntityFrameworkCore.ChangeTracking; public class MemberEntryTest @@ -9,7 +13,7 @@ public class MemberEntryTest public void Can_get_back_reference_property() { using var context = new FreezerContext(); - var entity = new Chunky(); + var entity = CreateChunky(); context.Add(entity); var entityEntry = context.Entry(entity); @@ -20,7 +24,7 @@ public void Can_get_back_reference_property() public void Can_get_back_reference_reference() { using var context = new FreezerContext(); - var entity = new Chunky(); + var entity = CreateChunky(); context.Add(entity); var entityEntry = context.Entry(entity); @@ -31,7 +35,7 @@ public void Can_get_back_reference_reference() public void Can_get_back_reference_collection() { using var context = new FreezerContext(); - var entity = new Cherry(); + var entity = CreateCherry(); context.Add(entity); var entityEntry = context.Entry(entity); @@ -42,7 +46,7 @@ public void Can_get_back_reference_collection() public void Can_get_metadata_property() { using var context = new FreezerContext(); - var entity = new Chunky(); + var entity = CreateChunky(); context.Add(entity); Assert.Equal("Monkey", context.Entry(entity).Member("Monkey").Metadata.Name); @@ -52,7 +56,7 @@ public void Can_get_metadata_property() public void Can_get_metadata_reference() { using var context = new FreezerContext(); - var entity = new Chunky(); + var entity = CreateChunky(); context.Add(entity); Assert.Equal("Garcia", context.Entry(entity).Member("Garcia").Metadata.Name); @@ -62,7 +66,7 @@ public void Can_get_metadata_reference() public void Can_get_metadata_collection() { using var context = new FreezerContext(); - var entity = new Cherry(); + var entity = CreateCherry(); context.Add(entity); Assert.Equal("Monkeys", context.Entry(entity).Member("Monkeys").Metadata.Name); @@ -72,7 +76,7 @@ public void Can_get_metadata_collection() public void Can_get_and_set_current_value_property() { using var context = new FreezerContext(); - var entity = new Chunky(); + var entity = CreateChunky(); context.Add(entity); var property = context.Entry(entity).Member("GarciaId"); @@ -90,8 +94,8 @@ public void Can_get_and_set_current_value_property() public void Can_get_and_set_current_value_reference() { using var context = new FreezerContext(); - var cherry = new Cherry(); - var chunky = new Chunky(); + var cherry = CreateCherry(); + var chunky = CreateChunky(); context.AddRange(chunky, cherry); var reference = context.Entry(chunky).Member("Garcia"); @@ -101,14 +105,14 @@ public void Can_get_and_set_current_value_reference() reference.CurrentValue = cherry; Assert.Same(cherry, chunky.Garcia); - Assert.Same(chunky, cherry.Monkeys.Single()); + Assert.Same(chunky, cherry.Monkeys!.Single()); Assert.Equal(cherry.Id, chunky.GarciaId); Assert.Same(cherry, reference.CurrentValue); reference.CurrentValue = null; Assert.Null(chunky.Garcia); - Assert.Empty(cherry.Monkeys); + Assert.Empty(cherry.Monkeys!); Assert.Null(chunky.GarciaId); Assert.Null(reference.CurrentValue); } @@ -117,8 +121,8 @@ public void Can_get_and_set_current_value_reference() public void Can_get_and_set_current_value_collection() { using var context = new FreezerContext(); - var cherry = new Cherry(); - var chunky = new Chunky(); + var cherry = CreateCherry(); + var chunky = CreateChunky(); context.AddRange(chunky, cherry); var collection = context.Entry(cherry).Member("Monkeys"); @@ -128,7 +132,7 @@ public void Can_get_and_set_current_value_collection() collection.CurrentValue = new List { chunky }; Assert.Same(cherry, chunky.Garcia); - Assert.Same(chunky, cherry.Monkeys.Single()); + Assert.Same(chunky, cherry.Monkeys!.Single()); Assert.Equal(cherry.Id, chunky.GarciaId); Assert.Same(chunky, ((ICollection)collection.CurrentValue).Single()); @@ -144,8 +148,9 @@ public void Can_get_and_set_current_value_collection() public void IsModified_tracks_state_of_FK_property_reference() { using var context = new FreezerContext(); - var cherry = new Cherry(); - var chunky = new Chunky { Garcia = cherry }; + var cherry = CreateCherry(); + var chunky = CreateChunky(); + chunky.Garcia = cherry; cherry.Monkeys = new List { chunky }; context.AttachRange(cherry, chunky); @@ -167,7 +172,26 @@ public void IsModified_tracks_state_of_FK_property_reference() public void IsModified_tracks_state_of_owned_entity() { using var context = new FreezerContext(); - var chunky = new Chunky { Chunk = new Chunk { Size = 1, Shape = "Sphere" } }; + var chunky = new Chunky + { + Chunk = new Chunk { Size = 1, Shape = "Sphere" }, + Culture = new() + { + License = new() { Charge = 1.0m, Tag = new() { Text = "Ta1" }, Title = "Ti1", Tog = new() { Text = "To1" } }, + Manufacturer = new() { Name = "M1", Rating = 7, Tag = new() { Text = "Ta2" }, Tog = new() { Text = "To2"} }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() { Charge = 1.0m, Tag = new() { Text = "Ta1" }, Title = "Ti1", Tog = new() { Text = "To1" } }, + Manufacturer = new() { Name = "M1", Rating = 7, Tag = new() { Text = "Ta2" }, Tog = new() { Text = "To2"} }, + Rating = 8, + Species = "S1", + Validation = false + } + }; context.Add(chunky); var reference = context.Entry(chunky).Member(nameof(Chunky.Chunk)); @@ -192,9 +216,11 @@ public void IsModified_tracks_state_of_owned_entity() public void IsModified_can_set_fk_to_modified_collection() { using var context = new FreezerContext(); - var cherry = new Cherry(); - var chunky1 = new Chunky { Garcia = cherry }; - var chunky2 = new Chunky { Garcia = cherry }; + var cherry = CreateCherry(); + var chunky1 = CreateChunky(); + chunky1.Garcia = cherry; + var chunky2 = CreateChunky(); + chunky2.Garcia = cherry; cherry.Monkeys = new List { chunky1, chunky2 }; context.AttachRange(cherry, chunky1, chunky2); @@ -217,30 +243,283 @@ public void IsModified_can_set_fk_to_modified_collection() Assert.Equal(EntityState.Unchanged, context.Entry(chunky2).State); } + [ConditionalFact] + public void Can_get_back_complex_property() + { + using var context = new FreezerContext(); + var entity = CreateChunky(); + context.Add(entity); + + var entityEntry = context.Entry(entity); + Assert.Same(entityEntry.Entity, entityEntry.Member("Culture").EntityEntry.Entity); + Assert.Same(entityEntry.Entity, entityEntry.Member("Milk").EntityEntry.Entity); + } + + [ConditionalFact] + public void Can_get_metadata_complex_property() + { + using var context = new FreezerContext(); + var entity = CreateChunky(); + context.Add(entity); + + Assert.Equal("Culture", context.Entry(entity).Member("Culture").Metadata.Name); + Assert.Equal("Milk", context.Entry(entity).Member("Milk").Metadata.Name); + } + + [ConditionalFact] + public void Can_get_and_set_current_value_complex_property() + { + using var context = new FreezerContext(); + var entity = CreateChunky(); + context.Add(entity); + + var property = context.Entry(entity).Member("Milk"); + + Assert.Equal(entity.Milk, property.CurrentValue); + + property.CurrentValue = new Milk { Species = "L. delbrueckii" }; + Assert.Equal("L. delbrueckii", ((Milk)property.CurrentValue).Species); + + property.CurrentValue = null; + Assert.Null(property.CurrentValue); + } + + [ConditionalFact] + public void Can_get_and_set_current_value_struct_complex_property() + { + using var context = new FreezerContext(); + var entity = CreateChunky(); + context.Add(entity); + + var property = context.Entry(entity).Member("Culture"); + + Assert.Equal(entity.Culture, property.CurrentValue); + + property.CurrentValue = new Culture { Species = "L. delbrueckii" }; + Assert.Equal("L. delbrueckii", ((Culture)property.CurrentValue).Species); + } + + [ConditionalFact] + public void Can_get_back_complex_property_using_fields() + { + using var context = new FreezerContext(); + var entity = CreateCherry(); + context.Add(entity); + + var entityEntry = context.Entry(entity); + Assert.Same(entityEntry.Entity, entityEntry.Member("Culture").EntityEntry.Entity); + Assert.Same(entityEntry.Entity, entityEntry.Member("Milk").EntityEntry.Entity); + } + + [ConditionalFact] + public void Can_get_metadata_complex_property_using_fields() + { + using var context = new FreezerContext(); + var entity = CreateCherry(); + context.Add(entity); + + Assert.Equal("Culture", context.Entry(entity).Member("Culture").Metadata.Name); + Assert.Equal("Milk", context.Entry(entity).Member("Milk").Metadata.Name); + } + + [ConditionalFact] + public void Can_get_and_set_current_value_complex_property_using_fields() + { + using var context = new FreezerContext(); + var entity = CreateCherry(); + context.Add(entity); + + var property = context.Entry(entity).Member("Milk"); + + Assert.Equal(entity.Milk, property.CurrentValue); + + property.CurrentValue = new FieldMilk { Species = "L. delbrueckii" }; + Assert.Equal("L. delbrueckii", ((FieldMilk)property.CurrentValue).Species); + + property.CurrentValue = null; + Assert.Null(property.CurrentValue); + } + + [ConditionalFact] + public void Can_get_and_set_current_value_struct_complex_property_using_fields() + { + using var context = new FreezerContext(); + var entity = CreateCherry(); + context.Add(entity); + + var property = context.Entry(entity).Member("Culture"); + + Assert.Equal(entity.Culture, property.CurrentValue); + + property.CurrentValue = new FieldCulture { Species = "L. delbrueckii" }; + Assert.Equal("L. delbrueckii", ((FieldCulture)property.CurrentValue).Species); + } + [Owned] public class Chunk { public int Size { get; set; } - public string Shape { get; set; } + public string? Shape { get; set; } } private class Chunky { public int Monkey { get; set; } public int Id { get; set; } + public Culture Culture { get; set; } + public Milk Milk { get; set; } = null!; public int? GarciaId { get; set; } - public Cherry Garcia { get; set; } + public Cherry? Garcia { get; set; } - public Chunk Chunk { get; set; } + public Chunk? Chunk { get; set; } } private class Cherry { public int Garcia { get; set; } public int Id { get; set; } + public FieldCulture Culture; + public FieldMilk Milk = null!; + + public ICollection? Monkeys { get; set; } + } + + private static Chunky CreateChunky(int id = 0) + => new() + { + Id = id, + Chunk = new Chunk { Size = 1, Shape = "Sphere" }, + Culture = new() + { + License = new() { Charge = 1.0m, Tag = new() { Text = "Ta1" }, Title = "Ti1", Tog = new() { Text = "To1" } }, + Manufacturer = new() { Name = "M1", Rating = 7, Tag = new() { Text = "Ta2" }, Tog = new() { Text = "To2"} }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() { Charge = 1.0m, Tag = new() { Text = "Ta1" }, Title = "Ti1", Tog = new() { Text = "To1" } }, + Manufacturer = new() { Name = "M1", Rating = 7, Tag = new() { Text = "Ta2" }, Tog = new() { Text = "To2"} }, + Rating = 8, + Species = "S1", + Validation = false + } + }; + + private static Cherry CreateCherry(int id = 0) + => new() + { + Id = id, + Culture = new() + { + License = new() { Charge = 1.0m, Tag = new() { Text = "Ta1" }, Title = "Ti1", Tog = new() { Text = "To1" } }, + Manufacturer = new() { Name = "M1", Rating = 7, Tag = new() { Text = "Ta2" }, Tog = new() { Text = "To2"} }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() { Charge = 1.0m, Tag = new() { Text = "Ta1" }, Title = "Ti1", Tog = new() { Text = "To1" } }, + Manufacturer = new() { Name = "M1", Rating = 7, Tag = new() { Text = "Ta2" }, Tog = new() { Text = "To2"} }, + Rating = 8, + Species = "S1", + Validation = false + } + }; + + private struct Culture + { + public string Species { get; set; } + public string? Subspecies { get; set; } + public int Rating { get; set; } + public bool? Validation { get; set; } + public Manufacturer Manufacturer { get; set; } + public License License { get; set; } + } + + private class Milk + { + public string Species { get; set; } = null!; + public string? Subspecies { get; set; } + public int Rating { get; set; } + public bool? Validation { get; set; } + public Manufacturer Manufacturer { get; set; } = null!; + public License License { get; set; } + } + + private class Manufacturer + { + public string? Name { get; set; } + public int Rating { get; set; } + public Tag Tag { get; set; } = null!; + public Tog Tog { get; set; } + } + + private struct License + { + public string Title { get; set; } + public decimal Charge { get; set; } + public Tag Tag { get; set; } + public Tog Tog { get; set; } + } + + private class Tag + { + public string? Text { get; set; } + } + + private struct Tog + { + public string? Text { get; set; } + } + + private struct FieldCulture + { + public string Species; + public string? Subspecies; + public int Rating; + public bool? Validation; + public FieldManufacturer Manufacturer; + public FieldLicense License; + } - public ICollection Monkeys { get; set; } + private class FieldMilk + { + public string Species = null!; + public string? Subspecies; + public int Rating; + public bool? Validation ; + public FieldManufacturer Manufacturer = null!; + public FieldLicense License; + } + + private class FieldManufacturer + { + public string? Name; + public int Rating; + public FieldTag Tag = null!; + public FieldTog Tog; + } + + private struct FieldLicense + { + public string Title; + public decimal Charge; + public FieldTag Tag; + public FieldTog Tog; + } + + private class FieldTag + { + public string? Text; + } + + private struct FieldTog + { + public string? Text; } private class FreezerContext : DbContext @@ -250,6 +529,85 @@ protected internal override void OnConfiguring(DbContextOptionsBuilder optionsBu .UseInternalServiceProvider(InMemoryFixture.DefaultServiceProvider) .UseInMemoryDatabase(nameof(FreezerContext)); - public DbSet Icecream { get; set; } + public DbSet Icecream { get; set; } = null!; + + protected internal override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity( + b => + { + b.ComplexProperty( + e => e.Culture, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + + b.ComplexProperty( + e => e.Milk, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + }); + + modelBuilder.Entity( + b => + { + b.ComplexProperty( + e => e.Culture, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + + b.ComplexProperty( + e => e.Milk, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + }); + } } } diff --git a/test/EFCore.Tests/ChangeTracking/PropertyEntryTest.cs b/test/EFCore.Tests/ChangeTracking/PropertyEntryTest.cs index 31fa64fcf17..e465cc6d1eb 100644 --- a/test/EFCore.Tests/ChangeTracking/PropertyEntryTest.cs +++ b/test/EFCore.Tests/ChangeTracking/PropertyEntryTest.cs @@ -1,6 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#nullable enable +#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value +#pragma warning disable CS0414 // Field is assigned but its value is never used + using System.ComponentModel; using System.Runtime.CompilerServices; @@ -43,7 +47,7 @@ public void Setting_IsModified_should_not_be_dependent_on_other_properties() using (var context = new UserContext()) { - var user = context.Find(id); + var user = context.Find(id)!; Assert.Equal("A", user.Name); Assert.Equal("NewB", user.LongName); @@ -66,7 +70,7 @@ public void SetValues_with_IsModified_can_mark_a_set_of_values_as_changed() using (var context = new UserContext()) { var disconnectedEntity = new User { Id = id, LongName = "NewLongName" }; - var trackedEntity = context.Find(id); + var trackedEntity = context.Find(id)!; Assert.Equal("A", trackedEntity.Name); Assert.Equal("B", trackedEntity.LongName); @@ -108,8 +112,8 @@ public void SetValues_with_IsModified_can_mark_a_set_of_values_as_changed() private class User { public Guid Id { get; set; } - public string Name { get; set; } - public string LongName { get; set; } + public string Name { get; set; } = null!; + public string LongName { get; set; } = null!; } private class UserContext : DbContext @@ -117,7 +121,7 @@ private class UserContext : DbContext protected internal override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder .UseInternalServiceProvider(InMemoryFixture.DefaultServiceProvider) - .UseInMemoryDatabase(GetType().FullName); + .UseInMemoryDatabase(GetType().FullName!); protected internal override void OnModelCreating(ModelBuilder modelBuilder) => modelBuilder.Entity( @@ -161,7 +165,7 @@ public void Setting_IsModified_is_not_reset_by_OriginalValues() using (var context = new UserContext()) { - var user = context.Find(id); + var user = context.Find(id)!; Assert.Equal("A", user.Name); Assert.Equal("B2", user.LongName); @@ -192,7 +196,7 @@ private void Can_get_name_helper() entry.SetEntityState(EntityState.Unchanged); - Assert.Equal("Primate", new PropertyEntry(entry, "Primate").Metadata.Name); + Assert.Equal("Primate", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).Metadata.Name); } [ConditionalFact] @@ -219,8 +223,8 @@ private void Can_get_current_value_helper() entry.SetEntityState(EntityState.Unchanged); - Assert.Equal("Monkey", new PropertyEntry(entry, "Primate").CurrentValue); - Assert.Equal("Tarsier", new PropertyEntry(entry, "RequiredPrimate").CurrentValue); + Assert.Equal("Monkey", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).CurrentValue); + Assert.Equal("Tarsier", new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).CurrentValue); } [ConditionalFact] @@ -244,8 +248,8 @@ private void Can_set_current_value_helper() var entry = context.Entry(entity).GetInfrastructure(); entry.SetEntityState(EntityState.Unchanged); - new PropertyEntry(entry, "Primate").CurrentValue = "Chimp"; - new PropertyEntry(entry, "RequiredPrimate").CurrentValue = "Bushbaby"; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).CurrentValue = "Chimp"; + new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).CurrentValue = "Bushbaby"; Assert.Equal("Chimp", entity.Primate); Assert.Equal("Bushbaby", entity.RequiredPrimate); @@ -277,8 +281,8 @@ private void Can_set_current_value_to_null_helper() var entry = context.Entry(entity).GetInfrastructure(); entry.SetEntityState(EntityState.Unchanged); - new PropertyEntry(entry, "Primate").CurrentValue = null; - new PropertyEntry(entry, "RequiredPrimate").CurrentValue = null; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).CurrentValue = null; + new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).CurrentValue = null; Assert.Null(entity.Primate); Assert.Null(entity.RequiredPrimate); @@ -310,24 +314,24 @@ private void Can_set_and_get_original_value_helper() var entry = context.Entry(entity).GetInfrastructure(); entry.SetEntityState(EntityState.Unchanged); - Assert.Equal("Monkey", new PropertyEntry(entry, "Primate").OriginalValue); - Assert.Equal("Tarsier", new PropertyEntry(entry, "RequiredPrimate").OriginalValue); + Assert.Equal("Monkey", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); + Assert.Equal("Tarsier", new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).OriginalValue); - new PropertyEntry(entry, "Primate").OriginalValue = "Chimp"; - new PropertyEntry(entry, "RequiredPrimate").OriginalValue = "Bushbaby"; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue = "Chimp"; + new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).OriginalValue = "Bushbaby"; - Assert.Equal("Chimp", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Chimp", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); Assert.Equal("Monkey", entity.Primate); - Assert.Equal("Bushbaby", new PropertyEntry(entry, "RequiredPrimate").OriginalValue); + Assert.Equal("Bushbaby", new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).OriginalValue); Assert.Equal("Tarsier", entity.RequiredPrimate); context.ChangeTracker.DetectChanges(); - Assert.Equal("Chimp", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Chimp", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); Assert.Equal("Monkey", entity.Primate); - Assert.Equal("Bushbaby", new PropertyEntry(entry, "RequiredPrimate").OriginalValue); + Assert.Equal("Bushbaby", new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).OriginalValue); Assert.Equal("Tarsier", entity.RequiredPrimate); } @@ -347,24 +351,24 @@ private void Can_set_and_get_original_value_starting_null_helper() var entry = context.Entry(entity).GetInfrastructure(); entry.SetEntityState(EntityState.Unchanged); - Assert.Null(new PropertyEntry(entry, "Primate").OriginalValue); - Assert.Null(new PropertyEntry(entry, "RequiredPrimate").OriginalValue); + Assert.Null(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); + Assert.Null(new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).OriginalValue); - new PropertyEntry(entry, "Primate").OriginalValue = "Chimp"; - new PropertyEntry(entry, "RequiredPrimate").OriginalValue = "Bushbaby"; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue = "Chimp"; + new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).OriginalValue = "Bushbaby"; - Assert.Equal("Chimp", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Chimp", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); Assert.Null(entity.Primate); - Assert.Equal("Bushbaby", new PropertyEntry(entry, "RequiredPrimate").OriginalValue); + Assert.Equal("Bushbaby", new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).OriginalValue); Assert.Null(entity.RequiredPrimate); context.ChangeTracker.DetectChanges(); - Assert.Equal("Chimp", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Chimp", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); Assert.Null(entity.Primate); - Assert.Equal("Bushbaby", new PropertyEntry(entry, "RequiredPrimate").OriginalValue); + Assert.Equal("Bushbaby", new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).OriginalValue); Assert.Null(entity.RequiredPrimate); } @@ -389,16 +393,16 @@ private void Can_set_original_value_to_null_helper() var entry = context.Entry(entity).GetInfrastructure(); entry.SetEntityState(EntityState.Unchanged); - new PropertyEntry(entry, "Primate").OriginalValue = null; - new PropertyEntry(entry, "RequiredPrimate").OriginalValue = null; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue = null; + new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).OriginalValue = null; - Assert.Null(new PropertyEntry(entry, "Primate").OriginalValue); - Assert.Null(new PropertyEntry(entry, "RequiredPrimate").OriginalValue); + Assert.Null(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); + Assert.Null(new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).OriginalValue); context.ChangeTracker.DetectChanges(); - Assert.Null(new PropertyEntry(entry, "Primate").OriginalValue); - Assert.Null(new PropertyEntry(entry, "RequiredPrimate").OriginalValue); + Assert.Null(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); + Assert.Null(new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).OriginalValue); } [ConditionalFact] @@ -417,35 +421,35 @@ private void Can_set_and_clear_modified_on_Modified_entity_helper() var entry = context.Entry(entity).GetInfrastructure(); entry.SetEntityState(EntityState.Modified); - Assert.True(new PropertyEntry(entry, "Primate").IsModified); - Assert.True(new PropertyEntry(entry, "RequiredPrimate").IsModified); + Assert.True(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified); + Assert.True(new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).IsModified); context.ChangeTracker.DetectChanges(); - Assert.True(new PropertyEntry(entry, "Primate").IsModified); - Assert.True(new PropertyEntry(entry, "RequiredPrimate").IsModified); + Assert.True(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified); + Assert.True(new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).IsModified); - new PropertyEntry(entry, "Primate").IsModified = false; - new PropertyEntry(entry, "RequiredPrimate").IsModified = false; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified = false; + new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).IsModified = false; - Assert.False(new PropertyEntry(entry, "Primate").IsModified); - Assert.False(new PropertyEntry(entry, "RequiredPrimate").IsModified); + Assert.False(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified); + Assert.False(new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).IsModified); context.ChangeTracker.DetectChanges(); - Assert.False(new PropertyEntry(entry, "Primate").IsModified); - Assert.False(new PropertyEntry(entry, "RequiredPrimate").IsModified); + Assert.False(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified); + Assert.False(new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).IsModified); - new PropertyEntry(entry, "Primate").IsModified = true; - new PropertyEntry(entry, "RequiredPrimate").IsModified = true; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified = true; + new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).IsModified = true; - Assert.True(new PropertyEntry(entry, "Primate").IsModified); - Assert.True(new PropertyEntry(entry, "RequiredPrimate").IsModified); + Assert.True(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified); + Assert.True(new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).IsModified); context.ChangeTracker.DetectChanges(); - Assert.True(new PropertyEntry(entry, "Primate").IsModified); - Assert.True(new PropertyEntry(entry, "RequiredPrimate").IsModified); + Assert.True(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified); + Assert.True(new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!).IsModified); } [ConditionalTheory] @@ -468,26 +472,26 @@ private void Can_set_and_clear_modified_on_Added_or_Deleted_entity_helper() var entry = context.Entry(entity).GetInfrastructure(); entry.SetEntityState(EntityState.Unchanged); - var primateEntry = new PropertyEntry(entry, "Primate") { OriginalValue = "Chimp", IsModified = true }; + var primateEntry = new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!) { OriginalValue = "Chimp", IsModified = true }; - var marmateEntry = new PropertyEntry(entry, "Marmate") { OriginalValue = "Marmite", IsModified = true }; + var marmateEntry = new PropertyEntry(entry, entry.EntityType.FindProperty("Marmate")!) { OriginalValue = "Marmite", IsModified = true }; - var requiredEntry = new PropertyEntry(entry, "RequiredPrimate") { OriginalValue = "Bushbaby", IsModified = true }; + var requiredEntry = new PropertyEntry(entry, entry.EntityType.FindProperty("RequiredPrimate")!) { OriginalValue = "Bushbaby", IsModified = true }; Assert.Equal(EntityState.Modified, entry.EntityState); Assert.Equal("Monkey", entity.Primate); @@ -627,7 +631,7 @@ private void Can_get_name_generic_helper() EntityState.Unchanged, new TWotty { Id = 1, Primate = "Monkey" }); - Assert.Equal("Primate", new PropertyEntry(entry, "Primate").Metadata.Name); + Assert.Equal("Primate", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).Metadata.Name); } [ConditionalFact] @@ -646,7 +650,7 @@ private void Can_get_current_value_generic_helper() EntityState.Unchanged, new TWotty { Id = 1, Primate = "Monkey" }); - Assert.Equal("Monkey", new PropertyEntry(entry, "Primate").CurrentValue); + Assert.Equal("Monkey", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).CurrentValue); } [ConditionalFact] @@ -667,7 +671,7 @@ private void Can_set_current_value_generic_helper() EntityState.Unchanged, entity); - new PropertyEntry(entry, "Primate").CurrentValue = "Chimp"; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).CurrentValue = "Chimp"; Assert.Equal("Chimp", entity.Primate); } @@ -690,7 +694,7 @@ private void Can_set_current_value_to_null_generic_helper() EntityState.Unchanged, entity); - new PropertyEntry(entry, "Primate").CurrentValue = null; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).CurrentValue = null; Assert.Null(entity.Primate); } @@ -713,11 +717,11 @@ private void Can_set_and_get_original_value_generic_helper() EntityState.Unchanged, entity); - Assert.Equal("Monkey", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Monkey", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); - new PropertyEntry(entry, "Primate").OriginalValue = "Chimp"; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue = "Chimp"; - Assert.Equal("Chimp", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Chimp", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); Assert.Equal("Monkey", entity.Primate); } @@ -737,9 +741,9 @@ private void Can_set_original_value_to_null_generic_helper() EntityState.Unchanged, new TWotty { Id = 1, Primate = "Monkey" }); - new PropertyEntry(entry, "Primate").OriginalValue = null; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue = null; - Assert.Null(new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Null(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); } [ConditionalFact] @@ -760,15 +764,15 @@ private void Can_set_and_clear_modified_generic_helper() EntityState.Unchanged, entity); - Assert.False(new PropertyEntry(entry, "Primate").IsModified); + Assert.False(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified); - new PropertyEntry(entry, "Primate").IsModified = true; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified = true; - Assert.True(new PropertyEntry(entry, "Primate").IsModified); + Assert.True(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified); - new PropertyEntry(entry, "Primate").IsModified = false; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified = false; - Assert.False(new PropertyEntry(entry, "Primate").IsModified); + Assert.False(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).IsModified); } [ConditionalFact] @@ -781,11 +785,11 @@ public void Can_set_and_get_original_value_notifying_entities() EntityState.Unchanged, entity); - Assert.Equal("Monkey", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Monkey", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); - new PropertyEntry(entry, "Primate").OriginalValue = "Chimp"; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue = "Chimp"; - Assert.Equal("Chimp", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Chimp", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); Assert.Equal("Monkey", entity.Primate); } @@ -797,9 +801,9 @@ public void Can_set_original_value_to_null_notifying_entities() EntityState.Unchanged, new NotifyingWotty { Id = 1, Primate = "Monkey" }); - new PropertyEntry(entry, "Primate").OriginalValue = null; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue = null; - Assert.Null(new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Null(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); } [ConditionalFact] @@ -812,11 +816,11 @@ public void Can_set_and_get_original_value_generic_notifying_entities() EntityState.Unchanged, entity); - Assert.Equal("Monkey", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Monkey", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); - new PropertyEntry(entry, "Primate").OriginalValue = "Chimp"; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue = "Chimp"; - Assert.Equal("Chimp", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Chimp", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); Assert.Equal("Monkey", entity.Primate); } @@ -828,9 +832,9 @@ public void Can_set_original_value_to_null_generic_notifying_entities() EntityState.Unchanged, new NotifyingWotty { Id = 1, Primate = "Monkey" }); - new PropertyEntry(entry, "Primate").OriginalValue = null; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue = null; - Assert.Null(new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Null(new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); } [ConditionalFact] @@ -843,11 +847,11 @@ public void Can_set_and_get_concurrency_token_original_value_full_notification_e EntityState.Unchanged, entity); - Assert.Equal("Monkey", new PropertyEntry(entry, "ConcurrentPrimate").OriginalValue); + Assert.Equal("Monkey", new PropertyEntry(entry, entry.EntityType.FindProperty("ConcurrentPrimate")!).OriginalValue); - new PropertyEntry(entry, "ConcurrentPrimate").OriginalValue = "Chimp"; + new PropertyEntry(entry, entry.EntityType.FindProperty("ConcurrentPrimate")!).OriginalValue = "Chimp"; - Assert.Equal("Chimp", new PropertyEntry(entry, "ConcurrentPrimate").OriginalValue); + Assert.Equal("Chimp", new PropertyEntry(entry, entry.EntityType.FindProperty("ConcurrentPrimate")!).OriginalValue); Assert.Equal("Monkey", entity.ConcurrentPrimate); } @@ -859,9 +863,9 @@ public void Can_set_concurrency_token_original_value_to_null_full_notification_e EntityState.Unchanged, new FullyNotifyingWotty { Id = 1, ConcurrentPrimate = "Monkey" }); - new PropertyEntry(entry, "ConcurrentPrimate").OriginalValue = null; + new PropertyEntry(entry, entry.EntityType.FindProperty("ConcurrentPrimate")!).OriginalValue = null; - Assert.Null(new PropertyEntry(entry, "ConcurrentPrimate").OriginalValue); + Assert.Null(new PropertyEntry(entry, entry.EntityType.FindProperty("ConcurrentPrimate")!).OriginalValue); } [ConditionalFact] @@ -874,11 +878,11 @@ public void Can_set_and_get_concurrency_token_original_value_generic_full_notifi EntityState.Unchanged, entity); - Assert.Equal("Monkey", new PropertyEntry(entry, "ConcurrentPrimate").OriginalValue); + Assert.Equal("Monkey", new PropertyEntry(entry, entry.EntityType.FindProperty("ConcurrentPrimate")!).OriginalValue); - new PropertyEntry(entry, "ConcurrentPrimate").OriginalValue = "Chimp"; + new PropertyEntry(entry, entry.EntityType.FindProperty("ConcurrentPrimate")!).OriginalValue = "Chimp"; - Assert.Equal("Chimp", new PropertyEntry(entry, "ConcurrentPrimate").OriginalValue); + Assert.Equal("Chimp", new PropertyEntry(entry, entry.EntityType.FindProperty("ConcurrentPrimate")!).OriginalValue); Assert.Equal("Monkey", entity.ConcurrentPrimate); } @@ -890,9 +894,9 @@ public void Can_set_concurrency_token_original_value_to_null_generic_full_notifi EntityState.Unchanged, new FullyNotifyingWotty { Id = 1, ConcurrentPrimate = "Monkey" }); - new PropertyEntry(entry, "ConcurrentPrimate").OriginalValue = null; + new PropertyEntry(entry, entry.EntityType.FindProperty("ConcurrentPrimate")!).OriginalValue = null; - Assert.Null(new PropertyEntry(entry, "ConcurrentPrimate").OriginalValue); + Assert.Null(new PropertyEntry(entry, entry.EntityType.FindProperty("ConcurrentPrimate")!).OriginalValue); } [ConditionalFact] @@ -905,7 +909,7 @@ public void Cannot_set_or_get_original_value_when_not_tracked() EntityState.Unchanged, entity); - var propertyEntry = new PropertyEntry(entry, "Primate"); + var propertyEntry = new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!); Assert.Equal( CoreStrings.OriginalValueNotTracked("Primate", "FullyNotifyingWotty"), @@ -926,7 +930,7 @@ public void Cannot_set_or_get_original_value_when_not_tracked_generic() EntityState.Unchanged, entity); - var propertyEntry = new PropertyEntry(entry, "Primate"); + var propertyEntry = new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!); Assert.Equal( CoreStrings.OriginalValueNotTracked("Primate", "FullyNotifyingWotty"), @@ -947,11 +951,11 @@ public void Can_set_or_get_original_value_when_property_explicitly_marked_to_be_ EntityState.Unchanged, entity); - Assert.Equal("Monkey", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Monkey", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); - new PropertyEntry(entry, "Primate").OriginalValue = "Chimp"; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue = "Chimp"; - Assert.Equal("Chimp", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Chimp", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); Assert.Equal("Monkey", entity.Primate); } @@ -965,50 +969,3574 @@ public void Can_set_or_get_original_value_when_property_explicitly_marked_to_be_ EntityState.Unchanged, entity); - Assert.Equal("Monkey", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Monkey", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); - new PropertyEntry(entry, "Primate").OriginalValue = "Chimp"; + new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue = "Chimp"; - Assert.Equal("Chimp", new PropertyEntry(entry, "Primate").OriginalValue); + Assert.Equal("Chimp", new PropertyEntry(entry, entry.EntityType.FindProperty("Primate")!).OriginalValue); Assert.Equal("Monkey", entity.Primate); } + [ConditionalFact] + public void Can_get_name_for_complex_property() + { + using var context = new YogurtContext(); + var entry = context.Entry( + new Yogurt + { + Culture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldCulture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldMilk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + } + }); + + entry.State = EntityState.Unchanged; + + var cultureEntry = entry.ComplexProperty(e => e.Culture); + var cultureManufacturerEntry = cultureEntry.ComplexProperty(e => e.Manufacturer); + var cultureLicenseEntry = cultureEntry.ComplexProperty(e => e.License); + var cultureManTogEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tog); + var cultureManTagEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tag); + var cultureLicTogEntry = cultureLicenseEntry.ComplexProperty(e => e.Tog); + var cultureLicTagEntry = cultureLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal("Culture", cultureEntry.Metadata.Name); + Assert.Equal("Rating", cultureEntry.Property(e => e.Rating).Metadata.Name); + Assert.Equal("Manufacturer", cultureManufacturerEntry.Metadata.Name); + Assert.Equal("Name", cultureManufacturerEntry.Property(e => e.Name).Metadata.Name); + Assert.Equal("License", cultureLicenseEntry.Metadata.Name); + Assert.Equal("Charge", cultureLicenseEntry.Property(e => e.Charge).Metadata.Name); + Assert.Equal("Tog", cultureManTogEntry.Metadata.Name); + Assert.Equal("Text", cultureManTogEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tag", cultureManTagEntry.Metadata.Name); + Assert.Equal("Text", cultureManTagEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tog", cultureLicTogEntry.Metadata.Name); + Assert.Equal("Text", cultureLicTogEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tag", cultureLicTagEntry.Metadata.Name); + Assert.Equal("Text", cultureLicTagEntry.Property(e => e.Text).Metadata.Name); + + var milkEntry = entry.ComplexProperty(e => e.Milk); + var milkManufacturerEntry = milkEntry.ComplexProperty(e => e.Manufacturer); + var milkLicenseEntry = milkEntry.ComplexProperty(e => e.License); + var milkManTogEntry = milkManufacturerEntry.ComplexProperty(e => e.Tog); + var milkManTagEntry = milkManufacturerEntry.ComplexProperty(e => e.Tag); + var milkLicTogEntry = milkLicenseEntry.ComplexProperty(e => e.Tog); + var milkLicTagEntry = milkLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal("Milk", milkEntry.Metadata.Name); + Assert.Equal("Rating", milkEntry.Property(e => e.Rating).Metadata.Name); + Assert.Equal("Manufacturer", milkManufacturerEntry.Metadata.Name); + Assert.Equal("Name", milkManufacturerEntry.Property(e => e.Name).Metadata.Name); + Assert.Equal("License", milkLicenseEntry.Metadata.Name); + Assert.Equal("Charge", milkLicenseEntry.Property(e => e.Charge).Metadata.Name); + Assert.Equal("Tog", milkManTogEntry.Metadata.Name); + Assert.Equal("Text", milkManTogEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tag", milkManTagEntry.Metadata.Name); + Assert.Equal("Text", milkManTagEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tog", milkLicTogEntry.Metadata.Name); + Assert.Equal("Text", milkLicTogEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tag", milkLicTagEntry.Metadata.Name); + Assert.Equal("Text", milkLicTagEntry.Property(e => e.Text).Metadata.Name); + + var fieldCultureEntry = entry.ComplexProperty(e => e.FieldCulture); + var fieldCultureManufacturerEntry = fieldCultureEntry.ComplexProperty(e => e.Manufacturer); + var fieldCultureLicenseEntry = fieldCultureEntry.ComplexProperty(e => e.License); + var fieldCultureManTogEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldCultureManTagEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldCultureLicTogEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tog); + var fieldCultureLicTagEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal("FieldCulture", fieldCultureEntry.Metadata.Name); + Assert.Equal("Rating", fieldCultureEntry.Property(e => e.Rating).Metadata.Name); + Assert.Equal("Manufacturer", fieldCultureManufacturerEntry.Metadata.Name); + Assert.Equal("Name", fieldCultureManufacturerEntry.Property(e => e.Name).Metadata.Name); + Assert.Equal("License", fieldCultureLicenseEntry.Metadata.Name); + Assert.Equal("Charge", fieldCultureLicenseEntry.Property(e => e.Charge).Metadata.Name); + Assert.Equal("Tog", fieldCultureManTogEntry.Metadata.Name); + Assert.Equal("Text", fieldCultureManTogEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tag", fieldCultureManTagEntry.Metadata.Name); + Assert.Equal("Text", fieldCultureManTagEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tog", fieldCultureLicTogEntry.Metadata.Name); + Assert.Equal("Text", fieldCultureLicTogEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tag", fieldCultureLicTagEntry.Metadata.Name); + Assert.Equal("Text", fieldCultureLicTagEntry.Property(e => e.Text).Metadata.Name); + + var fieldMilkEntry = entry.ComplexProperty(e => e.FieldMilk); + var fieldMilkManufacturerEntry = fieldMilkEntry.ComplexProperty(e => e.Manufacturer); + var fieldMilkLicenseEntry = fieldMilkEntry.ComplexProperty(e => e.License); + var fieldMilkManTogEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldMilkManTagEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldMilkLicTogEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tog); + var fieldMilkLicTagEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal("FieldMilk", fieldMilkEntry.Metadata.Name); + Assert.Equal("Rating", fieldMilkEntry.Property(e => e.Rating).Metadata.Name); + Assert.Equal("Manufacturer", fieldMilkManufacturerEntry.Metadata.Name); + Assert.Equal("Name", fieldMilkManufacturerEntry.Property(e => e.Name).Metadata.Name); + Assert.Equal("License", fieldMilkLicenseEntry.Metadata.Name); + Assert.Equal("Charge", fieldMilkLicenseEntry.Property(e => e.Charge).Metadata.Name); + Assert.Equal("Tog", fieldMilkManTogEntry.Metadata.Name); + Assert.Equal("Text", fieldMilkManTogEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tag", fieldMilkManTagEntry.Metadata.Name); + Assert.Equal("Text", fieldMilkManTagEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tog", fieldMilkLicTogEntry.Metadata.Name); + Assert.Equal("Text", fieldMilkLicTogEntry.Property(e => e.Text).Metadata.Name); + Assert.Equal("Tag", fieldMilkLicTagEntry.Metadata.Name); + Assert.Equal("Text", fieldMilkLicTagEntry.Property(e => e.Text).Metadata.Name); + } + + [ConditionalFact] + public void Can_get_current_value_for_complex_property() + { + using var context = new YogurtContext(); + var yogurt = new Yogurt + { + Culture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldCulture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldMilk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + } + }; + + var entry = context.Entry(yogurt); + entry.State = EntityState.Unchanged; + + var cultureEntry = entry.ComplexProperty(e => e.Culture); + var cultureManufacturerEntry = cultureEntry.ComplexProperty(e => e.Manufacturer); + var cultureLicenseEntry = cultureEntry.ComplexProperty(e => e.License); + var cultureManTogEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tog); + var cultureManTagEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tag); + var cultureLicTogEntry = cultureLicenseEntry.ComplexProperty(e => e.Tog); + var cultureLicTagEntry = cultureLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(yogurt.Culture, cultureEntry.CurrentValue); + Assert.Equal(yogurt.Culture.Rating, cultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Culture.Species, cultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.Culture.Subspecies, cultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.Culture.Validation, cultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer, cultureManufacturerEntry.CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Name, cultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Rating, cultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tog, cultureManTogEntry.CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tog.Text, cultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tag, cultureManTagEntry.CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tag.Text, cultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License, cultureLicenseEntry.CurrentValue); + Assert.Equal(yogurt.Culture.License.Title, cultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(yogurt.Culture.License.Charge, cultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tog, cultureLicTogEntry.CurrentValue); + Assert.Equal(yogurt.Culture.License.Tog.Text, cultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tag, cultureLicTagEntry.CurrentValue); + Assert.Equal(yogurt.Culture.License.Tag.Text, cultureLicTagEntry.Property(e => e.Text).CurrentValue); + + var milkEntry = entry.ComplexProperty(e => e.Milk); + var milkManufacturerEntry = milkEntry.ComplexProperty(e => e.Manufacturer); + var milkLicenseEntry = milkEntry.ComplexProperty(e => e.License); + var milkManTogEntry = milkManufacturerEntry.ComplexProperty(e => e.Tog); + var milkManTagEntry = milkManufacturerEntry.ComplexProperty(e => e.Tag); + var milkLicTogEntry = milkLicenseEntry.ComplexProperty(e => e.Tog); + var milkLicTagEntry = milkLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(yogurt.Milk, milkEntry.CurrentValue); + Assert.Equal(yogurt.Milk.Rating, milkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Milk.Species, milkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.Milk.Subspecies, milkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.Milk.Validation, milkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer, milkManufacturerEntry.CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Name, milkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Rating, milkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tog, milkManTogEntry.CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tog.Text, milkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tag, milkManTagEntry.CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tag.Text, milkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License, milkLicenseEntry.CurrentValue); + Assert.Equal(yogurt.Milk.License.Title, milkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(yogurt.Milk.License.Charge, milkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tog, milkLicTogEntry.CurrentValue); + Assert.Equal(yogurt.Milk.License.Tog.Text, milkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tag, milkLicTagEntry.CurrentValue); + Assert.Equal(yogurt.Milk.License.Tag.Text, milkLicTagEntry.Property(e => e.Text).CurrentValue); + + var fieldCultureEntry = entry.ComplexProperty(e => e.FieldCulture); + var fieldCultureManufacturerEntry = fieldCultureEntry.ComplexProperty(e => e.Manufacturer); + var fieldCultureLicenseEntry = fieldCultureEntry.ComplexProperty(e => e.License); + var fieldCultureManTogEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldCultureManTagEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldCultureLicTogEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tog); + var fieldCultureLicTagEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(yogurt.FieldCulture, fieldCultureEntry.CurrentValue); + Assert.Equal(yogurt.FieldCulture.Rating, fieldCultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Species, fieldCultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Subspecies, fieldCultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Validation, fieldCultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer, fieldCultureManufacturerEntry.CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Name, fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Rating, fieldCultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tog, fieldCultureManTogEntry.CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tog.Text, fieldCultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tag, fieldCultureManTagEntry.CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tag.Text, fieldCultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License, fieldCultureLicenseEntry.CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Title, fieldCultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Charge, fieldCultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tog, fieldCultureLicTogEntry.CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tog.Text, fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tag, fieldCultureLicTagEntry.CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tag.Text, fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue); + + var fieldMilkEntry = entry.ComplexProperty(e => e.FieldMilk); + var fieldMilkManufacturerEntry = fieldMilkEntry.ComplexProperty(e => e.Manufacturer); + var fieldMilkLicenseEntry = fieldMilkEntry.ComplexProperty(e => e.License); + var fieldMilkManTogEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldMilkManTagEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldMilkLicTogEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tog); + var fieldMilkLicTagEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(yogurt.FieldMilk, fieldMilkEntry.CurrentValue); + Assert.Equal(yogurt.FieldMilk.Rating, fieldMilkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Species, fieldMilkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Subspecies, fieldMilkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Validation, fieldMilkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer, fieldMilkManufacturerEntry.CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Name, fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Rating, fieldMilkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tog, fieldMilkManTogEntry.CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tog.Text, fieldMilkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tag, fieldMilkManTagEntry.CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tag.Text, fieldMilkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License, fieldMilkLicenseEntry.CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Title, fieldMilkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Charge, fieldMilkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tog, fieldMilkLicTogEntry.CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tog.Text, fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tag, fieldMilkLicTagEntry.CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tag.Text, fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue); + } + + [ConditionalFact] + public void Can_set_current_value_for_complex_property() + { + using var context = new YogurtContext(); + var yogurt = new Yogurt + { + Culture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldCulture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldMilk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + } + }; + + var entry = context.Entry(yogurt); + entry.State = EntityState.Unchanged; + + var cultureEntry = entry.ComplexProperty(e => e.Culture); + var cultureManufacturerEntry = cultureEntry.ComplexProperty(e => e.Manufacturer); + var cultureLicenseEntry = cultureEntry.ComplexProperty(e => e.License); + var cultureManTogEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tog); + var cultureManTagEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tag); + var cultureLicTogEntry = cultureLicenseEntry.ComplexProperty(e => e.Tog); + var cultureLicTagEntry = cultureLicenseEntry.ComplexProperty(e => e.Tag); + + cultureEntry.Property(e => e.Rating).CurrentValue = 11; + cultureEntry.Property(e => e.Species).CurrentValue = "XY"; + cultureEntry.Property(e => e.Subspecies).CurrentValue = "Z"; + cultureEntry.Property(e => e.Validation).CurrentValue = true; + cultureManufacturerEntry.Property(e => e.Name).CurrentValue = "Nom"; + cultureManufacturerEntry.Property(e => e.Rating).CurrentValue = 9; + cultureManTogEntry.Property(e => e.Text).CurrentValue = "Tog1"; + cultureManTagEntry.Property(e => e.Text).CurrentValue = "Tag1"; + cultureLicenseEntry.Property(e => e.Title).CurrentValue = "Title"; + cultureLicenseEntry.Property(e => e.Charge).CurrentValue = 11.0m; + cultureLicTogEntry.Property(e => e.Text).CurrentValue = "Tog2"; + cultureLicTagEntry.Property(e => e.Text).CurrentValue = "Tag2"; + + Assert.Equal(yogurt.Culture, cultureEntry.CurrentValue); + Assert.Equal(11, cultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("XY", cultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("Z", cultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(true, cultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer, cultureManufacturerEntry.CurrentValue); + Assert.Equal("Nom", cultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(9, cultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tog, cultureManTogEntry.CurrentValue); + Assert.Equal("Tog1", cultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tag, cultureManTagEntry.CurrentValue); + Assert.Equal("Tag1", cultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License, cultureLicenseEntry.CurrentValue); + Assert.Equal("Title", cultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(11.0m, cultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tog, cultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2", cultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tag, cultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2", cultureLicTagEntry.Property(e => e.Text).CurrentValue); + + var milkEntry = entry.ComplexProperty(e => e.Milk); + var milkManufacturerEntry = milkEntry.ComplexProperty(e => e.Manufacturer); + var milkLicenseEntry = milkEntry.ComplexProperty(e => e.License); + var milkManTogEntry = milkManufacturerEntry.ComplexProperty(e => e.Tog); + var milkManTagEntry = milkManufacturerEntry.ComplexProperty(e => e.Tag); + var milkLicTogEntry = milkLicenseEntry.ComplexProperty(e => e.Tog); + var milkLicTagEntry = milkLicenseEntry.ComplexProperty(e => e.Tag); + + milkEntry.Property(e => e.Rating).CurrentValue = 11; + milkEntry.Property(e => e.Species).CurrentValue = "XY"; + milkEntry.Property(e => e.Subspecies).CurrentValue = "Z"; + milkEntry.Property(e => e.Validation).CurrentValue = true; + milkManufacturerEntry.Property(e => e.Name).CurrentValue = "Nom"; + milkManufacturerEntry.Property(e => e.Rating).CurrentValue = 9; + milkManTogEntry.Property(e => e.Text).CurrentValue = "Tog1"; + milkManTagEntry.Property(e => e.Text).CurrentValue = "Tag1"; + milkLicenseEntry.Property(e => e.Title).CurrentValue = "Title"; + milkLicenseEntry.Property(e => e.Charge).CurrentValue = 11.0m; + milkLicTogEntry.Property(e => e.Text).CurrentValue = "Tog2"; + milkLicTagEntry.Property(e => e.Text).CurrentValue = "Tag2"; + + Assert.Equal(yogurt.Milk, milkEntry.CurrentValue); + Assert.Equal(11, milkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("XY", milkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("Z", milkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(true, milkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer, milkManufacturerEntry.CurrentValue); + Assert.Equal("Nom", milkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(9, milkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tog, milkManTogEntry.CurrentValue); + Assert.Equal("Tog1", milkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tag, milkManTagEntry.CurrentValue); + Assert.Equal("Tag1", milkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License, milkLicenseEntry.CurrentValue); + Assert.Equal("Title", milkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(11.0m, milkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tog, milkLicTogEntry.CurrentValue); + Assert.Equal("Tog2", milkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tag, milkLicTagEntry.CurrentValue); + Assert.Equal("Tag2", milkLicTagEntry.Property(e => e.Text).CurrentValue); + + var fieldCultureEntry = entry.ComplexProperty(e => e.FieldCulture); + var fieldCultureManufacturerEntry = fieldCultureEntry.ComplexProperty(e => e.Manufacturer); + var fieldCultureLicenseEntry = fieldCultureEntry.ComplexProperty(e => e.License); + var fieldCultureManTogEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldCultureManTagEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldCultureLicTogEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tog); + var fieldCultureLicTagEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tag); + + fieldCultureEntry.Property(e => e.Rating).CurrentValue = 11; + fieldCultureEntry.Property(e => e.Species).CurrentValue = "XY"; + fieldCultureEntry.Property(e => e.Subspecies).CurrentValue = "Z"; + fieldCultureEntry.Property(e => e.Validation).CurrentValue = true; + fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue = "Nom"; + fieldCultureManufacturerEntry.Property(e => e.Rating).CurrentValue = 9; + fieldCultureManTogEntry.Property(e => e.Text).CurrentValue = "Tog1"; + fieldCultureManTagEntry.Property(e => e.Text).CurrentValue = "Tag1"; + fieldCultureLicenseEntry.Property(e => e.Title).CurrentValue = "Title"; + fieldCultureLicenseEntry.Property(e => e.Charge).CurrentValue = 11.0m; + fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue = "Tog2"; + fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue = "Tag2"; + + Assert.Equal(yogurt.FieldCulture, fieldCultureEntry.CurrentValue); + Assert.Equal(11, fieldCultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("XY", fieldCultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("Z", fieldCultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(true, fieldCultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer, fieldCultureManufacturerEntry.CurrentValue); + Assert.Equal("Nom", fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(9, fieldCultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tog, fieldCultureManTogEntry.CurrentValue); + Assert.Equal("Tog1", fieldCultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tag, fieldCultureManTagEntry.CurrentValue); + Assert.Equal("Tag1", fieldCultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License, fieldCultureLicenseEntry.CurrentValue); + Assert.Equal("Title", fieldCultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(11.0m, fieldCultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tog, fieldCultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2", fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tag, fieldCultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2", fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue); + + var fieldMilkEntry = entry.ComplexProperty(e => e.FieldMilk); + var fieldMilkManufacturerEntry = fieldMilkEntry.ComplexProperty(e => e.Manufacturer); + var fieldMilkLicenseEntry = fieldMilkEntry.ComplexProperty(e => e.License); + var fieldMilkManTogEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldMilkManTagEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldMilkLicTogEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tog); + var fieldMilkLicTagEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tag); + + fieldMilkEntry.Property(e => e.Rating).CurrentValue = 11; + fieldMilkEntry.Property(e => e.Species).CurrentValue = "XY"; + fieldMilkEntry.Property(e => e.Subspecies).CurrentValue = "Z"; + fieldMilkEntry.Property(e => e.Validation).CurrentValue = true; + fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue = "Nom"; + fieldMilkManufacturerEntry.Property(e => e.Rating).CurrentValue = 9; + fieldMilkManTogEntry.Property(e => e.Text).CurrentValue = "Tog1"; + fieldMilkManTagEntry.Property(e => e.Text).CurrentValue = "Tag1"; + fieldMilkLicenseEntry.Property(e => e.Title).CurrentValue = "Title"; + fieldMilkLicenseEntry.Property(e => e.Charge).CurrentValue = 11.0m; + fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue = "Tog2"; + fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue = "Tag2"; + + Assert.Equal(yogurt.FieldMilk, fieldMilkEntry.CurrentValue); + Assert.Equal(11, fieldMilkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("XY", fieldMilkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("Z", fieldMilkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(true, fieldMilkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer, fieldMilkManufacturerEntry.CurrentValue); + Assert.Equal("Nom", fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(9, fieldMilkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tog, fieldMilkManTogEntry.CurrentValue); + Assert.Equal("Tog1", fieldMilkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tag, fieldMilkManTagEntry.CurrentValue); + Assert.Equal("Tag1", fieldMilkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License, fieldMilkLicenseEntry.CurrentValue); + Assert.Equal("Title", fieldMilkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(11.0m, fieldMilkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tog, fieldMilkLicTogEntry.CurrentValue); + Assert.Equal("Tog2", fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tag, fieldMilkLicTagEntry.CurrentValue); + Assert.Equal("Tag2", fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue); + + context.ChangeTracker.DetectChanges(); + + Assert.Equal(yogurt.Culture, cultureEntry.CurrentValue); + Assert.Equal(11, cultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("XY", cultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("Z", cultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(true, cultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer, cultureManufacturerEntry.CurrentValue); + Assert.Equal("Nom", cultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(9, cultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tog, cultureManTogEntry.CurrentValue); + Assert.Equal("Tog1", cultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tag, cultureManTagEntry.CurrentValue); + Assert.Equal("Tag1", cultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License, cultureLicenseEntry.CurrentValue); + Assert.Equal("Title", cultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(11.0m, cultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tog, cultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2", cultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tag, cultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2", cultureLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Equal(yogurt.Milk, milkEntry.CurrentValue); + Assert.Equal(11, milkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("XY", milkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("Z", milkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(true, milkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer, milkManufacturerEntry.CurrentValue); + Assert.Equal("Nom", milkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(9, milkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tog, milkManTogEntry.CurrentValue); + Assert.Equal("Tog1", milkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tag, milkManTagEntry.CurrentValue); + Assert.Equal("Tag1", milkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License, milkLicenseEntry.CurrentValue); + Assert.Equal("Title", milkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(11.0m, milkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tog, milkLicTogEntry.CurrentValue); + Assert.Equal("Tog2", milkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tag, milkLicTagEntry.CurrentValue); + Assert.Equal("Tag2", milkLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Equal(yogurt.FieldCulture, fieldCultureEntry.CurrentValue); + Assert.Equal(11, fieldCultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("XY", fieldCultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("Z", fieldCultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(true, fieldCultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer, fieldCultureManufacturerEntry.CurrentValue); + Assert.Equal("Nom", fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(9, fieldCultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tog, fieldCultureManTogEntry.CurrentValue); + Assert.Equal("Tog1", fieldCultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tag, fieldCultureManTagEntry.CurrentValue); + Assert.Equal("Tag1", fieldCultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License, fieldCultureLicenseEntry.CurrentValue); + Assert.Equal("Title", fieldCultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(11.0m, fieldCultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tog, fieldCultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2", fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tag, fieldCultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2", fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Equal(yogurt.FieldMilk, fieldMilkEntry.CurrentValue); + Assert.Equal(11, fieldMilkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("XY", fieldMilkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("Z", fieldMilkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(true, fieldMilkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer, fieldMilkManufacturerEntry.CurrentValue); + Assert.Equal("Nom", fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(9, fieldMilkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tog, fieldMilkManTogEntry.CurrentValue); + Assert.Equal("Tog1", fieldMilkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tag, fieldMilkManTagEntry.CurrentValue); + Assert.Equal("Tag1", fieldMilkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License, fieldMilkLicenseEntry.CurrentValue); + Assert.Equal("Title", fieldMilkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(11.0m, fieldMilkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tog, fieldMilkLicTogEntry.CurrentValue); + Assert.Equal("Tog2", fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tag, fieldMilkLicTagEntry.CurrentValue); + Assert.Equal("Tag2", fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue); + } + + [ConditionalFact] + public void Can_set_current_value_for_complex_property_using_complex_type() + { + using var context = new YogurtContext(); + var yogurt = new Yogurt + { + Culture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldCulture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldMilk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + } + }; + + var entry = context.Entry(yogurt); + entry.State = EntityState.Unchanged; + + var cultureEntry = entry.ComplexProperty(e => e.Culture); + var cultureManufacturerEntry = cultureEntry.ComplexProperty(e => e.Manufacturer); + var cultureLicenseEntry = cultureEntry.ComplexProperty(e => e.License); + var cultureManTogEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tog); + var cultureManTagEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tag); + var cultureLicTogEntry = cultureLicenseEntry.ComplexProperty(e => e.Tog); + var cultureLicTagEntry = cultureLicenseEntry.ComplexProperty(e => e.Tag); + + cultureManTagEntry.CurrentValue = new() { Text = "Tag1a" }; + cultureManTogEntry.CurrentValue = new() { Text = "Tog1a" }; + cultureLicTagEntry.CurrentValue = new() { Text = "Tag2a" }; + cultureLicTogEntry.CurrentValue = new() { Text = "Tog2a" }; + + Assert.Equal(yogurt.Culture, cultureEntry.CurrentValue); + Assert.Equal(yogurt.Culture.Rating, cultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Culture.Species, cultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.Culture.Subspecies, cultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.Culture.Validation, cultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer, cultureManufacturerEntry.CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Name, cultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Rating, cultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tog, cultureManTogEntry.CurrentValue); + Assert.Equal("Tog1a", cultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tag, cultureManTagEntry.CurrentValue); + Assert.Equal("Tag1a", cultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License, cultureLicenseEntry.CurrentValue); + Assert.Equal(yogurt.Culture.License.Title, cultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(yogurt.Culture.License.Charge, cultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tog, cultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2a", cultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tag, cultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2a", cultureLicTagEntry.Property(e => e.Text).CurrentValue); + + cultureManufacturerEntry.CurrentValue = new() + { + Name = "NameB", + Rating = -7, + Tag = new() { Text = "Tag1b" }, + Tog = new() { Text = "Tog1b" } + }; + + cultureLicenseEntry.CurrentValue = new() + { + Charge = -1.0m, + Title = "TitleB", + Tag = new() { Text = "Tag2b" }, + Tog = new() { Text = "Tog2b" } + }; + + Assert.Equal(yogurt.Culture, cultureEntry.CurrentValue); + Assert.Equal(yogurt.Culture.Rating, cultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Culture.Species, cultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.Culture.Subspecies, cultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.Culture.Validation, cultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer, cultureManufacturerEntry.CurrentValue); + Assert.Equal("NameB", cultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-7, cultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tog, cultureManTogEntry.CurrentValue); + Assert.Equal("Tog1b", cultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tag, cultureManTagEntry.CurrentValue); + Assert.Equal("Tag1b", cultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License, cultureLicenseEntry.CurrentValue); + Assert.Equal("TitleB", cultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-1.0m, cultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tog, cultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2b", cultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tag, cultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2b", cultureLicTagEntry.Property(e => e.Text).CurrentValue); + + cultureEntry.CurrentValue = new() + { + License = new() + { + Charge = -2.0m, + Title = "TitleC", + Tag = new() { Text = "Tag2c" }, + Tog = new() { Text = "Tog2c" } + }, + Manufacturer = new() + { + Name = "NameC", + Rating = -8, + Tag = new() { Text = "Tag1c" }, + Tog = new() { Text = "Tog1c" } + }, + Rating = -77, + Species = "SpC", + Subspecies = "SpS", + Validation = null + }; + + Assert.Equal(yogurt.Culture, cultureEntry.CurrentValue); + Assert.Equal(-77, cultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("SpC", cultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("SpS", cultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(cultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer, cultureManufacturerEntry.CurrentValue); + Assert.Equal("NameC", cultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-8, cultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tog, cultureManTogEntry.CurrentValue); + Assert.Equal("Tog1c", cultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tag, cultureManTagEntry.CurrentValue); + Assert.Equal("Tag1c", cultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License, cultureLicenseEntry.CurrentValue); + Assert.Equal("TitleC", cultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-2.0m, cultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tog, cultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2c", cultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tag, cultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2c", cultureLicTagEntry.Property(e => e.Text).CurrentValue); + + var milkEntry = entry.ComplexProperty(e => e.Milk); + var milkManufacturerEntry = milkEntry.ComplexProperty(e => e.Manufacturer); + var milkLicenseEntry = milkEntry.ComplexProperty(e => e.License); + var milkManTogEntry = milkManufacturerEntry.ComplexProperty(e => e.Tog); + var milkManTagEntry = milkManufacturerEntry.ComplexProperty(e => e.Tag); + var milkLicTogEntry = milkLicenseEntry.ComplexProperty(e => e.Tog); + var milkLicTagEntry = milkLicenseEntry.ComplexProperty(e => e.Tag); + + milkManTagEntry.CurrentValue = new() { Text = "Tag1a" }; + milkManTogEntry.CurrentValue = new() { Text = "Tog1a" }; + milkLicTagEntry.CurrentValue = new() { Text = "Tag2a" }; + milkLicTogEntry.CurrentValue = new() { Text = "Tog2a" }; + + Assert.Equal(yogurt.Milk, milkEntry.CurrentValue); + Assert.Equal(yogurt.Milk.Rating, milkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Milk.Species, milkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.Milk.Subspecies, milkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.Milk.Validation, milkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer, milkManufacturerEntry.CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Name, milkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Rating, milkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tog, milkManTogEntry.CurrentValue); + Assert.Equal("Tog1a", milkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tag, milkManTagEntry.CurrentValue); + Assert.Equal("Tag1a", milkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License, milkLicenseEntry.CurrentValue); + Assert.Equal(yogurt.Milk.License.Title, milkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(yogurt.Milk.License.Charge, milkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tog, milkLicTogEntry.CurrentValue); + Assert.Equal("Tog2a", milkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tag, milkLicTagEntry.CurrentValue); + Assert.Equal("Tag2a", milkLicTagEntry.Property(e => e.Text).CurrentValue); + + milkManufacturerEntry.CurrentValue = new() + { + Name = "NameB", + Rating = -7, + Tag = new() { Text = "Tag1b" }, + Tog = new() { Text = "Tog1b" } + }; + + milkLicenseEntry.CurrentValue = new() + { + Charge = -1.0m, + Title = "TitleB", + Tag = new() { Text = "Tag2b" }, + Tog = new() { Text = "Tog2b" } + }; + + Assert.Equal(yogurt.Milk, milkEntry.CurrentValue); + Assert.Equal(yogurt.Milk.Rating, milkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Milk.Species, milkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.Milk.Subspecies, milkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.Milk.Validation, milkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer, milkManufacturerEntry.CurrentValue); + Assert.Equal("NameB", milkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-7, milkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tog, milkManTogEntry.CurrentValue); + Assert.Equal("Tog1b", milkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tag, milkManTagEntry.CurrentValue); + Assert.Equal("Tag1b", milkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License, milkLicenseEntry.CurrentValue); + Assert.Equal("TitleB", milkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-1.0m, milkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tog, milkLicTogEntry.CurrentValue); + Assert.Equal("Tog2b", milkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tag, milkLicTagEntry.CurrentValue); + Assert.Equal("Tag2b", milkLicTagEntry.Property(e => e.Text).CurrentValue); + + milkEntry.CurrentValue = new() + { + License = new() + { + Charge = -2.0m, + Title = "TitleC", + Tag = new() { Text = "Tag2c" }, + Tog = new() { Text = "Tog2c" } + }, + Manufacturer = new() + { + Name = "NameC", + Rating = -8, + Tag = new() { Text = "Tag1c" }, + Tog = new() { Text = "Tog1c" } + }, + Rating = -77, + Species = "SpC", + Subspecies = "SpS", + Validation = null + }; + + Assert.Equal(yogurt.Milk, milkEntry.CurrentValue); + Assert.Equal(-77, milkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("SpC", milkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("SpS", milkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(milkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer, milkManufacturerEntry.CurrentValue); + Assert.Equal("NameC", milkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-8, milkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tog, milkManTogEntry.CurrentValue); + Assert.Equal("Tog1c", milkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tag, milkManTagEntry.CurrentValue); + Assert.Equal("Tag1c", milkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License, milkLicenseEntry.CurrentValue); + Assert.Equal("TitleC", milkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-2.0m, milkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tog, milkLicTogEntry.CurrentValue); + Assert.Equal("Tog2c", milkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tag, milkLicTagEntry.CurrentValue); + Assert.Equal("Tag2c", milkLicTagEntry.Property(e => e.Text).CurrentValue); + + var fieldCultureEntry = entry.ComplexProperty(e => e.FieldCulture); + var fieldCultureManufacturerEntry = fieldCultureEntry.ComplexProperty(e => e.Manufacturer); + var fieldCultureLicenseEntry = fieldCultureEntry.ComplexProperty(e => e.License); + var fieldCultureManTogEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldCultureManTagEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldCultureLicTogEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tog); + var fieldCultureLicTagEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tag); + + fieldCultureManTagEntry.CurrentValue = new() { Text = "Tag1a" }; + fieldCultureManTogEntry.CurrentValue = new() { Text = "Tog1a" }; + fieldCultureLicTagEntry.CurrentValue = new() { Text = "Tag2a" }; + fieldCultureLicTogEntry.CurrentValue = new() { Text = "Tog2a" }; + + Assert.Equal(yogurt.FieldCulture, fieldCultureEntry.CurrentValue); + Assert.Equal(yogurt.FieldCulture.Rating, fieldCultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Species, fieldCultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Subspecies, fieldCultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Validation, fieldCultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer, fieldCultureManufacturerEntry.CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Name, fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Rating, fieldCultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tog, fieldCultureManTogEntry.CurrentValue); + Assert.Equal("Tog1a", fieldCultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tag, fieldCultureManTagEntry.CurrentValue); + Assert.Equal("Tag1a", fieldCultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License, fieldCultureLicenseEntry.CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Title, fieldCultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Charge, fieldCultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tog, fieldCultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2a", fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tag, fieldCultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2a", fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue); + + fieldCultureManufacturerEntry.CurrentValue = new() + { + Name = "NameB", + Rating = -7, + Tag = new() { Text = "Tag1b" }, + Tog = new() { Text = "Tog1b" } + }; + + fieldCultureLicenseEntry.CurrentValue = new() + { + Charge = -1.0m, + Title = "TitleB", + Tag = new() { Text = "Tag2b" }, + Tog = new() { Text = "Tog2b" } + }; + + Assert.Equal(yogurt.FieldCulture, fieldCultureEntry.CurrentValue); + Assert.Equal(yogurt.FieldCulture.Rating, fieldCultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Species, fieldCultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Subspecies, fieldCultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Validation, fieldCultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer, fieldCultureManufacturerEntry.CurrentValue); + Assert.Equal("NameB", fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-7, fieldCultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tog, fieldCultureManTogEntry.CurrentValue); + Assert.Equal("Tog1b", fieldCultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tag, fieldCultureManTagEntry.CurrentValue); + Assert.Equal("Tag1b", fieldCultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License, fieldCultureLicenseEntry.CurrentValue); + Assert.Equal("TitleB", fieldCultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-1.0m, fieldCultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tog, fieldCultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2b", fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tag, fieldCultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2b", fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue); + + fieldCultureEntry.CurrentValue = new() + { + License = new() + { + Charge = -2.0m, + Title = "TitleC", + Tag = new() { Text = "Tag2c" }, + Tog = new() { Text = "Tog2c" } + }, + Manufacturer = new() + { + Name = "NameC", + Rating = -8, + Tag = new() { Text = "Tag1c" }, + Tog = new() { Text = "Tog1c" } + }, + Rating = -77, + Species = "SpC", + Subspecies = "SpS", + Validation = null + }; + + Assert.Equal(yogurt.FieldCulture, fieldCultureEntry.CurrentValue); + Assert.Equal(-77, fieldCultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("SpC", fieldCultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("SpS", fieldCultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(fieldCultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer, fieldCultureManufacturerEntry.CurrentValue); + Assert.Equal("NameC", fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-8, fieldCultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tog, fieldCultureManTogEntry.CurrentValue); + Assert.Equal("Tog1c", fieldCultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tag, fieldCultureManTagEntry.CurrentValue); + Assert.Equal("Tag1c", fieldCultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License, fieldCultureLicenseEntry.CurrentValue); + Assert.Equal("TitleC", fieldCultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-2.0m, fieldCultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tog, fieldCultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2c", fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tag, fieldCultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2c", fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue); + + var fieldMilkEntry = entry.ComplexProperty(e => e.FieldMilk); + var fieldMilkManufacturerEntry = fieldMilkEntry.ComplexProperty(e => e.Manufacturer); + var fieldMilkLicenseEntry = fieldMilkEntry.ComplexProperty(e => e.License); + var fieldMilkManTogEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldMilkManTagEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldMilkLicTogEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tog); + var fieldMilkLicTagEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tag); + + fieldMilkManTagEntry.CurrentValue = new() { Text = "Tag1a" }; + fieldMilkManTogEntry.CurrentValue = new() { Text = "Tog1a" }; + fieldMilkLicTagEntry.CurrentValue = new() { Text = "Tag2a" }; + fieldMilkLicTogEntry.CurrentValue = new() { Text = "Tog2a" }; + + Assert.Equal(yogurt.FieldMilk, fieldMilkEntry.CurrentValue); + Assert.Equal(yogurt.FieldMilk.Rating, fieldMilkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Species, fieldMilkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Subspecies, fieldMilkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Validation, fieldMilkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer, fieldMilkManufacturerEntry.CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Name, fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Rating, fieldMilkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tog, fieldMilkManTogEntry.CurrentValue); + Assert.Equal("Tog1a", fieldMilkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tag, fieldMilkManTagEntry.CurrentValue); + Assert.Equal("Tag1a", fieldMilkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License, fieldMilkLicenseEntry.CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Title, fieldMilkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Charge, fieldMilkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tog, fieldMilkLicTogEntry.CurrentValue); + Assert.Equal("Tog2a", fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tag, fieldMilkLicTagEntry.CurrentValue); + Assert.Equal("Tag2a", fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue); + + fieldMilkManufacturerEntry.CurrentValue = new() + { + Name = "NameB", + Rating = -7, + Tag = new() { Text = "Tag1b" }, + Tog = new() { Text = "Tog1b" } + }; + + fieldMilkLicenseEntry.CurrentValue = new() + { + Charge = -1.0m, + Title = "TitleB", + Tag = new() { Text = "Tag2b" }, + Tog = new() { Text = "Tog2b" } + }; + + Assert.Equal(yogurt.FieldMilk, fieldMilkEntry.CurrentValue); + Assert.Equal(yogurt.FieldMilk.Rating, fieldMilkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Species, fieldMilkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Subspecies, fieldMilkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Validation, fieldMilkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer, fieldMilkManufacturerEntry.CurrentValue); + Assert.Equal("NameB", fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-7, fieldMilkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tog, fieldMilkManTogEntry.CurrentValue); + Assert.Equal("Tog1b", fieldMilkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tag, fieldMilkManTagEntry.CurrentValue); + Assert.Equal("Tag1b", fieldMilkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License, fieldMilkLicenseEntry.CurrentValue); + Assert.Equal("TitleB", fieldMilkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-1.0m, fieldMilkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tog, fieldMilkLicTogEntry.CurrentValue); + Assert.Equal("Tog2b", fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tag, fieldMilkLicTagEntry.CurrentValue); + Assert.Equal("Tag2b", fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue); + + fieldMilkEntry.CurrentValue = new() + { + License = new() + { + Charge = -2.0m, + Title = "TitleC", + Tag = new() { Text = "Tag2c" }, + Tog = new() { Text = "Tog2c" } + }, + Manufacturer = new() + { + Name = "NameC", + Rating = -8, + Tag = new() { Text = "Tag1c" }, + Tog = new() { Text = "Tog1c" } + }, + Rating = -77, + Species = "SpC", + Subspecies = "SpS", + Validation = null + }; + + Assert.Equal(yogurt.FieldMilk, fieldMilkEntry.CurrentValue); + Assert.Equal(-77, fieldMilkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("SpC", fieldMilkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("SpS", fieldMilkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(fieldMilkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer, fieldMilkManufacturerEntry.CurrentValue); + Assert.Equal("NameC", fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-8, fieldMilkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tog, fieldMilkManTogEntry.CurrentValue); + Assert.Equal("Tog1c", fieldMilkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tag, fieldMilkManTagEntry.CurrentValue); + Assert.Equal("Tag1c", fieldMilkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License, fieldMilkLicenseEntry.CurrentValue); + Assert.Equal("TitleC", fieldMilkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-2.0m, fieldMilkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tog, fieldMilkLicTogEntry.CurrentValue); + Assert.Equal("Tog2c", fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tag, fieldMilkLicTagEntry.CurrentValue); + Assert.Equal("Tag2c", fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue); + + context.ChangeTracker.DetectChanges(); + + Assert.Equal(yogurt.Culture, cultureEntry.CurrentValue); + Assert.Equal(-77, cultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("SpC", cultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("SpS", cultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(cultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer, cultureManufacturerEntry.CurrentValue); + Assert.Equal("NameC", cultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-8, cultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tog, cultureManTogEntry.CurrentValue); + Assert.Equal("Tog1c", cultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.Manufacturer.Tag, cultureManTagEntry.CurrentValue); + Assert.Equal("Tag1c", cultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License, cultureLicenseEntry.CurrentValue); + Assert.Equal("TitleC", cultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-2.0m, cultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tog, cultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2c", cultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Culture.License.Tag, cultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2c", cultureLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Equal(yogurt.Milk, milkEntry.CurrentValue); + Assert.Equal(-77, milkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("SpC", milkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("SpS", milkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(milkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer, milkManufacturerEntry.CurrentValue); + Assert.Equal("NameC", milkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-8, milkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tog, milkManTogEntry.CurrentValue); + Assert.Equal("Tog1c", milkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.Manufacturer.Tag, milkManTagEntry.CurrentValue); + Assert.Equal("Tag1c", milkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License, milkLicenseEntry.CurrentValue); + Assert.Equal("TitleC", milkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-2.0m, milkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tog, milkLicTogEntry.CurrentValue); + Assert.Equal("Tog2c", milkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.Milk.License.Tag, milkLicTagEntry.CurrentValue); + Assert.Equal("Tag2c", milkLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Equal(yogurt.FieldCulture, fieldCultureEntry.CurrentValue); + Assert.Equal(-77, fieldCultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("SpC", fieldCultureEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("SpS", fieldCultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(fieldCultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer, fieldCultureManufacturerEntry.CurrentValue); + Assert.Equal("NameC", fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-8, fieldCultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tog, fieldCultureManTogEntry.CurrentValue); + Assert.Equal("Tog1c", fieldCultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.Manufacturer.Tag, fieldCultureManTagEntry.CurrentValue); + Assert.Equal("Tag1c", fieldCultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License, fieldCultureLicenseEntry.CurrentValue); + Assert.Equal("TitleC", fieldCultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-2.0m, fieldCultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tog, fieldCultureLicTogEntry.CurrentValue); + Assert.Equal("Tog2c", fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldCulture.License.Tag, fieldCultureLicTagEntry.CurrentValue); + Assert.Equal("Tag2c", fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Equal(yogurt.FieldMilk, fieldMilkEntry.CurrentValue); + Assert.Equal(-77, fieldMilkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("SpC", fieldMilkEntry.Property(e => e.Species).CurrentValue); + Assert.Equal("SpS", fieldMilkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(fieldMilkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer, fieldMilkManufacturerEntry.CurrentValue); + Assert.Equal("NameC", fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(-8, fieldMilkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tog, fieldMilkManTogEntry.CurrentValue); + Assert.Equal("Tog1c", fieldMilkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.Manufacturer.Tag, fieldMilkManTagEntry.CurrentValue); + Assert.Equal("Tag1c", fieldMilkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License, fieldMilkLicenseEntry.CurrentValue); + Assert.Equal("TitleC", fieldMilkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(-2.0m, fieldMilkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tog, fieldMilkLicTogEntry.CurrentValue); + Assert.Equal("Tog2c", fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal(yogurt.FieldMilk.License.Tag, fieldMilkLicTagEntry.CurrentValue); + Assert.Equal("Tag2c", fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue); + } + + [ConditionalFact] + public void Can_set_current_value_for_property_of_complex_type_to_null() + { + using var context = new YogurtContext(); + var yogurt = new Yogurt + { + Culture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldCulture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldMilk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + } + }; + + var entry = context.Entry(yogurt); + entry.State = EntityState.Unchanged; + + var cultureEntry = entry.ComplexProperty(e => e.Culture); + var cultureManufacturerEntry = cultureEntry.ComplexProperty(e => e.Manufacturer); + var cultureLicenseEntry = cultureEntry.ComplexProperty(e => e.License); + var cultureManTogEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tog); + var cultureManTagEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tag); + var cultureLicTogEntry = cultureLicenseEntry.ComplexProperty(e => e.Tog); + var cultureLicTagEntry = cultureLicenseEntry.ComplexProperty(e => e.Tag); + + cultureEntry.Property(e => e.Subspecies).CurrentValue = null; + cultureEntry.Property(e => e.Validation).CurrentValue = null; + cultureManufacturerEntry.Property(e => e.Name).CurrentValue = null; + cultureManTogEntry.Property(e => e.Text).CurrentValue = null; + cultureManTagEntry.Property(e => e.Text).CurrentValue = null; + cultureLicTogEntry.Property(e => e.Text).CurrentValue = null; + cultureLicTagEntry.Property(e => e.Text).CurrentValue = null; + + Assert.Null(cultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(cultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Null(cultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Null(cultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(cultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Null(cultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(cultureLicTagEntry.Property(e => e.Text).CurrentValue); + + var milkEntry = entry.ComplexProperty(e => e.Milk); + var milkManufacturerEntry = milkEntry.ComplexProperty(e => e.Manufacturer); + var milkLicenseEntry = milkEntry.ComplexProperty(e => e.License); + var milkManTogEntry = milkManufacturerEntry.ComplexProperty(e => e.Tog); + var milkManTagEntry = milkManufacturerEntry.ComplexProperty(e => e.Tag); + var milkLicTogEntry = milkLicenseEntry.ComplexProperty(e => e.Tog); + var milkLicTagEntry = milkLicenseEntry.ComplexProperty(e => e.Tag); + + milkEntry.Property(e => e.Subspecies).CurrentValue = null; + milkEntry.Property(e => e.Validation).CurrentValue = null; + milkManufacturerEntry.Property(e => e.Name).CurrentValue = null; + milkManTogEntry.Property(e => e.Text).CurrentValue = null; + milkManTagEntry.Property(e => e.Text).CurrentValue = null; + milkLicTogEntry.Property(e => e.Text).CurrentValue = null; + milkLicTagEntry.Property(e => e.Text).CurrentValue = null; + + Assert.Null(milkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(milkEntry.Property(e => e.Validation).CurrentValue); + Assert.Null(milkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Null(milkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(milkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Null(milkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(milkLicTagEntry.Property(e => e.Text).CurrentValue); + + var fieldCultureEntry = entry.ComplexProperty(e => e.FieldCulture); + var fieldCultureManufacturerEntry = fieldCultureEntry.ComplexProperty(e => e.Manufacturer); + var fieldCultureLicenseEntry = fieldCultureEntry.ComplexProperty(e => e.License); + var fieldCultureManTogEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldCultureManTagEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldCultureLicTogEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tog); + var fieldCultureLicTagEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tag); + + fieldCultureEntry.Property(e => e.Subspecies).CurrentValue = null; + fieldCultureEntry.Property(e => e.Validation).CurrentValue = null; + fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue = null; + fieldCultureManTogEntry.Property(e => e.Text).CurrentValue = null; + fieldCultureManTagEntry.Property(e => e.Text).CurrentValue = null; + fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue = null; + fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue = null; + + Assert.Null(fieldCultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(fieldCultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Null(fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Null(fieldCultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldCultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue); + + var fieldMilkEntry = entry.ComplexProperty(e => e.FieldMilk); + var fieldMilkManufacturerEntry = fieldMilkEntry.ComplexProperty(e => e.Manufacturer); + var fieldMilkLicenseEntry = fieldMilkEntry.ComplexProperty(e => e.License); + var fieldMilkManTogEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldMilkManTagEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldMilkLicTogEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tog); + var fieldMilkLicTagEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tag); + + fieldMilkEntry.Property(e => e.Subspecies).CurrentValue = null; + fieldMilkEntry.Property(e => e.Validation).CurrentValue = null; + fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue = null; + fieldMilkManTogEntry.Property(e => e.Text).CurrentValue = null; + fieldMilkManTagEntry.Property(e => e.Text).CurrentValue = null; + fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue = null; + fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue = null; + + Assert.Null(fieldMilkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(fieldMilkEntry.Property(e => e.Validation).CurrentValue); + Assert.Null(fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Null(fieldMilkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldMilkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue); + + context.ChangeTracker.DetectChanges(); + + Assert.Null(cultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(cultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Null(cultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Null(cultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(cultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Null(cultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(cultureLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Null(milkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(milkEntry.Property(e => e.Validation).CurrentValue); + Assert.Null(milkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Null(milkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(milkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Null(milkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(milkLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Null(fieldCultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(fieldCultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Null(fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Null(fieldCultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldCultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Null(fieldMilkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.Null(fieldMilkEntry.Property(e => e.Validation).CurrentValue); + Assert.Null(fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Null(fieldMilkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldMilkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Null(fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue); + } + + [ConditionalFact] + public void Can_set_and_get_original_value_for_complex_property() + { + using var context = new YogurtContext(); + var yogurt = new Yogurt + { + Culture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldCulture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldMilk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + } + }; + + var entry = context.Entry(yogurt); + entry.State = EntityState.Unchanged; + + var cultureEntry = entry.ComplexProperty(e => e.Culture); + var cultureManufacturerEntry = cultureEntry.ComplexProperty(e => e.Manufacturer); + var cultureLicenseEntry = cultureEntry.ComplexProperty(e => e.License); + var cultureManTogEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tog); + var cultureManTagEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tag); + var cultureLicTogEntry = cultureLicenseEntry.ComplexProperty(e => e.Tog); + var cultureLicTagEntry = cultureLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(8, cultureEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("S1", cultureEntry.Property(e => e.Species).OriginalValue); + Assert.Null(cultureEntry.Property(e => e.Subspecies).OriginalValue); + Assert.False(cultureEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("M1", cultureManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(7, cultureManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("To2", cultureManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ta2", cultureManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ti1", cultureLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(1.0m, cultureLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("To1", cultureLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ta1", cultureLicTagEntry.Property(e => e.Text).OriginalValue); + + cultureEntry.Property(e => e.Rating).OriginalValue = 11; + cultureEntry.Property(e => e.Species).OriginalValue = "XY"; + cultureEntry.Property(e => e.Subspecies).OriginalValue = "Z"; + cultureEntry.Property(e => e.Validation).OriginalValue = true; + cultureManufacturerEntry.Property(e => e.Name).OriginalValue = "Nom"; + cultureManufacturerEntry.Property(e => e.Rating).OriginalValue = 9; + cultureManTogEntry.Property(e => e.Text).OriginalValue = "Tog1"; + cultureManTagEntry.Property(e => e.Text).OriginalValue = "Tag1"; + cultureLicenseEntry.Property(e => e.Title).OriginalValue = "Title"; + cultureLicenseEntry.Property(e => e.Charge).OriginalValue = 11.0m; + cultureLicTogEntry.Property(e => e.Text).OriginalValue = "Tog2"; + cultureLicTagEntry.Property(e => e.Text).OriginalValue = "Tag2"; + + Assert.Equal(11, cultureEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("XY", cultureEntry.Property(e => e.Species).OriginalValue); + Assert.Equal("Z", cultureEntry.Property(e => e.Subspecies).OriginalValue); + Assert.Equal(true, cultureEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("Nom", cultureManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(9, cultureManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("Tog1", cultureManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag1", cultureManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Title", cultureLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(11.0m, cultureLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("Tog2", cultureLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag2", cultureLicTagEntry.Property(e => e.Text).OriginalValue); + + Assert.Equal(8, cultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("S1", cultureEntry.Property(e => e.Species).CurrentValue); + Assert.Null(cultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.False(cultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal("M1", cultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(7, cultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("To2", cultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta2", cultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ti1", cultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(1.0m, cultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal("To1", cultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta1", cultureLicTagEntry.Property(e => e.Text).CurrentValue); + + var milkEntry = entry.ComplexProperty(e => e.Milk); + var milkManufacturerEntry = milkEntry.ComplexProperty(e => e.Manufacturer); + var milkLicenseEntry = milkEntry.ComplexProperty(e => e.License); + var milkManTogEntry = milkManufacturerEntry.ComplexProperty(e => e.Tog); + var milkManTagEntry = milkManufacturerEntry.ComplexProperty(e => e.Tag); + var milkLicTogEntry = milkLicenseEntry.ComplexProperty(e => e.Tog); + var milkLicTagEntry = milkLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(8, milkEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("S1", milkEntry.Property(e => e.Species).OriginalValue); + Assert.Null(milkEntry.Property(e => e.Subspecies).OriginalValue); + Assert.False(milkEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("M1", milkManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(7, milkManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("To2", milkManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ta2", milkManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ti1", milkLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(1.0m, milkLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("To1", milkLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ta1", milkLicTagEntry.Property(e => e.Text).OriginalValue); + + milkEntry.Property(e => e.Rating).OriginalValue = 11; + milkEntry.Property(e => e.Species).OriginalValue = "XY"; + milkEntry.Property(e => e.Subspecies).OriginalValue = "Z"; + milkEntry.Property(e => e.Validation).OriginalValue = true; + milkManufacturerEntry.Property(e => e.Name).OriginalValue = "Nom"; + milkManufacturerEntry.Property(e => e.Rating).OriginalValue = 9; + milkManTogEntry.Property(e => e.Text).OriginalValue = "Tog1"; + milkManTagEntry.Property(e => e.Text).OriginalValue = "Tag1"; + milkLicenseEntry.Property(e => e.Title).OriginalValue = "Title"; + milkLicenseEntry.Property(e => e.Charge).OriginalValue = 11.0m; + milkLicTogEntry.Property(e => e.Text).OriginalValue = "Tog2"; + milkLicTagEntry.Property(e => e.Text).OriginalValue = "Tag2"; + + Assert.Equal(11, milkEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("XY", milkEntry.Property(e => e.Species).OriginalValue); + Assert.Equal("Z", milkEntry.Property(e => e.Subspecies).OriginalValue); + Assert.Equal(true, milkEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("Nom", milkManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(9, milkManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("Tog1", milkManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag1", milkManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Title", milkLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(11.0m, milkLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("Tog2", milkLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag2", milkLicTagEntry.Property(e => e.Text).OriginalValue); + + Assert.Equal(8, milkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("S1", milkEntry.Property(e => e.Species).CurrentValue); + Assert.Null(milkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.False(milkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal("M1", milkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(7, milkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("To2", milkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta2", milkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ti1", milkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(1.0m, milkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal("To1", milkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta1", milkLicTagEntry.Property(e => e.Text).CurrentValue); + + var fieldCultureEntry = entry.ComplexProperty(e => e.FieldCulture); + var fieldCultureManufacturerEntry = fieldCultureEntry.ComplexProperty(e => e.Manufacturer); + var fieldCultureLicenseEntry = fieldCultureEntry.ComplexProperty(e => e.License); + var fieldCultureManTogEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldCultureManTagEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldCultureLicTogEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tog); + var fieldCultureLicTagEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(8, fieldCultureEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("S1", fieldCultureEntry.Property(e => e.Species).OriginalValue); + Assert.Null(fieldCultureEntry.Property(e => e.Subspecies).OriginalValue); + Assert.False(fieldCultureEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("M1", fieldCultureManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(7, fieldCultureManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("To2", fieldCultureManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ta2", fieldCultureManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ti1", fieldCultureLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(1.0m, fieldCultureLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("To1", fieldCultureLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ta1", fieldCultureLicTagEntry.Property(e => e.Text).OriginalValue); + + fieldCultureEntry.Property(e => e.Rating).OriginalValue = 11; + fieldCultureEntry.Property(e => e.Species).OriginalValue = "XY"; + fieldCultureEntry.Property(e => e.Subspecies).OriginalValue = "Z"; + fieldCultureEntry.Property(e => e.Validation).OriginalValue = true; + fieldCultureManufacturerEntry.Property(e => e.Name).OriginalValue = "Nom"; + fieldCultureManufacturerEntry.Property(e => e.Rating).OriginalValue = 9; + fieldCultureManTogEntry.Property(e => e.Text).OriginalValue = "Tog1"; + fieldCultureManTagEntry.Property(e => e.Text).OriginalValue = "Tag1"; + fieldCultureLicenseEntry.Property(e => e.Title).OriginalValue = "Title"; + fieldCultureLicenseEntry.Property(e => e.Charge).OriginalValue = 11.0m; + fieldCultureLicTogEntry.Property(e => e.Text).OriginalValue = "Tog2"; + fieldCultureLicTagEntry.Property(e => e.Text).OriginalValue = "Tag2"; + + Assert.Equal(11, fieldCultureEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("XY", fieldCultureEntry.Property(e => e.Species).OriginalValue); + Assert.Equal("Z", fieldCultureEntry.Property(e => e.Subspecies).OriginalValue); + Assert.Equal(true, fieldCultureEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("Nom", fieldCultureManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(9, fieldCultureManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("Tog1", fieldCultureManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag1", fieldCultureManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Title", fieldCultureLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(11.0m, fieldCultureLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("Tog2", fieldCultureLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag2", fieldCultureLicTagEntry.Property(e => e.Text).OriginalValue); + + Assert.Equal(8, fieldCultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("S1", fieldCultureEntry.Property(e => e.Species).CurrentValue); + Assert.Null(fieldCultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.False(fieldCultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal("M1", fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(7, fieldCultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("To2", fieldCultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta2", fieldCultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ti1", fieldCultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(1.0m, fieldCultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal("To1", fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta1", fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue); + + var fieldMilkEntry = entry.ComplexProperty(e => e.FieldMilk); + var fieldMilkManufacturerEntry = fieldMilkEntry.ComplexProperty(e => e.Manufacturer); + var fieldMilkLicenseEntry = fieldMilkEntry.ComplexProperty(e => e.License); + var fieldMilkManTogEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldMilkManTagEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldMilkLicTogEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tog); + var fieldMilkLicTagEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(8, fieldMilkEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("S1", fieldMilkEntry.Property(e => e.Species).OriginalValue); + Assert.Null(fieldMilkEntry.Property(e => e.Subspecies).OriginalValue); + Assert.False(fieldMilkEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("M1", fieldMilkManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(7, fieldMilkManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("To2", fieldMilkManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ta2", fieldMilkManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ti1", fieldMilkLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(1.0m, fieldMilkLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("To1", fieldMilkLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Ta1", fieldMilkLicTagEntry.Property(e => e.Text).OriginalValue); + + fieldMilkEntry.Property(e => e.Rating).OriginalValue = 11; + fieldMilkEntry.Property(e => e.Species).OriginalValue = "XY"; + fieldMilkEntry.Property(e => e.Subspecies).OriginalValue = "Z"; + fieldMilkEntry.Property(e => e.Validation).OriginalValue = true; + fieldMilkManufacturerEntry.Property(e => e.Name).OriginalValue = "Nom"; + fieldMilkManufacturerEntry.Property(e => e.Rating).OriginalValue = 9; + fieldMilkManTogEntry.Property(e => e.Text).OriginalValue = "Tog1"; + fieldMilkManTagEntry.Property(e => e.Text).OriginalValue = "Tag1"; + fieldMilkLicenseEntry.Property(e => e.Title).OriginalValue = "Title"; + fieldMilkLicenseEntry.Property(e => e.Charge).OriginalValue = 11.0m; + fieldMilkLicTogEntry.Property(e => e.Text).OriginalValue = "Tog2"; + fieldMilkLicTagEntry.Property(e => e.Text).OriginalValue = "Tag2"; + + Assert.Equal(11, fieldMilkEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("XY", fieldMilkEntry.Property(e => e.Species).OriginalValue); + Assert.Equal("Z", fieldMilkEntry.Property(e => e.Subspecies).OriginalValue); + Assert.Equal(true, fieldMilkEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("Nom", fieldMilkManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(9, fieldMilkManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("Tog1", fieldMilkManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag1", fieldMilkManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Title", fieldMilkLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(11.0m, fieldMilkLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("Tog2", fieldMilkLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag2", fieldMilkLicTagEntry.Property(e => e.Text).OriginalValue); + + Assert.Equal(8, fieldMilkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("S1", fieldMilkEntry.Property(e => e.Species).CurrentValue); + Assert.Null(fieldMilkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.False(fieldMilkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal("M1", fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(7, fieldMilkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("To2", fieldMilkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta2", fieldMilkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ti1", fieldMilkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(1.0m, fieldMilkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal("To1", fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta1", fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue); + + context.ChangeTracker.DetectChanges(); + + Assert.Equal(11, cultureEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("XY", cultureEntry.Property(e => e.Species).OriginalValue); + Assert.Equal("Z", cultureEntry.Property(e => e.Subspecies).OriginalValue); + Assert.Equal(true, cultureEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("Nom", cultureManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(9, cultureManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("Tog1", cultureManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag1", cultureManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Title", cultureLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(11.0m, cultureLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("Tog2", cultureLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag2", cultureLicTagEntry.Property(e => e.Text).OriginalValue); + + Assert.Equal(8, cultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("S1", cultureEntry.Property(e => e.Species).CurrentValue); + Assert.Null(cultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.False(cultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal("M1", cultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(7, cultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("To2", cultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta2", cultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ti1", cultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(1.0m, cultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal("To1", cultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta1", cultureLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Equal(11, milkEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("XY", milkEntry.Property(e => e.Species).OriginalValue); + Assert.Equal("Z", milkEntry.Property(e => e.Subspecies).OriginalValue); + Assert.Equal(true, milkEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("Nom", milkManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(9, milkManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("Tog1", milkManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag1", milkManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Title", milkLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(11.0m, milkLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("Tog2", milkLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag2", milkLicTagEntry.Property(e => e.Text).OriginalValue); + + Assert.Equal(8, milkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("S1", milkEntry.Property(e => e.Species).CurrentValue); + Assert.Null(milkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.False(milkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal("M1", milkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(7, milkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("To2", milkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta2", milkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ti1", milkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(1.0m, milkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal("To1", milkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta1", milkLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Equal(11, fieldCultureEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("XY", fieldCultureEntry.Property(e => e.Species).OriginalValue); + Assert.Equal("Z", fieldCultureEntry.Property(e => e.Subspecies).OriginalValue); + Assert.Equal(true, fieldCultureEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("Nom", fieldCultureManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(9, fieldCultureManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("Tog1", fieldCultureManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag1", fieldCultureManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Title", fieldCultureLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(11.0m, fieldCultureLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("Tog2", fieldCultureLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag2", fieldCultureLicTagEntry.Property(e => e.Text).OriginalValue); + + Assert.Equal(8, fieldCultureEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("S1", fieldCultureEntry.Property(e => e.Species).CurrentValue); + Assert.Null(fieldCultureEntry.Property(e => e.Subspecies).CurrentValue); + Assert.False(fieldCultureEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal("M1", fieldCultureManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(7, fieldCultureManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("To2", fieldCultureManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta2", fieldCultureManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ti1", fieldCultureLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(1.0m, fieldCultureLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal("To1", fieldCultureLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta1", fieldCultureLicTagEntry.Property(e => e.Text).CurrentValue); + + Assert.Equal(11, fieldMilkEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("XY", fieldMilkEntry.Property(e => e.Species).OriginalValue); + Assert.Equal("Z", fieldMilkEntry.Property(e => e.Subspecies).OriginalValue); + Assert.Equal(true, fieldMilkEntry.Property(e => e.Validation).OriginalValue); + Assert.Equal("Nom", fieldMilkManufacturerEntry.Property(e => e.Name).OriginalValue); + Assert.Equal(9, fieldMilkManufacturerEntry.Property(e => e.Rating).OriginalValue); + Assert.Equal("Tog1", fieldMilkManTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag1", fieldMilkManTagEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Title", fieldMilkLicenseEntry.Property(e => e.Title).OriginalValue); + Assert.Equal(11.0m, fieldMilkLicenseEntry.Property(e => e.Charge).OriginalValue); + Assert.Equal("Tog2", fieldMilkLicTogEntry.Property(e => e.Text).OriginalValue); + Assert.Equal("Tag2", fieldMilkLicTagEntry.Property(e => e.Text).OriginalValue); + + Assert.Equal(8, fieldMilkEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("S1", fieldMilkEntry.Property(e => e.Species).CurrentValue); + Assert.Null(fieldMilkEntry.Property(e => e.Subspecies).CurrentValue); + Assert.False(fieldMilkEntry.Property(e => e.Validation).CurrentValue); + Assert.Equal("M1", fieldMilkManufacturerEntry.Property(e => e.Name).CurrentValue); + Assert.Equal(7, fieldMilkManufacturerEntry.Property(e => e.Rating).CurrentValue); + Assert.Equal("To2", fieldMilkManTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta2", fieldMilkManTagEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ti1", fieldMilkLicenseEntry.Property(e => e.Title).CurrentValue); + Assert.Equal(1.0m, fieldMilkLicenseEntry.Property(e => e.Charge).CurrentValue); + Assert.Equal("To1", fieldMilkLicTogEntry.Property(e => e.Text).CurrentValue); + Assert.Equal("Ta1", fieldMilkLicTagEntry.Property(e => e.Text).CurrentValue); + } + + [ConditionalFact] + public void Can_set_and_clear_modified_on_Modified_entity_for_complex_property() + { + using var context = new YogurtContext(); + var yogurt = new Yogurt + { + Culture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldCulture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldMilk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + } + }; + + var entry = context.Entry(yogurt); + entry.State = EntityState.Unchanged; + + var cultureEntry = entry.ComplexProperty(e => e.Culture); + var cultureManufacturerEntry = cultureEntry.ComplexProperty(e => e.Manufacturer); + var cultureLicenseEntry = cultureEntry.ComplexProperty(e => e.License); + var cultureManTogEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tog); + var cultureManTagEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tag); + var cultureLicTogEntry = cultureLicenseEntry.ComplexProperty(e => e.Tog); + var cultureLicTagEntry = cultureLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.False(cultureManufacturerEntry.IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicTagEntry.IsModified); + Assert.False(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureLicTagEntry.Property(e => e.Text).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.False(cultureManufacturerEntry.IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.True(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(cultureLicTagEntry.IsModified); + Assert.True(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureLicTagEntry.Property(e => e.Text).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.False(cultureManufacturerEntry.IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicTagEntry.IsModified); + Assert.False(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureManufacturerEntry.Property(e => e.Name).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.True(cultureManufacturerEntry.IsModified); + Assert.True(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicTagEntry.IsModified); + Assert.False(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureManufacturerEntry.Property(e => e.Name).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.False(cultureManufacturerEntry.IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicTagEntry.IsModified); + Assert.False(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureEntry.Property(e => e.Subspecies).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.True(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.False(cultureManufacturerEntry.IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicTagEntry.IsModified); + Assert.False(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureEntry.Property(e => e.Subspecies).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.False(cultureManufacturerEntry.IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicTagEntry.IsModified); + Assert.False(cultureLicTagEntry.Property(e => e.Text).IsModified); + + var milkEntry = entry.ComplexProperty(e => e.Milk); + var milkManufacturerEntry = milkEntry.ComplexProperty(e => e.Manufacturer); + var milkLicenseEntry = milkEntry.ComplexProperty(e => e.License); + var milkManTogEntry = milkManufacturerEntry.ComplexProperty(e => e.Tog); + var milkManTagEntry = milkManufacturerEntry.ComplexProperty(e => e.Tag); + var milkLicTogEntry = milkLicenseEntry.ComplexProperty(e => e.Tog); + var milkLicTagEntry = milkLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.False(milkManufacturerEntry.IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicTagEntry.IsModified); + Assert.False(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkLicTagEntry.Property(e => e.Text).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.False(milkManufacturerEntry.IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.True(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(milkLicTagEntry.IsModified); + Assert.True(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkLicTagEntry.Property(e => e.Text).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.False(milkManufacturerEntry.IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicTagEntry.IsModified); + Assert.False(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkManufacturerEntry.Property(e => e.Name).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.True(milkManufacturerEntry.IsModified); + Assert.True(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicTagEntry.IsModified); + Assert.False(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkManufacturerEntry.Property(e => e.Name).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.False(milkManufacturerEntry.IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicTagEntry.IsModified); + Assert.False(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkEntry.Property(e => e.Subspecies).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.True(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.False(milkManufacturerEntry.IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicTagEntry.IsModified); + Assert.False(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkEntry.Property(e => e.Subspecies).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.False(milkManufacturerEntry.IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicTagEntry.IsModified); + Assert.False(milkLicTagEntry.Property(e => e.Text).IsModified); + + var fieldCultureEntry = entry.ComplexProperty(e => e.FieldCulture); + var fieldCultureManufacturerEntry = fieldCultureEntry.ComplexProperty(e => e.Manufacturer); + var fieldCultureLicenseEntry = fieldCultureEntry.ComplexProperty(e => e.License); + var fieldCultureManTogEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldCultureManTagEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldCultureLicTogEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tog); + var fieldCultureLicTagEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldCultureManufacturerEntry.IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicTagEntry.IsModified); + Assert.False(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureLicTagEntry.Property(e => e.Text).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldCultureManufacturerEntry.IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.True(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(fieldCultureLicTagEntry.IsModified); + Assert.True(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureLicTagEntry.Property(e => e.Text).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldCultureManufacturerEntry.IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicTagEntry.IsModified); + Assert.False(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureManufacturerEntry.Property(e => e.Name).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.True(fieldCultureManufacturerEntry.IsModified); + Assert.True(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicTagEntry.IsModified); + Assert.False(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureManufacturerEntry.Property(e => e.Name).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldCultureManufacturerEntry.IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicTagEntry.IsModified); + Assert.False(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureEntry.Property(e => e.Subspecies).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.True(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldCultureManufacturerEntry.IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicTagEntry.IsModified); + Assert.False(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureEntry.Property(e => e.Subspecies).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldCultureManufacturerEntry.IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicTagEntry.IsModified); + Assert.False(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + var fieldMilkEntry = entry.ComplexProperty(e => e.FieldMilk); + var fieldMilkManufacturerEntry = fieldMilkEntry.ComplexProperty(e => e.Manufacturer); + var fieldMilkLicenseEntry = fieldMilkEntry.ComplexProperty(e => e.License); + var fieldMilkManTogEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldMilkManTagEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldMilkLicTogEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tog); + var fieldMilkLicTagEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldMilkManufacturerEntry.IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicTagEntry.IsModified); + Assert.False(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkLicTagEntry.Property(e => e.Text).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldMilkManufacturerEntry.IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.True(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(fieldMilkLicTagEntry.IsModified); + Assert.True(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkLicTagEntry.Property(e => e.Text).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldMilkManufacturerEntry.IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicTagEntry.IsModified); + Assert.False(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkManufacturerEntry.Property(e => e.Name).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.True(fieldMilkManufacturerEntry.IsModified); + Assert.True(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicTagEntry.IsModified); + Assert.False(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkManufacturerEntry.Property(e => e.Name).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldMilkManufacturerEntry.IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicTagEntry.IsModified); + Assert.False(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkEntry.Property(e => e.Subspecies).IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.True(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldMilkManufacturerEntry.IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicTagEntry.IsModified); + Assert.False(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkEntry.Property(e => e.Subspecies).IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldMilkManufacturerEntry.IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicTagEntry.IsModified); + Assert.False(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + } + + [ConditionalFact] + public void Can_set_and_clear_modified_on_Modified_using_complex_type() + { + using var context = new YogurtContext(); + var yogurt = new Yogurt + { + Culture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + Milk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldCulture = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + }, + FieldMilk = new() + { + License = new() + { + Charge = 1.0m, + Tag = new() { Text = "Ta1" }, + Title = "Ti1", + Tog = new() { Text = "To1" } + }, + Manufacturer = new() + { + Name = "M1", + Rating = 7, + Tag = new() { Text = "Ta2" }, + Tog = new() { Text = "To2" } + }, + Rating = 8, + Species = "S1", + Validation = false + } + }; + + var entry = context.Entry(yogurt); + entry.State = EntityState.Unchanged; + + var cultureEntry = entry.ComplexProperty(e => e.Culture); + var cultureManufacturerEntry = cultureEntry.ComplexProperty(e => e.Manufacturer); + var cultureLicenseEntry = cultureEntry.ComplexProperty(e => e.License); + var cultureManTogEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tog); + var cultureManTagEntry = cultureManufacturerEntry.ComplexProperty(e => e.Tag); + var cultureLicTogEntry = cultureLicenseEntry.ComplexProperty(e => e.Tog); + var cultureLicTagEntry = cultureLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.False(cultureManufacturerEntry.IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicTagEntry.IsModified); + Assert.False(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureLicTagEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.False(cultureManufacturerEntry.IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.True(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(cultureLicTagEntry.IsModified); + Assert.True(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureLicTagEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.False(cultureManufacturerEntry.IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicTagEntry.IsModified); + Assert.False(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureManufacturerEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.True(cultureManufacturerEntry.IsModified); + Assert.True(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.True(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.True(cultureManTogEntry.IsModified); + Assert.True(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.True(cultureManTagEntry.IsModified); + Assert.True(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicTagEntry.IsModified); + Assert.False(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureManufacturerEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.False(cultureManufacturerEntry.IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicTagEntry.IsModified); + Assert.False(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(cultureEntry.IsModified); + Assert.True(cultureEntry.Property(e => e.Rating).IsModified); + Assert.True(cultureEntry.Property(e => e.Species).IsModified); + Assert.True(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.True(cultureEntry.Property(e => e.Validation).IsModified); + Assert.True(cultureManufacturerEntry.IsModified); + Assert.True(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.True(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.True(cultureManTogEntry.IsModified); + Assert.True(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.True(cultureManTagEntry.IsModified); + Assert.True(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.True(cultureLicenseEntry.IsModified); + Assert.True(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.True(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.True(cultureLicTogEntry.IsModified); + Assert.True(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(cultureLicTagEntry.IsModified); + Assert.True(cultureLicTagEntry.Property(e => e.Text).IsModified); + + cultureEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(cultureEntry.IsModified); + Assert.False(cultureEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureEntry.Property(e => e.Species).IsModified); + Assert.False(cultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(cultureEntry.Property(e => e.Validation).IsModified); + Assert.False(cultureManufacturerEntry.IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(cultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(cultureManTogEntry.IsModified); + Assert.False(cultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureManTagEntry.IsModified); + Assert.False(cultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicenseEntry.IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(cultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(cultureLicTogEntry.IsModified); + Assert.False(cultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(cultureLicTagEntry.IsModified); + Assert.False(cultureLicTagEntry.Property(e => e.Text).IsModified); + + var milkEntry = entry.ComplexProperty(e => e.Milk); + var milkManufacturerEntry = milkEntry.ComplexProperty(e => e.Manufacturer); + var milkLicenseEntry = milkEntry.ComplexProperty(e => e.License); + var milkManTogEntry = milkManufacturerEntry.ComplexProperty(e => e.Tog); + var milkManTagEntry = milkManufacturerEntry.ComplexProperty(e => e.Tag); + var milkLicTogEntry = milkLicenseEntry.ComplexProperty(e => e.Tog); + var milkLicTagEntry = milkLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.False(milkManufacturerEntry.IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicTagEntry.IsModified); + Assert.False(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkLicTagEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.False(milkManufacturerEntry.IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.True(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(milkLicTagEntry.IsModified); + Assert.True(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkLicTagEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.False(milkManufacturerEntry.IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicTagEntry.IsModified); + Assert.False(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkManufacturerEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.True(milkManufacturerEntry.IsModified); + Assert.True(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.True(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.True(milkManTogEntry.IsModified); + Assert.True(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.True(milkManTagEntry.IsModified); + Assert.True(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicTagEntry.IsModified); + Assert.False(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkManufacturerEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.False(milkManufacturerEntry.IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicTagEntry.IsModified); + Assert.False(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(milkEntry.IsModified); + Assert.True(milkEntry.Property(e => e.Rating).IsModified); + Assert.True(milkEntry.Property(e => e.Species).IsModified); + Assert.True(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.True(milkEntry.Property(e => e.Validation).IsModified); + Assert.True(milkManufacturerEntry.IsModified); + Assert.True(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.True(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.True(milkManTogEntry.IsModified); + Assert.True(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.True(milkManTagEntry.IsModified); + Assert.True(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.True(milkLicenseEntry.IsModified); + Assert.True(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.True(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.True(milkLicTogEntry.IsModified); + Assert.True(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(milkLicTagEntry.IsModified); + Assert.True(milkLicTagEntry.Property(e => e.Text).IsModified); + + milkEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(milkEntry.IsModified); + Assert.False(milkEntry.Property(e => e.Rating).IsModified); + Assert.False(milkEntry.Property(e => e.Species).IsModified); + Assert.False(milkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(milkEntry.Property(e => e.Validation).IsModified); + Assert.False(milkManufacturerEntry.IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(milkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(milkManTogEntry.IsModified); + Assert.False(milkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkManTagEntry.IsModified); + Assert.False(milkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicenseEntry.IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(milkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(milkLicTogEntry.IsModified); + Assert.False(milkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(milkLicTagEntry.IsModified); + Assert.False(milkLicTagEntry.Property(e => e.Text).IsModified); + + var fieldCultureEntry = entry.ComplexProperty(e => e.FieldCulture); + var fieldCultureManufacturerEntry = fieldCultureEntry.ComplexProperty(e => e.Manufacturer); + var fieldCultureLicenseEntry = fieldCultureEntry.ComplexProperty(e => e.License); + var fieldCultureManTogEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldCultureManTagEntry = fieldCultureManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldCultureLicTogEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tog); + var fieldCultureLicTagEntry = fieldCultureLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldCultureManufacturerEntry.IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicTagEntry.IsModified); + Assert.False(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureLicTagEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldCultureManufacturerEntry.IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.True(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(fieldCultureLicTagEntry.IsModified); + Assert.True(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureLicTagEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldCultureManufacturerEntry.IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicTagEntry.IsModified); + Assert.False(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureManufacturerEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.True(fieldCultureManufacturerEntry.IsModified); + Assert.True(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.True(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.True(fieldCultureManTogEntry.IsModified); + Assert.True(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.True(fieldCultureManTagEntry.IsModified); + Assert.True(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicTagEntry.IsModified); + Assert.False(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureManufacturerEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldCultureManufacturerEntry.IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicTagEntry.IsModified); + Assert.False(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldCultureEntry.IsModified); + Assert.True(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.True(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.True(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.True(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.True(fieldCultureManufacturerEntry.IsModified); + Assert.True(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.True(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.True(fieldCultureManTogEntry.IsModified); + Assert.True(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.True(fieldCultureManTagEntry.IsModified); + Assert.True(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.True(fieldCultureLicenseEntry.IsModified); + Assert.True(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.True(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.True(fieldCultureLicTogEntry.IsModified); + Assert.True(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(fieldCultureLicTagEntry.IsModified); + Assert.True(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + fieldCultureEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldCultureEntry.IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Species).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldCultureEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldCultureManufacturerEntry.IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldCultureManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldCultureManTogEntry.IsModified); + Assert.False(fieldCultureManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureManTagEntry.IsModified); + Assert.False(fieldCultureManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicenseEntry.IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldCultureLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldCultureLicTogEntry.IsModified); + Assert.False(fieldCultureLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldCultureLicTagEntry.IsModified); + Assert.False(fieldCultureLicTagEntry.Property(e => e.Text).IsModified); + + var fieldMilkEntry = entry.ComplexProperty(e => e.FieldMilk); + var fieldMilkManufacturerEntry = fieldMilkEntry.ComplexProperty(e => e.Manufacturer); + var fieldMilkLicenseEntry = fieldMilkEntry.ComplexProperty(e => e.License); + var fieldMilkManTogEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tog); + var fieldMilkManTagEntry = fieldMilkManufacturerEntry.ComplexProperty(e => e.Tag); + var fieldMilkLicTogEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tog); + var fieldMilkLicTagEntry = fieldMilkLicenseEntry.ComplexProperty(e => e.Tag); + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldMilkManufacturerEntry.IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicTagEntry.IsModified); + Assert.False(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkLicTagEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldMilkManufacturerEntry.IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.True(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(fieldMilkLicTagEntry.IsModified); + Assert.True(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkLicTagEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldMilkManufacturerEntry.IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicTagEntry.IsModified); + Assert.False(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkManufacturerEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.True(fieldMilkManufacturerEntry.IsModified); + Assert.True(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.True(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.True(fieldMilkManTogEntry.IsModified); + Assert.True(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.True(fieldMilkManTagEntry.IsModified); + Assert.True(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicTagEntry.IsModified); + Assert.False(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkManufacturerEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldMilkManufacturerEntry.IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicTagEntry.IsModified); + Assert.False(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkEntry.IsModified = true; + + Assert.Equal(EntityState.Modified, entry.State); + Assert.True(fieldMilkEntry.IsModified); + Assert.True(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.True(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.True(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.True(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.True(fieldMilkManufacturerEntry.IsModified); + Assert.True(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.True(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.True(fieldMilkManTogEntry.IsModified); + Assert.True(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.True(fieldMilkManTagEntry.IsModified); + Assert.True(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.True(fieldMilkLicenseEntry.IsModified); + Assert.True(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.True(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.True(fieldMilkLicTogEntry.IsModified); + Assert.True(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.True(fieldMilkLicTagEntry.IsModified); + Assert.True(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + + fieldMilkEntry.IsModified = false; + + Assert.Equal(EntityState.Unchanged, entry.State); + Assert.False(fieldMilkEntry.IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Species).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Subspecies).IsModified); + Assert.False(fieldMilkEntry.Property(e => e.Validation).IsModified); + Assert.False(fieldMilkManufacturerEntry.IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Name).IsModified); + Assert.False(fieldMilkManufacturerEntry.Property(e => e.Rating).IsModified); + Assert.False(fieldMilkManTogEntry.IsModified); + Assert.False(fieldMilkManTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkManTagEntry.IsModified); + Assert.False(fieldMilkManTagEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicenseEntry.IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Title).IsModified); + Assert.False(fieldMilkLicenseEntry.Property(e => e.Charge).IsModified); + Assert.False(fieldMilkLicTogEntry.IsModified); + Assert.False(fieldMilkLicTogEntry.Property(e => e.Text).IsModified); + Assert.False(fieldMilkLicTagEntry.IsModified); + Assert.False(fieldMilkLicTagEntry.Property(e => e.Text).IsModified); + } + private interface IWotty { int Id { get; set; } - string Primate { get; set; } + string? Primate { get; set; } string RequiredPrimate { get; set; } - string Marmate { get; set; } + string? Marmate { get; set; } } private class ObjectWotty : IWotty { - private object _id; - private object _primate; - private object _requiredPrimate; - private object _marmate; + private object? _id; + private object? _primate; + private object? _requiredPrimate; + private object? _marmate; public int Id { - get => (int)_id; + get => (int)_id!; set => _id = value; } - public string Primate + public string? Primate { - get => (string)_primate; + get => (string)_primate!; set => _primate = value; } public string RequiredPrimate { - get => (string)_requiredPrimate; + get => (string)_requiredPrimate!; set => _requiredPrimate = value; } - public string Marmate + public string? Marmate { - get => (string)_marmate; + get => (string)_marmate!; set => _marmate = value; } } @@ -1016,16 +4544,16 @@ public string Marmate private class Wotty : IWotty { public int Id { get; set; } - public string Primate { get; set; } - public string RequiredPrimate { get; set; } - public string Marmate { get; set; } + public string? Primate { get; set; } + public string RequiredPrimate { get; set; } = null!; + public string? Marmate { get; set; } } private class FullyNotifyingWotty : HasChangedAndChanging { private int _id; - private string _primate; - private string _concurrentprimate; + private string? _primate; + private string? _concurrentprimate; public int Id { @@ -1041,7 +4569,7 @@ public int Id } } - public string Primate + public string? Primate { get => _primate; set @@ -1055,7 +4583,7 @@ public string Primate } } - public string ConcurrentPrimate + public string? ConcurrentPrimate { get => _concurrentprimate; set @@ -1073,7 +4601,7 @@ public string ConcurrentPrimate private class NotifyingWotty : HasChanged { private int _id; - private string _primate; + private string? _primate; public int Id { @@ -1088,7 +4616,7 @@ public int Id } } - public string Primate + public string? Primate { get => _primate; set @@ -1104,7 +4632,7 @@ public string Primate private abstract class HasChanged : INotifyPropertyChanged { - public event PropertyChangedEventHandler PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); @@ -1112,7 +4640,7 @@ protected void OnPropertyChanged([CallerMemberName] string propertyName = "") private abstract class HasChangedAndChanging : HasChanged, INotifyPropertyChanging { - public event PropertyChangingEventHandler PropertyChanging; + public event PropertyChangingEventHandler? PropertyChanging; protected void OnPropertyChanging([CallerMemberName] string propertyName = "") => PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName)); @@ -1120,7 +4648,7 @@ protected void OnPropertyChanging([CallerMemberName] string propertyName = "") public static IModel BuildModel( ChangeTrackingStrategy fullNotificationStrategy = ChangeTrackingStrategy.ChangingAndChangedNotifications, - ModelBuilder builder = null, + ModelBuilder builder = null!, bool finalize = true) { builder ??= InMemoryTestHelpers.Instance.CreateConventionBuilder(); @@ -1154,6 +4682,96 @@ public static IModel BuildModel( return finalize ? builder.Model.FinalizeModel() : (IModel)builder.Model; } + private class Yogurt + { + public Guid Id { get; set; } + public Culture Culture { get; set; } + public Milk Milk { get; set; } = null!; + public FieldCulture FieldCulture; + public FieldMilk FieldMilk = null!; + } + + private class YogurtContext : DbContext + { + protected internal override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder + .UseInternalServiceProvider(InMemoryFixture.DefaultServiceProvider) + .UseInMemoryDatabase(GetType().FullName!); + + protected internal override void OnModelCreating(ModelBuilder modelBuilder) + => modelBuilder.Entity( + b => + { + b.ComplexProperty( + e => e.Culture, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + + b.ComplexProperty( + e => e.Milk, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + + b.ComplexProperty( + e => e.FieldCulture, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + + b.ComplexProperty( + e => e.FieldMilk, b => + { + b.ComplexProperty( + e => e.License, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + b.ComplexProperty( + e => e.Manufacturer, b => + { + b.ComplexProperty(e => e.Tag); + b.ComplexProperty(e => e.Tog); + }); + }); + }); + } + private class PrimateContext : DbContext { private readonly ChangeTrackingStrategy _fullNotificationStrategy; @@ -1166,9 +4784,101 @@ public PrimateContext(ChangeTrackingStrategy fullNotificationStrategy = ChangeTr protected internal override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder .UseInternalServiceProvider(InMemoryFixture.DefaultServiceProvider) - .UseInMemoryDatabase(GetType().FullName); + .UseInMemoryDatabase(GetType().FullName!); protected internal override void OnModelCreating(ModelBuilder modelBuilder) => BuildModel(_fullNotificationStrategy, modelBuilder, finalize: false); } + + private struct Culture + { + public string Species { get; set; } + public string? Subspecies { get; set; } + public int Rating { get; set; } + public bool? Validation { get; set; } + public Manufacturer Manufacturer { get; set; } + public License License { get; set; } + } + + private class Milk + { + public string Species { get; set; } = null!; + public string? Subspecies { get; set; } + public int Rating { get; set; } + public bool? Validation { get; set; } + public Manufacturer Manufacturer { get; set; } = null!; + public License License { get; set; } + } + + private class Manufacturer + { + public string? Name { get; set; } + public int Rating { get; set; } + public Tag Tag { get; set; } = null!; + public Tog Tog { get; set; } + } + + private struct License + { + public string Title { get; set; } + public decimal Charge { get; set; } + public Tag Tag { get; set; } + public Tog Tog { get; set; } + } + + private class Tag + { + public string? Text { get; set; } + } + + private struct Tog + { + public string? Text { get; set; } + } + + private struct FieldCulture + { + public string Species; + public string? Subspecies; + public int Rating; + public bool? Validation; + public FieldManufacturer Manufacturer; + public FieldLicense License; + } + + private class FieldMilk + { + public string Species = null!; + public string? Subspecies; + public int Rating; + public bool? Validation ; + public FieldManufacturer Manufacturer = null!; + public FieldLicense License; + } + + private class FieldManufacturer + { + public string? Name; + public int Rating; + public FieldTag Tag = null!; + public FieldTog Tog; + } + + private struct FieldLicense + { + public string Title; + public decimal Charge; + public FieldTag Tag; + public FieldTog Tog; + } + + private class FieldTag + { + public string? Text; + } + + private struct FieldTog + { + public string? Text; + } } diff --git a/test/EFCore.Tests/DbContextTest.cs b/test/EFCore.Tests/DbContextTest.cs index 27ca6518fdb..692e710b24e 100644 --- a/test/EFCore.Tests/DbContextTest.cs +++ b/test/EFCore.Tests/DbContextTest.cs @@ -174,7 +174,17 @@ public async Task SaveChangesAsync_with_canceled_token() new ServiceCollection().AddSingleton(loggerFactory)); using var context = new ButTheHedgehogContext(provider); - context.Products.Add(new Product()); + context.Products.Add( + new Product + { + Stamp = new() { Code = new Guid("984ade3c-2f7b-4651-a351-642e92ab7146") }, + Tag = new() + { + Name = "Tanavast", + Stamp = new() { Code = new Guid("984ade3c-2f7b-4651-a351-642e92ab7147") }, + Notes = new[] { "A", "B" } + } + }); await Assert.ThrowsAnyAsync(() => context.SaveChangesAsync(new CancellationToken(canceled: true))); @@ -562,20 +572,13 @@ public async Task SaveChanges_calls_DetectChanges_by_default(bool async) if (async) { - await context.SaveChangesAsync(); + Assert.Equal(1, await context.SaveChangesAsync()); } else { - context.SaveChanges(); + Assert.Equal(1, context.SaveChanges()); } } - - using (var context = new ButTheHedgehogContext(provider)) - { - // TODO: In-memory complex type support, #31464 - Assert.Throws(() => context.Products.Single().Name); - // Assert.Equal("Cracked Cookies", context.Products.Single().Name); - } } [ConditionalTheory] @@ -619,20 +622,13 @@ public async Task Auto_DetectChanges_for_SaveChanges_can_be_switched_off(bool as if (async) { - await context.SaveChangesAsync(); + Assert.Equal(0, await context.SaveChangesAsync()); } else { - context.SaveChanges(); + Assert.Equal(0, context.SaveChanges()); } } - - using (var context = new ButTheHedgehogContext(provider)) - { - // TODO: In-memory complex type support, #31464 - Assert.Throws(() => context.Products.Single().Name); - // Assert.Equal("Little Hedgehogs", context.Products.Single().Name); - } } private class ButTheHedgehogContext : DbContext diff --git a/test/EFCore.Tests/DbContextTrackingTest.cs b/test/EFCore.Tests/DbContextTrackingTest.cs index b2424175a3b..233b863b07b 100644 --- a/test/EFCore.Tests/DbContextTrackingTest.cs +++ b/test/EFCore.Tests/DbContextTrackingTest.cs @@ -2027,7 +2027,20 @@ public void Can_attach_with_inconsistent_FK_principal_first_collection_not_fixed { using var context = new EarlyLearningCenter(InMemoryTestHelpers.Instance.CreateServiceProvider()); var category7 = context.Attach( - new Category { Id = 7, Products = new List() }).Entity; + new Category { Id = 7, Products = new List(), + Stamp = new() + { + Code = new Guid("984ade3c-2f7b-4651-a351-642e92ab7146") + }, + Tag = new() + { + Name = "Tanavast", + Stamp = new() + { + Code = new Guid("984ade3c-2f7b-4651-a351-642e92ab7147") + }, + Notes = new[] { "A", "B" } + } }).Entity; var category = new Category { Id = 1, Name = "Beverages", Stamp = new() @@ -2093,7 +2106,23 @@ public void Can_attach_with_inconsistent_FK_dependent_first_collection_not_fixed { using var context = new EarlyLearningCenter(InMemoryTestHelpers.Instance.CreateServiceProvider()); var category7 = context.Attach( - new Category { Id = 7, Products = new List() }).Entity; + new Category + { + Id = 7, Products = new List(), + Stamp = new() + { + Code = new Guid("984ade3c-2f7b-4651-a351-642e92ab7146") + }, + Tag = new() + { + Name = "Tanavast", + Stamp = new() + { + Code = new Guid("984ade3c-2f7b-4651-a351-642e92ab7147") + }, + Notes = new[] { "A", "B" } + } + }).Entity; var category = new Category { Id = 1, Name = "Beverages", Stamp = new() @@ -2159,7 +2188,20 @@ public void Can_attach_with_inconsistent_FK_principal_first_reference_not_fixed_ { using var context = new EarlyLearningCenter(InMemoryTestHelpers.Instance.CreateServiceProvider()); var category7 = context.Attach( - new Category { Id = 7, Products = new List() }).Entity; + new Category { Id = 7, Products = new List(), + Stamp = new() + { + Code = new Guid("984ade3c-2f7b-4651-a351-642e92ab7146") + }, + Tag = new() + { + Name = "Tanavast", + Stamp = new() + { + Code = new Guid("984ade3c-2f7b-4651-a351-642e92ab7147") + }, + Notes = new[] { "A", "B" } + } }).Entity; var category = new Category { Id = 1, Name = "Beverages", Stamp = new()