diff --git a/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs b/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs index 28b29de9845..5b2276fca91 100644 --- a/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs +++ b/src/EFCore.Proxies/Proxies/Internal/PropertyChangedInterceptor.cs @@ -2,10 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; using System.ComponentModel; using Castle.DynamicProxy; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Internal; namespace Microsoft.EntityFrameworkCore.Proxies.Internal { @@ -22,7 +25,6 @@ public class PropertyChangedInterceptor : IInterceptor private readonly IEntityType _entityType; private readonly bool _checkEquality; private PropertyChangedEventHandler _handler; - private Type _proxyType; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -68,14 +70,18 @@ public virtual void Intercept(IInvocation invocation) var property = _entityType.FindProperty(propertyName); if (property != null) { - HandleChanged(invocation, propertyName); + var comparer = property.IsKeyOrForeignKey() + ? property.GetKeyValueComparer() + : property.GetValueComparer(); + + HandleChanged(invocation, property, comparer); } else { var navigation = _entityType.FindNavigation(propertyName); if (navigation != null) { - HandleChanged(invocation, propertyName); + HandleChanged(invocation, navigation, ReferenceEqualityComparer.Instance); } else { @@ -89,29 +95,19 @@ public virtual void Intercept(IInvocation invocation) } } - private void HandleChanged(IInvocation invocation, string propertyName) + private void HandleChanged(IInvocation invocation, IPropertyBase property, IEqualityComparer comparer) { var newValue = invocation.Arguments[^1]; if (_checkEquality) { - if (_proxyType == null) - { - _proxyType = invocation.Proxy.GetType(); - } + var oldValue = property.GetGetter().GetClrValue(invocation.Proxy); - var property = _proxyType.GetProperty(propertyName); - if (property != null) - { - var oldValue = property.GetValue(invocation.Proxy); - - invocation.Proceed(); + invocation.Proceed(); - if ((oldValue is null ^ newValue is null) - || oldValue?.Equals(newValue) == false) - { - NotifyPropertyChanged(propertyName, invocation.Proxy); - } + if (!(comparer?.Equals(oldValue, newValue) ?? Equals(oldValue, newValue))) + { + NotifyPropertyChanged(property.Name, invocation.Proxy); } else { @@ -121,14 +117,11 @@ private void HandleChanged(IInvocation invocation, string propertyName) else { invocation.Proceed(); - NotifyPropertyChanged(propertyName, invocation.Proxy); + NotifyPropertyChanged(property.Name, invocation.Proxy); } } private void NotifyPropertyChanged(string propertyName, object proxy) - { - var args = new PropertyChangedEventArgs(propertyName); - _handler?.Invoke(proxy, args); - } + => _handler?.Invoke(proxy, new PropertyChangedEventArgs(propertyName)); } } diff --git a/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs b/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs index b730c07fc5e..8fe134569ea 100644 --- a/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs +++ b/src/EFCore.Proxies/Proxies/Internal/PropertyChangingInterceptor.cs @@ -2,10 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; using System.ComponentModel; using Castle.DynamicProxy; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Internal; namespace Microsoft.EntityFrameworkCore.Proxies.Internal { @@ -22,7 +25,6 @@ public class PropertyChangingInterceptor : IInterceptor private readonly IEntityType _entityType; private readonly bool _checkEquality; private PropertyChangingEventHandler _handler; - private Type _proxyType; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -68,14 +70,18 @@ public virtual void Intercept(IInvocation invocation) var property = _entityType.FindProperty(propertyName); if (property != null) { - HandleChanging(invocation, propertyName); + var comparer = property.IsKeyOrForeignKey() + ? property.GetKeyValueComparer() + : property.GetValueComparer(); + + HandleChanging(invocation, property, comparer); } else { var navigation = _entityType.FindNavigation(propertyName); if (navigation != null) { - HandleChanging(invocation, propertyName); + HandleChanging(invocation, navigation, ReferenceEqualityComparer.Instance); } else { @@ -89,40 +95,27 @@ public virtual void Intercept(IInvocation invocation) } } - private void HandleChanging(IInvocation invocation, string propertyName) + private void HandleChanging(IInvocation invocation, IPropertyBase property, IEqualityComparer comparer) { if (_checkEquality) { - if (_proxyType == null) - { - _proxyType = invocation.Proxy.GetType(); - } + var oldValue = property.GetGetter().GetClrValue(invocation.Proxy); + var newValue = invocation.Arguments[^1]; - var property = _proxyType.GetProperty(propertyName); - if (property != null) + if (!(comparer?.Equals(oldValue, newValue) ?? Equals(oldValue, newValue))) { - var oldValue = property.GetValue(invocation.Proxy); - var newValue = invocation.Arguments[^1]; - - if ((oldValue is null ^ newValue is null) - || oldValue?.Equals(newValue) == false) - { - NotifyPropertyChanging(propertyName, invocation.Proxy); - } + NotifyPropertyChanging(property.Name, invocation.Proxy); } } else { - NotifyPropertyChanging(propertyName, invocation.Proxy); + NotifyPropertyChanging(property.Name, invocation.Proxy); } invocation.Proceed(); } private void NotifyPropertyChanging(string propertyName, object proxy) - { - var args = new PropertyChangingEventArgs(propertyName); - _handler?.Invoke(proxy, args); - } + => _handler?.Invoke(proxy, new PropertyChangingEventArgs(propertyName)); } } diff --git a/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs b/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs index 7851e679d7f..72a340cd39a 100644 --- a/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs +++ b/src/EFCore.Proxies/Proxies/Internal/ProxyFactory.cs @@ -56,7 +56,7 @@ public virtual object Create( entityType, context.GetService(), constructorArguments); - } + } return CreateProxy( options, @@ -171,7 +171,7 @@ private Type[] GetInterfacesToProxy( { interfacesToProxy.Add(_notifyPropertyChangedInterface); } - + break; case ChangeTrackingStrategy.ChangingAndChangedNotifications: case ChangeTrackingStrategy.ChangingAndChangedNotificationsWithOriginalValues: @@ -186,8 +186,6 @@ private Type[] GetInterfacesToProxy( interfacesToProxy.Add(_notifyPropertyChangingInterface); } - break; - default: break; } } @@ -232,9 +230,7 @@ private Castle.DynamicProxy.IInterceptor[] GetNotifyChangeInterceptors( { interceptors.Add(new PropertyChangingInterceptor(entityType, options.CheckEquality)); } - - break; - default: + break; } } diff --git a/src/EFCore.Proxies/ProxiesExtensions.cs b/src/EFCore.Proxies/ProxiesExtensions.cs index 65d868a503f..a67872f9f2d 100644 --- a/src/EFCore.Proxies/ProxiesExtensions.cs +++ b/src/EFCore.Proxies/ProxiesExtensions.cs @@ -159,7 +159,27 @@ public static object CreateProxy( public static TEntity CreateProxy( [NotNull] this DbContext context, [NotNull] params object[] constructorArguments) - => (TEntity)context.CreateProxy(typeof(TEntity), constructorArguments); + => CreateProxy(context, null, constructorArguments); + + /// + /// Creates a proxy instance for an entity type if proxy creation has been turned on. + /// + /// The entity type for which a proxy is needed. + /// The . + /// Called after the entity is created to set property values, etc. + /// Arguments to pass to the entity type constructor. + /// The proxy instance. + public static TEntity CreateProxy( + [NotNull] this DbContext context, + [CanBeNull] Action configureEntity, + [NotNull] params object[] constructorArguments) + { + var entity = (TEntity)context.CreateProxy(typeof(TEntity), constructorArguments); + + configureEntity?.Invoke(entity); + + return entity; + } /// /// Creates a proxy instance for an entity type if proxy creation has been turned on. @@ -172,13 +192,31 @@ public static TEntity CreateProxy( [NotNull] this DbSet set, [NotNull] params object[] constructorArguments) where TEntity : class + => CreateProxy(set, null, constructorArguments); + + /// + /// Creates a proxy instance for an entity type if proxy creation has been turned on. + /// + /// The entity type for which a proxy is needed. + /// The . + /// Called after the entity is created to set property values, etc. + /// Arguments to pass to the entity type constructor. + /// The proxy instance. + public static TEntity CreateProxy( + [NotNull] this DbSet set, + [CanBeNull] Action configureEntity, + [NotNull] params object[] constructorArguments) + where TEntity : class { Check.NotNull(set, nameof(set)); Check.NotNull(constructorArguments, nameof(constructorArguments)); - return (TEntity)set.GetInfrastructure().CreateProxy(typeof(TEntity), constructorArguments); - } + var entity = (TEntity)set.GetInfrastructure().CreateProxy(typeof(TEntity), constructorArguments); + + configureEntity?.Invoke(entity); + return entity; + } private static object CreateProxy( this IServiceProvider serviceProvider, Type entityType, diff --git a/src/EFCore/Internal/ReferenceEqualityComparer.cs b/src/EFCore/Internal/ReferenceEqualityComparer.cs index 2b416d0601a..507a0dfe516 100644 --- a/src/EFCore/Internal/ReferenceEqualityComparer.cs +++ b/src/EFCore/Internal/ReferenceEqualityComparer.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; @@ -13,7 +14,7 @@ namespace Microsoft.EntityFrameworkCore.Internal /// doing so can result in application failures when updating to a new Entity Framework Core release. /// // Sealed for perf - public sealed class ReferenceEqualityComparer : IEqualityComparer + public sealed class ReferenceEqualityComparer : IEqualityComparer, IEqualityComparer { private ReferenceEqualityComparer() { @@ -27,8 +28,36 @@ private ReferenceEqualityComparer() /// public static ReferenceEqualityComparer Instance { get; } = new ReferenceEqualityComparer(); + /// + /// 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. + /// bool IEqualityComparer.Equals(object x, object y) => ReferenceEquals(x, y); + /// + /// 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. + /// + bool IEqualityComparer.Equals(object x, object y) => ReferenceEquals(x, y); + + /// + /// 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. + /// + int IEqualityComparer.GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj); + + /// + /// 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. + /// int IEqualityComparer.GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj); } } diff --git a/test/EFCore.InMemory.FunctionalTests/ProxyGraphUpdatesInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/ProxyGraphUpdatesInMemoryTest.cs index 159d3f98250..49caa202885 100644 --- a/test/EFCore.InMemory.FunctionalTests/ProxyGraphUpdatesInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/ProxyGraphUpdatesInMemoryTest.cs @@ -13,7 +13,7 @@ namespace Microsoft.EntityFrameworkCore public class ProxyGraphUpdatesInMemoryTest { public abstract class ProxyGraphUpdatesInMemoryTestBase : ProxyGraphUpdatesTestBase - where TFixture : ProxyGraphUpdatesInMemoryTestBase.ProxyGraphUpdatesSqliteFixtureBase, new() + where TFixture : ProxyGraphUpdatesInMemoryTestBase.ProxyGraphUpdatesInMemoryFixtureBase, new() { protected ProxyGraphUpdatesInMemoryTestBase(TFixture fixture) : base(fixture) @@ -105,7 +105,7 @@ protected override void ExecuteWithStrategyInTransaction( Fixture.Reseed(); } - public abstract class ProxyGraphUpdatesSqliteFixtureBase : ProxyGraphUpdatesFixtureBase + public abstract class ProxyGraphUpdatesInMemoryFixtureBase : ProxyGraphUpdatesFixtureBase { protected override ITestStoreFactory TestStoreFactory => InMemoryTestStoreFactory.Instance; @@ -121,7 +121,10 @@ public LazyLoading(ProxyGraphUpdatesWithLazyLoadingInMemoryFixture fixture) { } - public class ProxyGraphUpdatesWithLazyLoadingInMemoryFixture : ProxyGraphUpdatesSqliteFixtureBase + protected override bool DoesLazyLoading => true; + protected override bool DoesChangeTracking => false; + + public class ProxyGraphUpdatesWithLazyLoadingInMemoryFixture : ProxyGraphUpdatesInMemoryFixtureBase { protected override string StoreName { get; } = "ProxyGraphLazyLoadingUpdatesTest"; @@ -132,5 +135,52 @@ protected override IServiceCollection AddServices(IServiceCollection serviceColl => base.AddServices(serviceCollection.AddEntityFrameworkProxies()); } } + + public class ChangeTracking : ProxyGraphUpdatesInMemoryTestBase + { + public ChangeTracking(ProxyGraphUpdatesWithChangeTrackingInMemoryFixture fixture) + : base(fixture) + { + } + + protected override bool DoesLazyLoading => false; + protected override bool DoesChangeTracking => true; + + public class ProxyGraphUpdatesWithChangeTrackingInMemoryFixture : ProxyGraphUpdatesInMemoryFixtureBase + { + protected override string StoreName { get; } = "ProxyGraphChangeTrackingUpdatesTest"; + + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder.UseChangeDetectionProxies()); + + protected override IServiceCollection AddServices(IServiceCollection serviceCollection) + => base.AddServices(serviceCollection.AddEntityFrameworkProxies()); + } + } + + public class LazyLoadingAndChangeTracking : ProxyGraphUpdatesInMemoryTestBase + { + public LazyLoadingAndChangeTracking(ProxyGraphUpdatesWithChangeTrackingInMemoryFixture fixture) + : base(fixture) + { + } + + protected override bool DoesLazyLoading => true; + protected override bool DoesChangeTracking => true; + + public class ProxyGraphUpdatesWithChangeTrackingInMemoryFixture : ProxyGraphUpdatesInMemoryFixtureBase + { + protected override string StoreName { get; } = "ProxyGraphLazyLoadingAndChangeTrackingUpdatesTest"; + + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions( + builder + .UseChangeDetectionProxies() + .UseLazyLoadingProxies()); + + protected override IServiceCollection AddServices(IServiceCollection serviceCollection) + => base.AddServices(serviceCollection.AddEntityFrameworkProxies()); + } + } } } diff --git a/test/EFCore.Specification.Tests/ProxyGraphUpdatesFixtureBase.cs b/test/EFCore.Specification.Tests/ProxyGraphUpdatesFixtureBase.cs index 96bb9b031f7..199fb2a8e15 100644 --- a/test/EFCore.Specification.Tests/ProxyGraphUpdatesFixtureBase.cs +++ b/test/EFCore.Specification.Tests/ProxyGraphUpdatesFixtureBase.cs @@ -15,10 +15,11 @@ namespace Microsoft.EntityFrameworkCore { public abstract partial class ProxyGraphUpdatesTestBase { + protected abstract bool DoesLazyLoading { get; } + protected abstract bool DoesChangeTracking { get; } + public abstract class ProxyGraphUpdatesFixtureBase : SharedStoreFixtureBase { - protected override string StoreName { get; } = "ProxyGraphUpdatesTest"; - public readonly Guid RootAK = Guid.NewGuid(); protected override bool UsePooling => false; @@ -342,197 +343,249 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con modelBuilder.Entity(); } - protected virtual object CreateFullGraph() - => new Root - { - AlternateId = RootAK, - RequiredChildren = - new ObservableHashSet(ReferenceEqualityComparer.Instance) + protected virtual object CreateFullGraph(DbContext context) + => context.CreateProxy( + e => + { + e.AlternateId = RootAK; + + e.RequiredChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) { - new Required1 - { - Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + context.CreateProxy( + e => { - new Required2(), new Required2() - } - }, - new Required1 - { - Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + e.Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.Set().CreateProxy(), context.Set().CreateProxy() + }; + }), + context.CreateProxy( + e => { - new Required2(), new Required2() - } - } - }, - OptionalChildren = - new ObservableHashSet(ReferenceEqualityComparer.Instance) + e.Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.Set().CreateProxy(), context.Set().CreateProxy() + }; + }) + }; + + e.OptionalChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) { - new Optional1 - { - Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + context.Set().CreateProxy( + e => { - new Optional2(), new Optional2() - }, - CompositeChildren = - new ObservableHashSet(ReferenceEqualityComparer.Instance) - }, - new Optional1 - { - Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + e.Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.Set().CreateProxy(), context.Set().CreateProxy() + }; + + e.CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance); + }), + context.Set().CreateProxy( + e => { - new Optional2(), new Optional2() - }, - CompositeChildren = - new ObservableHashSet(ReferenceEqualityComparer.Instance) - } - }, - RequiredSingle = new RequiredSingle1 { Single = new RequiredSingle2() }, - OptionalSingle = new OptionalSingle1 { Single = new OptionalSingle2() }, - OptionalSingleDerived = new OptionalSingle1Derived { Single = new OptionalSingle2Derived() }, - OptionalSingleMoreDerived = new OptionalSingle1MoreDerived { Single = new OptionalSingle2MoreDerived() }, - RequiredNonPkSingle = new RequiredNonPkSingle1 { Single = new RequiredNonPkSingle2() }, - RequiredNonPkSingleDerived = - new RequiredNonPkSingle1Derived { Single = new RequiredNonPkSingle2Derived(), Root = new Root() }, - RequiredNonPkSingleMoreDerived = - new RequiredNonPkSingle1MoreDerived - { - Single = new RequiredNonPkSingle2MoreDerived(), - Root = new Root(), - DerivedRoot = new Root() - }, - RequiredChildrenAk = - new ObservableHashSet(ReferenceEqualityComparer.Instance) - { - new RequiredAk1 + e.Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.Set().CreateProxy(), context.Set().CreateProxy() + }; + + e.CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance); + }) + }; + + e.RequiredSingle = context.CreateProxy( + e => e.Single = context.Set().CreateProxy()); + + e.OptionalSingle = context.CreateProxy( + e => e.Single = context.Set().CreateProxy()); + + e.OptionalSingleDerived = context.CreateProxy( + e => e.Single = context.Set().CreateProxy()); + + e.OptionalSingleMoreDerived = context.CreateProxy( + e => e.Single = context.Set().CreateProxy()); + + e.RequiredNonPkSingle = context.CreateProxy( + e => e.Single = context.Set().CreateProxy()); + + e.RequiredNonPkSingleDerived = context.CreateProxy( + e => + { + e.Single = context.Set().CreateProxy(); + e.Root = context.Set().CreateProxy(); + }); + + e.RequiredNonPkSingleMoreDerived = context.CreateProxy( + e => { - AlternateId = Guid.NewGuid(), - Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + e.Single = context.Set().CreateProxy(); + e.Root = context.Set().CreateProxy(); + e.DerivedRoot = context.Set().CreateProxy(); + }); + + e.RequiredChildrenAk = new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.Set().CreateProxy( + e => { - new RequiredAk2 { AlternateId = Guid.NewGuid() }, new RequiredAk2 { AlternateId = Guid.NewGuid() } - }, - CompositeChildren = - new ObservableHashSet(ReferenceEqualityComparer.Instance) + e.AlternateId = Guid.NewGuid(); + + e.Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) { - new RequiredComposite2(), new RequiredComposite2() - } - }, - new RequiredAk1 - { - AlternateId = Guid.NewGuid(), - Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + context.Set().CreateProxy(e => e.AlternateId = Guid.NewGuid()), + context.Set().CreateProxy(e => e.AlternateId = Guid.NewGuid()) + }; + + e.CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.Set().CreateProxy(), context.Set().CreateProxy() + }; + }), + context.Set().CreateProxy( + e => { - new RequiredAk2 { AlternateId = Guid.NewGuid() }, new RequiredAk2 { AlternateId = Guid.NewGuid() } - }, - CompositeChildren = - new ObservableHashSet(ReferenceEqualityComparer.Instance) + e.AlternateId = Guid.NewGuid(); + + e.Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) { - new RequiredComposite2(), new RequiredComposite2() - } - } - }, - OptionalChildrenAk = - new ObservableHashSet(ReferenceEqualityComparer.Instance) + context.Set().CreateProxy(e => e.AlternateId = Guid.NewGuid()), + context.Set().CreateProxy(e => e.AlternateId = Guid.NewGuid()) + }; + + e.CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.Set().CreateProxy(), context.Set().CreateProxy() + }; + }) + }; + + e.OptionalChildrenAk = new ObservableHashSet(ReferenceEqualityComparer.Instance) { - new OptionalAk1 - { - AlternateId = Guid.NewGuid(), - Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + context.Set().CreateProxy( + e => { - new OptionalAk2 { AlternateId = Guid.NewGuid() }, new OptionalAk2 { AlternateId = Guid.NewGuid() } - }, - CompositeChildren = - new ObservableHashSet(ReferenceEqualityComparer.Instance) + e.AlternateId = Guid.NewGuid(); + + e.Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) { - new OptionalComposite2(), new OptionalComposite2() - } - }, - new OptionalAk1 - { - AlternateId = Guid.NewGuid(), - Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) + context.Set().CreateProxy(e => e.AlternateId = Guid.NewGuid()), + context.Set().CreateProxy(e => e.AlternateId = Guid.NewGuid()) + }; + e.CompositeChildren = + new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.Set().CreateProxy(), + context.Set().CreateProxy() + }; + }), + context.Set().CreateProxy( + e => { - new OptionalAk2 { AlternateId = Guid.NewGuid() }, new OptionalAk2 { AlternateId = Guid.NewGuid() } - }, - CompositeChildren = - new ObservableHashSet(ReferenceEqualityComparer.Instance) + e.AlternateId = Guid.NewGuid(); + + e.Children = new ObservableHashSet(ReferenceEqualityComparer.Instance) { - new OptionalComposite2(), new OptionalComposite2() - } - } - }, - RequiredSingleAk = - new RequiredSingleAk1 - { - AlternateId = Guid.NewGuid(), - Single = new RequiredSingleAk2 { AlternateId = Guid.NewGuid() }, - SingleComposite = new RequiredSingleComposite2() - }, - OptionalSingleAk = - new OptionalSingleAk1 - { - AlternateId = Guid.NewGuid(), - Single = new OptionalSingleAk2 { AlternateId = Guid.NewGuid() }, - SingleComposite = new OptionalSingleComposite2() - }, - OptionalSingleAkDerived = - new OptionalSingleAk1Derived - { - AlternateId = Guid.NewGuid(), Single = new OptionalSingleAk2Derived { AlternateId = Guid.NewGuid() } - }, - OptionalSingleAkMoreDerived = - new OptionalSingleAk1MoreDerived - { - AlternateId = Guid.NewGuid(), Single = new OptionalSingleAk2MoreDerived { AlternateId = Guid.NewGuid() } - }, - RequiredNonPkSingleAk = - new RequiredNonPkSingleAk1 - { - AlternateId = Guid.NewGuid(), Single = new RequiredNonPkSingleAk2 { AlternateId = Guid.NewGuid() } - }, - RequiredNonPkSingleAkDerived = - new RequiredNonPkSingleAk1Derived - { - AlternateId = Guid.NewGuid(), - Single = new RequiredNonPkSingleAk2Derived { AlternateId = Guid.NewGuid() }, - Root = new Root() - }, - RequiredNonPkSingleAkMoreDerived = - new RequiredNonPkSingleAk1MoreDerived - { - AlternateId = Guid.NewGuid(), - Single = new RequiredNonPkSingleAk2MoreDerived { AlternateId = Guid.NewGuid() }, - Root = new Root(), - DerivedRoot = new Root() - }, - RequiredCompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) - { - new RequiredComposite1 - { - Id = 1, - CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) + context.Set().CreateProxy(e => e.AlternateId = Guid.NewGuid()), + context.Set().CreateProxy(e => e.AlternateId = Guid.NewGuid()) + }; + + e.CompositeChildren = + new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.Set().CreateProxy(), + context.Set().CreateProxy() + }; + }) + }; + + e.RequiredSingleAk = context.CreateProxy( + e => { - new OptionalOverlapping2 { Id = 1 }, new OptionalOverlapping2 { Id = 2 } - } - }, - new RequiredComposite1 - { - Id = 2, - CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) + e.AlternateId = Guid.NewGuid(); + e.Single = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + e.SingleComposite = context.CreateProxy(); + }); + + e.OptionalSingleAk = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + e.SingleComposite = context.CreateProxy(); + }); + + e.OptionalSingleAkDerived = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + }); + + e.OptionalSingleAkMoreDerived = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + }); + + e.RequiredNonPkSingleAk = context.CreateProxy( + e => { - new OptionalOverlapping2 { Id = 3 }, new OptionalOverlapping2 { Id = 4 } - } - } - } - }; + e.AlternateId = Guid.NewGuid(); + e.Single = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + }); + + e.RequiredNonPkSingleAkDerived = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + e.Root = context.CreateProxy(); + }); + + e.RequiredNonPkSingleAkMoreDerived = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + e.Root = context.CreateProxy(); + e.DerivedRoot = context.CreateProxy(); + }); + + e.RequiredCompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.Set().CreateProxy( + e => + { + e.Id = 1; + + e.CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.CreateProxy(e => e.Id = 1), + context.CreateProxy(e => e.Id = 2) + }; + }), + context.Set().CreateProxy( + e => + { + e.Id = 2; + + e.CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.CreateProxy(e => e.Id = 3), + context.CreateProxy(e => e.Id = 4) + }; + }) + }; + }); protected override void Seed(DbContext context) { var tracker = new KeyValueEntityTracker(); - context.ChangeTracker.TrackGraph(CreateFullGraph(), e => tracker.TrackEntity(e.Entry)); + context.ChangeTracker.TrackGraph(CreateFullGraph(context), e => tracker.TrackEntity(e.Entry)); - context.Add( - new BadOrder { BadCustomer = new BadCustomer() }); + context.Add(context.CreateProxy(e => e.BadCustomer = context.CreateProxy())); context.SaveChanges(); } @@ -567,6 +620,10 @@ protected Root LoadRoot(DbContext context) public class Root { + protected Root() + { + } + public virtual int Id { get; set; } public virtual Guid AlternateId { get; set; } @@ -625,6 +682,10 @@ public override bool Equals(object obj) public class Required1 { + protected Required1() + { + } + public virtual int Id { get; set; } public virtual int ParentId { get; set; } @@ -645,6 +706,10 @@ public override bool Equals(object obj) public class Required1Derived : Required1 { + protected Required1Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as Required1Derived); public override int GetHashCode() => base.GetHashCode(); @@ -652,6 +717,10 @@ public class Required1Derived : Required1 public class Required1MoreDerived : Required1Derived { + protected Required1MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as Required1MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -659,6 +728,10 @@ public class Required1MoreDerived : Required1Derived public class Required2 { + protected Required2() + { + } + public virtual int Id { get; set; } public virtual int ParentId { get; set; } @@ -676,6 +749,10 @@ public override bool Equals(object obj) public class Required2Derived : Required2 { + protected Required2Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as Required2Derived); public override int GetHashCode() => base.GetHashCode(); @@ -683,6 +760,10 @@ public class Required2Derived : Required2 public class Required2MoreDerived : Required2Derived { + protected Required2MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as Required2MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -690,6 +771,10 @@ public class Required2MoreDerived : Required2Derived public class Optional1 { + protected Optional1() + { + } + public virtual int Id { get; set; } public virtual int? ParentId { get; set; } @@ -713,6 +798,10 @@ public override bool Equals(object obj) public class Optional1Derived : Optional1 { + protected Optional1Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as Optional1Derived); public override int GetHashCode() => base.GetHashCode(); @@ -720,6 +809,10 @@ public class Optional1Derived : Optional1 public class Optional1MoreDerived : Optional1Derived { + protected Optional1MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as Optional1MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -727,6 +820,10 @@ public class Optional1MoreDerived : Optional1Derived public class Optional2 { + protected Optional2() + { + } + public virtual int Id { get; set; } public virtual int? ParentId { get; set; } @@ -744,6 +841,10 @@ public override bool Equals(object obj) public class Optional2Derived : Optional2 { + protected Optional2Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as Optional2Derived); public override int GetHashCode() => base.GetHashCode(); @@ -751,6 +852,10 @@ public class Optional2Derived : Optional2 public class Optional2MoreDerived : Optional2Derived { + protected Optional2MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as Optional2MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -758,6 +863,10 @@ public class Optional2MoreDerived : Optional2Derived public class RequiredSingle1 { + protected RequiredSingle1() + { + } + public virtual int Id { get; set; } public virtual Root Root { get; set; } @@ -775,6 +884,10 @@ public override bool Equals(object obj) public class RequiredSingle2 { + protected RequiredSingle2() + { + } + public virtual int Id { get; set; } public virtual RequiredSingle1 Back { get; set; } @@ -790,6 +903,10 @@ public override bool Equals(object obj) public class RequiredNonPkSingle1 { + protected RequiredNonPkSingle1() + { + } + public virtual int Id { get; set; } public virtual int RootId { get; set; } @@ -809,6 +926,10 @@ public override bool Equals(object obj) public class RequiredNonPkSingle1Derived : RequiredNonPkSingle1 { + protected RequiredNonPkSingle1Derived() + { + } + public virtual int DerivedRootId { get; set; } public virtual Root DerivedRoot { get; set; } @@ -820,6 +941,10 @@ public class RequiredNonPkSingle1Derived : RequiredNonPkSingle1 public class RequiredNonPkSingle1MoreDerived : RequiredNonPkSingle1Derived { + protected RequiredNonPkSingle1MoreDerived() + { + } + public virtual int MoreDerivedRootId { get; set; } public virtual Root MoreDerivedRoot { get; set; } @@ -831,6 +956,10 @@ public class RequiredNonPkSingle1MoreDerived : RequiredNonPkSingle1Derived public class RequiredNonPkSingle2 { + protected RequiredNonPkSingle2() + { + } + public virtual int Id { get; set; } public virtual int BackId { get; set; } @@ -848,6 +977,10 @@ public override bool Equals(object obj) public class RequiredNonPkSingle2Derived : RequiredNonPkSingle2 { + protected RequiredNonPkSingle2Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as RequiredNonPkSingle2Derived); public override int GetHashCode() => base.GetHashCode(); @@ -855,6 +988,10 @@ public class RequiredNonPkSingle2Derived : RequiredNonPkSingle2 public class RequiredNonPkSingle2MoreDerived : RequiredNonPkSingle2Derived { + protected RequiredNonPkSingle2MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as RequiredNonPkSingle2MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -862,6 +999,10 @@ public class RequiredNonPkSingle2MoreDerived : RequiredNonPkSingle2Derived public class OptionalSingle1 { + protected OptionalSingle1() + { + } + public virtual int Id { get; set; } public virtual int? RootId { get; set; } @@ -881,6 +1022,10 @@ public override bool Equals(object obj) public class OptionalSingle1Derived : OptionalSingle1 { + protected OptionalSingle1Derived() + { + } + public virtual int? DerivedRootId { get; set; } public virtual Root DerivedRoot { get; set; } @@ -892,6 +1037,10 @@ public class OptionalSingle1Derived : OptionalSingle1 public class OptionalSingle1MoreDerived : OptionalSingle1Derived { + protected OptionalSingle1MoreDerived() + { + } + public virtual int? MoreDerivedRootId { get; set; } public virtual Root MoreDerivedRoot { get; set; } @@ -903,6 +1052,10 @@ public class OptionalSingle1MoreDerived : OptionalSingle1Derived public class OptionalSingle2 { + protected OptionalSingle2() + { + } + public virtual int Id { get; set; } public virtual int? BackId { get; set; } @@ -920,6 +1073,10 @@ public override bool Equals(object obj) public class OptionalSingle2Derived : OptionalSingle2 { + protected OptionalSingle2Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as OptionalSingle2Derived); public override int GetHashCode() => base.GetHashCode(); @@ -927,6 +1084,10 @@ public class OptionalSingle2Derived : OptionalSingle2 public class OptionalSingle2MoreDerived : OptionalSingle2Derived { + protected OptionalSingle2MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as OptionalSingle2MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -934,6 +1095,10 @@ public class OptionalSingle2MoreDerived : OptionalSingle2Derived public class RequiredAk1 { + protected RequiredAk1() + { + } + public virtual int Id { get; set; } public virtual Guid AlternateId { get; set; } @@ -959,6 +1124,10 @@ public override bool Equals(object obj) public class RequiredAk1Derived : RequiredAk1 { + protected RequiredAk1Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as RequiredAk1Derived); public override int GetHashCode() => base.GetHashCode(); @@ -966,6 +1135,10 @@ public class RequiredAk1Derived : RequiredAk1 public class RequiredAk1MoreDerived : RequiredAk1Derived { + protected RequiredAk1MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as RequiredAk1MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -973,6 +1146,10 @@ public class RequiredAk1MoreDerived : RequiredAk1Derived public class RequiredAk2 { + protected RequiredAk2() + { + } + public virtual int Id { get; set; } public virtual Guid AlternateId { get; set; } @@ -992,6 +1169,10 @@ public override bool Equals(object obj) public class RequiredComposite1 { + protected RequiredComposite1() + { + } + public virtual int Id { get; set; } public virtual Guid ParentAlternateId { get; set; } @@ -1012,6 +1193,10 @@ public override bool Equals(object obj) public class OptionalOverlapping2 { + protected OptionalOverlapping2() + { + } + public virtual int Id { get; set; } public virtual Guid ParentAlternateId { get; set; } @@ -1033,6 +1218,10 @@ public override bool Equals(object obj) public class RequiredComposite2 { + protected RequiredComposite2() + { + } + public virtual int Id { get; set; } public virtual Guid ParentAlternateId { get; set; } @@ -1052,6 +1241,10 @@ public override bool Equals(object obj) public class RequiredAk2Derived : RequiredAk2 { + protected RequiredAk2Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as RequiredAk2Derived); public override int GetHashCode() => base.GetHashCode(); @@ -1059,6 +1252,10 @@ public class RequiredAk2Derived : RequiredAk2 public class RequiredAk2MoreDerived : RequiredAk2Derived { + protected RequiredAk2MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as RequiredAk2MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -1066,6 +1263,10 @@ public class RequiredAk2MoreDerived : RequiredAk2Derived public class OptionalAk1 { + protected OptionalAk1() + { + } + public virtual int Id { get; set; } public virtual Guid AlternateId { get; set; } @@ -1091,6 +1292,10 @@ public override bool Equals(object obj) public class OptionalAk1Derived : OptionalAk1 { + protected OptionalAk1Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as OptionalAk1Derived); public override int GetHashCode() => base.GetHashCode(); @@ -1098,6 +1303,10 @@ public class OptionalAk1Derived : OptionalAk1 public class OptionalAk1MoreDerived : OptionalAk1Derived { + protected OptionalAk1MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as OptionalAk1MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -1105,6 +1314,10 @@ public class OptionalAk1MoreDerived : OptionalAk1Derived public class OptionalAk2 { + protected OptionalAk2() + { + } + public virtual int Id { get; set; } public virtual Guid AlternateId { get; set; } @@ -1124,6 +1337,10 @@ public override bool Equals(object obj) public class OptionalComposite2 { + protected OptionalComposite2() + { + } + public virtual int Id { get; set; } public virtual Guid ParentAlternateId { get; set; } @@ -1147,6 +1364,10 @@ public override bool Equals(object obj) public class OptionalAk2Derived : OptionalAk2 { + protected OptionalAk2Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as OptionalAk2Derived); public override int GetHashCode() => base.GetHashCode(); @@ -1154,6 +1375,10 @@ public class OptionalAk2Derived : OptionalAk2 public class OptionalAk2MoreDerived : OptionalAk2Derived { + protected OptionalAk2MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as OptionalAk2MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -1161,6 +1386,10 @@ public class OptionalAk2MoreDerived : OptionalAk2Derived public class RequiredSingleAk1 { + protected RequiredSingleAk1() + { + } + public virtual int Id { get; set; } public virtual Guid AlternateId { get; set; } @@ -1184,6 +1413,10 @@ public override bool Equals(object obj) public class RequiredSingleAk2 { + protected RequiredSingleAk2() + { + } + public virtual int Id { get; set; } public virtual Guid AlternateId { get; set; } @@ -1203,6 +1436,10 @@ public override bool Equals(object obj) public class RequiredSingleComposite2 { + protected RequiredSingleComposite2() + { + } + public virtual int Id { get; set; } public virtual Guid BackAlternateId { get; set; } @@ -1222,6 +1459,10 @@ public override bool Equals(object obj) public class RequiredNonPkSingleAk1 { + protected RequiredNonPkSingleAk1() + { + } + public virtual int Id { get; set; } public virtual Guid AlternateId { get; set; } @@ -1243,6 +1484,10 @@ public override bool Equals(object obj) public class RequiredNonPkSingleAk1Derived : RequiredNonPkSingleAk1 { + protected RequiredNonPkSingleAk1Derived() + { + } + public virtual Guid DerivedRootId { get; set; } public virtual Root DerivedRoot { get; set; } @@ -1254,6 +1499,10 @@ public class RequiredNonPkSingleAk1Derived : RequiredNonPkSingleAk1 public class RequiredNonPkSingleAk1MoreDerived : RequiredNonPkSingleAk1Derived { + protected RequiredNonPkSingleAk1MoreDerived() + { + } + public virtual Guid MoreDerivedRootId { get; set; } public virtual Root MoreDerivedRoot { get; set; } @@ -1265,6 +1514,10 @@ public class RequiredNonPkSingleAk1MoreDerived : RequiredNonPkSingleAk1Derived public class RequiredNonPkSingleAk2 { + protected RequiredNonPkSingleAk2() + { + } + public virtual int Id { get; set; } public virtual Guid AlternateId { get; set; } @@ -1284,6 +1537,10 @@ public override bool Equals(object obj) public class RequiredNonPkSingleAk2Derived : RequiredNonPkSingleAk2 { + protected RequiredNonPkSingleAk2Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as RequiredNonPkSingleAk2Derived); public override int GetHashCode() => base.GetHashCode(); @@ -1291,6 +1548,10 @@ public class RequiredNonPkSingleAk2Derived : RequiredNonPkSingleAk2 public class RequiredNonPkSingleAk2MoreDerived : RequiredNonPkSingleAk2Derived { + protected RequiredNonPkSingleAk2MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as RequiredNonPkSingleAk2MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -1298,6 +1559,10 @@ public class RequiredNonPkSingleAk2MoreDerived : RequiredNonPkSingleAk2Derived public class OptionalSingleAk1 { + protected OptionalSingleAk1() + { + } + public virtual int Id { get; set; } public virtual Guid AlternateId { get; set; } @@ -1321,6 +1586,10 @@ public override bool Equals(object obj) public class OptionalSingleAk1Derived : OptionalSingleAk1 { + protected OptionalSingleAk1Derived() + { + } + public virtual Guid? DerivedRootId { get; set; } public virtual Root DerivedRoot { get; set; } @@ -1332,6 +1601,10 @@ public class OptionalSingleAk1Derived : OptionalSingleAk1 public class OptionalSingleAk1MoreDerived : OptionalSingleAk1Derived { + protected OptionalSingleAk1MoreDerived() + { + } + public virtual Guid? MoreDerivedRootId { get; set; } public virtual Root MoreDerivedRoot { get; set; } @@ -1343,6 +1616,10 @@ public class OptionalSingleAk1MoreDerived : OptionalSingleAk1Derived public class OptionalSingleAk2 { + protected OptionalSingleAk2() + { + } + public virtual int Id { get; set; } public virtual Guid AlternateId { get; set; } @@ -1362,6 +1639,10 @@ public override bool Equals(object obj) public class OptionalSingleComposite2 { + protected OptionalSingleComposite2() + { + } + public virtual int Id { get; set; } public virtual Guid ParentAlternateId { get; set; } @@ -1381,6 +1662,10 @@ public override bool Equals(object obj) public class OptionalSingleAk2Derived : OptionalSingleAk2 { + protected OptionalSingleAk2Derived() + { + } + public override bool Equals(object obj) => base.Equals(obj as OptionalSingleAk2Derived); public override int GetHashCode() => base.GetHashCode(); @@ -1388,6 +1673,10 @@ public class OptionalSingleAk2Derived : OptionalSingleAk2 public class OptionalSingleAk2MoreDerived : OptionalSingleAk2Derived { + protected OptionalSingleAk2MoreDerived() + { + } + public override bool Equals(object obj) => base.Equals(obj as OptionalSingleAk2MoreDerived); public override int GetHashCode() => base.GetHashCode(); @@ -1395,6 +1684,10 @@ public class OptionalSingleAk2MoreDerived : OptionalSingleAk2Derived public class BadCustomer { + protected BadCustomer() + { + } + public virtual int Id { get; set; } public virtual int Status { get; set; } @@ -1405,6 +1698,10 @@ public class BadCustomer public class BadOrder { + protected BadOrder() + { + } + public virtual int Id { get; set; } public virtual int? BadCustomerId { get; set; } diff --git a/test/EFCore.Specification.Tests/ProxyGraphUpdatesTestBase.cs b/test/EFCore.Specification.Tests/ProxyGraphUpdatesTestBase.cs index 747347bbe94..95dc46b8c7c 100644 --- a/test/EFCore.Specification.Tests/ProxyGraphUpdatesTestBase.cs +++ b/test/EFCore.Specification.Tests/ProxyGraphUpdatesTestBase.cs @@ -30,7 +30,7 @@ public virtual void Optional_one_to_one_relationships_are_one_to_one() { var root = context.Set().Single(IsTheRoot); - root.OptionalSingle = new OptionalSingle1(); + root.OptionalSingle = context.CreateProxy(); Assert.Throws(() => context.SaveChanges()); }); @@ -44,7 +44,7 @@ public virtual void Required_one_to_one_relationships_are_one_to_one() { var root = context.Set().Single(IsTheRoot); - root.RequiredSingle = new RequiredSingle1(); + root.RequiredSingle = context.CreateProxy(); Assert.Throws(() => context.SaveChanges()); }); @@ -58,7 +58,7 @@ public virtual void Optional_one_to_one_with_AK_relationships_are_one_to_one() { var root = context.Set().Single(IsTheRoot); - root.OptionalSingleAk = new OptionalSingleAk1(); + root.OptionalSingleAk = context.CreateProxy(); Assert.Throws(() => context.SaveChanges()); }); @@ -72,7 +72,7 @@ public virtual void Required_one_to_one_with_AK_relationships_are_one_to_one() { var root = context.Set().Single(IsTheRoot); - root.RequiredSingleAk = new RequiredSingleAk1(); + root.RequiredSingleAk = context.CreateProxy(); Assert.Throws(() => context.SaveChanges()); }); @@ -82,7 +82,13 @@ public virtual void Required_one_to_one_with_AK_relationships_are_one_to_one() public virtual void No_fixup_to_Deleted_entities() { using var context = CreateContext(); + var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildren).Load(); + } + var existing = root.OptionalChildren.OrderBy(e => e.Id).First(); existing.Parent = null; @@ -119,17 +125,25 @@ public virtual void No_fixup_to_Deleted_entities() [InlineData((int)(ChangeMechanism.Principal | ChangeMechanism.Dependent | ChangeMechanism.Fk), true)] public virtual void Save_optional_many_to_one_dependents(ChangeMechanism changeMechanism, bool useExistingEntities) { - var new1 = new Optional1(); - var new1d = new Optional1Derived(); - var new1dd = new Optional1MoreDerived(); - var new2a = new Optional2(); - var new2b = new Optional2(); - var new2d = new Optional2Derived(); - var new2dd = new Optional2MoreDerived(); + Optional1 new1 = null; + Optional1Derived new1d = null; + Optional1MoreDerived new1dd = null; + Optional2 new2a = null; + Optional2 new2b = null; + Optional2Derived new2d = null; + Optional2MoreDerived new2dd = null; ExecuteWithStrategyInTransaction( context => { + new1 = context.CreateProxy(); + new1d = context.CreateProxy(); + new1dd = context.CreateProxy(); + new2a = context.CreateProxy(); + new2b = context.CreateProxy(); + new2d = context.CreateProxy(); + new2dd = context.CreateProxy(); + if (useExistingEntities) { context.AddRange(new1, new1d, new1dd, new2a, new2d, new2dd, new2b); @@ -139,6 +153,12 @@ public virtual void Save_optional_many_to_one_dependents(ChangeMechanism changeM context => { var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildren).Load(); + } + var existing = root.OptionalChildren.OrderBy(e => e.Id).First(); if (useExistingEntities) @@ -238,18 +258,27 @@ public virtual void Save_optional_many_to_one_dependents(ChangeMechanism changeM [InlineData((int)(ChangeMechanism.Principal | ChangeMechanism.Dependent | ChangeMechanism.Fk), true)] public virtual void Save_required_many_to_one_dependents(ChangeMechanism changeMechanism, bool useExistingEntities) { - var newRoot = new Root(); - var new1 = new Required1 { Parent = newRoot }; - var new1d = new Required1Derived { Parent = newRoot }; - var new1dd = new Required1MoreDerived { Parent = newRoot }; - var new2a = new Required2 { Parent = new1 }; - var new2b = new Required2 { Parent = new1 }; - var new2d = new Required2Derived { Parent = new1 }; - var new2dd = new Required2MoreDerived { Parent = new1 }; + Root newRoot; + Required1 new1 = null; + Required1Derived new1d = null; + Required1MoreDerived new1dd = null; + Required2 new2a = null; + Required2 new2b = null; + Required2Derived new2d = null; + Required2MoreDerived new2dd = null; ExecuteWithStrategyInTransaction( context => { + newRoot = context.CreateProxy(); + new1 = context.CreateProxy(e => e.Parent = newRoot); + new1d = context.CreateProxy(e => e.Parent = newRoot); + new1dd = context.CreateProxy(e => e.Parent = newRoot); + new2a = context.CreateProxy(e => e.Parent = new1); + new2b = context.CreateProxy(e => e.Parent = new1); + new2d = context.CreateProxy(e => e.Parent = new1); + new2dd = context.CreateProxy(e => e.Parent = new1); + if (useExistingEntities) { context.AddRange(newRoot, new1, new1d, new1dd, new2a, new2d, new2dd, new2b); @@ -259,6 +288,12 @@ public virtual void Save_required_many_to_one_dependents(ChangeMechanism changeM context => { var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildren).Load(); + } + var existing = root.RequiredChildren.OrderBy(e => e.Id).First(); if (useExistingEntities) @@ -361,6 +396,12 @@ public virtual void Save_removed_optional_many_to_one_dependents(ChangeMechanism { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildren).Load(); + context.Entry(root.OptionalChildren.First()).Collection(e => e.Children).Load(); + } + var childCollection = root.OptionalChildren.First().Children; var removed2 = childCollection.First(); var removed1 = root.OptionalChildren.Skip(1).First(); @@ -403,6 +444,12 @@ public virtual void Save_removed_optional_many_to_one_dependents(ChangeMechanism { var loadedRoot = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(loadedRoot).Collection(e => e.OptionalChildren).Load(); + context.Entry(loadedRoot.OptionalChildren.First()).Collection(e => e.Children).Load(); + } + Assert.Single(loadedRoot.OptionalChildren); Assert.Single(loadedRoot.OptionalChildren.First().Children); } @@ -428,6 +475,12 @@ public virtual void Save_removed_required_many_to_one_dependents(ChangeMechanism { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildren).Load(); + context.Entry(root.RequiredChildren.First()).Collection(e => e.Children).Load(); + } + var childCollection = root.RequiredChildren.First().Children; var removed2 = childCollection.First(); var removed1 = root.RequiredChildren.Skip(1).First(); @@ -464,6 +517,11 @@ public virtual void Save_removed_required_many_to_one_dependents(ChangeMechanism { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildren).Load(); + } + Assert.Single(root.RequiredChildren); Assert.DoesNotContain(removed1Id, root.RequiredChildren.Select(e => e.Id)); @@ -490,12 +548,12 @@ public virtual void Save_removed_required_many_to_one_dependents(ChangeMechanism [InlineData((int)(ChangeMechanism.Principal | ChangeMechanism.Dependent | ChangeMechanism.Fk), true)] public virtual void Save_changed_optional_one_to_one(ChangeMechanism changeMechanism, bool useExistingEntities) { - var new2 = new OptionalSingle2(); - var new2d = new OptionalSingle2Derived(); - var new2dd = new OptionalSingle2MoreDerived(); - var new1 = new OptionalSingle1 { Single = new2 }; - var new1d = new OptionalSingle1Derived { Single = new2d }; - var new1dd = new OptionalSingle1MoreDerived { Single = new2dd }; + OptionalSingle2 new2 = null; + OptionalSingle2Derived new2d = null; + OptionalSingle2MoreDerived new2dd = null; + OptionalSingle1 new1 = null; + OptionalSingle1Derived new1d = null; + OptionalSingle1MoreDerived new1dd = null; OptionalSingle1 old1 = null; OptionalSingle1Derived old1d = null; OptionalSingle1MoreDerived old1dd = null; @@ -506,6 +564,13 @@ public virtual void Save_changed_optional_one_to_one(ChangeMechanism changeMecha ExecuteWithStrategyInTransaction( context => { + new2 = context.CreateProxy(); + new2d = context.CreateProxy(); + new2dd = context.CreateProxy(); + new1 = context.CreateProxy(e => e.Single = new2); + new1d = context.CreateProxy(e => e.Single = new2d); + new1dd = context.CreateProxy(e => e.Single = new2dd); + if (useExistingEntities) { context.AddRange(new1, new1d, new1dd, new2, new2d, new2dd); @@ -516,9 +581,24 @@ public virtual void Save_changed_optional_one_to_one(ChangeMechanism changeMecha { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingle).Load(); + context.Entry(root).Reference(e => e.OptionalSingleDerived).Load(); + context.Entry(root).Reference(e => e.OptionalSingleMoreDerived).Load(); + } + old1 = root.OptionalSingle; old1d = root.OptionalSingleDerived; old1dd = root.OptionalSingleMoreDerived; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + context.Entry(old1d).Reference(e => e.Single).Load(); + context.Entry(old1dd).Reference(e => e.Single).Load(); + } + old2 = root.OptionalSingle.Single; old2d = (OptionalSingle2Derived)root.OptionalSingleDerived.Single; old2dd = (OptionalSingle2MoreDerived)root.OptionalSingleMoreDerived.Single; @@ -628,21 +708,36 @@ public virtual void Save_required_one_to_one_changed_by_reference(ChangeMechanis { RequiredSingle1 old1 = null; RequiredSingle2 old2 = null; - Root oldRoot = null; + Root oldRoot; + RequiredSingle2 new2 = null; + RequiredSingle1 new1 = null; ExecuteWithStrategyInTransaction( context => { oldRoot = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(oldRoot).Reference(e => e.RequiredSingle).Load(); + } + old1 = oldRoot.RequiredSingle; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + } + old2 = oldRoot.RequiredSingle.Single; context.Entry(oldRoot).State = EntityState.Detached; context.Entry(old1).State = EntityState.Detached; context.Entry(old2).State = EntityState.Detached; + + new2 = context.CreateProxy(); + new1 = context.CreateProxy(e => e.Single = new2); }); - var new2 = new RequiredSingle2(); - var new1 = new RequiredSingle1 { Single = new2 }; ExecuteWithStrategyInTransaction( context => @@ -703,23 +798,13 @@ public virtual void Save_required_one_to_one_changed_by_reference(ChangeMechanis [InlineData((int)(ChangeMechanism.Principal | ChangeMechanism.Dependent | ChangeMechanism.Fk), true)] public virtual void Save_required_non_PK_one_to_one_changed_by_reference(ChangeMechanism changeMechanism, bool useExistingEntities) { - var new2 = new RequiredNonPkSingle2(); - var new2d = new RequiredNonPkSingle2Derived(); - var new2dd = new RequiredNonPkSingle2MoreDerived(); - var new1 = new RequiredNonPkSingle1 { Single = new2 }; - var new1d = new RequiredNonPkSingle1Derived { Single = new2d, Root = new Root() }; - var new1dd = new RequiredNonPkSingle1MoreDerived - { - Single = new2dd, - Root = new Root(), - DerivedRoot = new Root() - }; - var newRoot = new Root - { - RequiredNonPkSingle = new1, - RequiredNonPkSingleDerived = new1d, - RequiredNonPkSingleMoreDerived = new1dd - }; + RequiredNonPkSingle2 new2 = null; + RequiredNonPkSingle2Derived new2d = null; + RequiredNonPkSingle2MoreDerived new2dd = null; + RequiredNonPkSingle1 new1 = null; + RequiredNonPkSingle1Derived new1d = null; + RequiredNonPkSingle1MoreDerived new1dd = null; + Root newRoot; RequiredNonPkSingle1 old1 = null; RequiredNonPkSingle1Derived old1d = null; RequiredNonPkSingle1MoreDerived old1dd = null; @@ -730,6 +815,31 @@ public virtual void Save_required_non_PK_one_to_one_changed_by_reference(ChangeM ExecuteWithStrategyInTransaction( context => { + new2 = context.CreateProxy(); + new2d = context.CreateProxy(); + new2dd = context.CreateProxy(); + new1 = context.CreateProxy(e => e.Single = new2); + new1d = context.CreateProxy( + e => + { + e.Single = new2d; + e.Root = context.CreateProxy(); + }); + new1dd = context.CreateProxy( + e => + { + e.Single = new2dd; + e.Root = context.CreateProxy(); + e.DerivedRoot = context.CreateProxy(); + }); + newRoot = context.CreateProxy( + e => + { + e.RequiredNonPkSingle = new1; + e.RequiredNonPkSingleDerived = new1d; + e.RequiredNonPkSingleMoreDerived = new1dd; + }); + if (useExistingEntities) { context.AddRange(newRoot, new1, new1d, new1dd, new2, new2d, new2dd); @@ -740,9 +850,27 @@ public virtual void Save_required_non_PK_one_to_one_changed_by_reference(ChangeM { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + context.Entry(root).Reference(e => e.RequiredNonPkSingleDerived).Load(); + context.Entry(root).Reference(e => e.RequiredNonPkSingleMoreDerived).Load(); + } + old1 = root.RequiredNonPkSingle; old1d = root.RequiredNonPkSingleDerived; old1dd = root.RequiredNonPkSingleMoreDerived; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + context.Entry(old1d).Reference(e => e.Single).Load(); + context.Entry(old1dd).Reference(e => e.Single).Load(); + context.Entry(old1d).Reference(e => e.Root).Load(); + context.Entry(old1dd).Reference(e => e.Root).Load(); + context.Entry(old1dd).Reference(e => e.DerivedRoot).Load(); + } + old2 = root.RequiredNonPkSingle.Single; old2d = (RequiredNonPkSingle2Derived)root.RequiredNonPkSingleDerived.Single; old2dd = (RequiredNonPkSingle2MoreDerived)root.RequiredNonPkSingleMoreDerived.Single; @@ -852,7 +980,18 @@ public virtual void Sever_optional_one_to_one(ChangeMechanism changeMechanism) { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingle).Load(); + } + old1 = root.OptionalSingle; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + } + old2 = root.OptionalSingle.Single; if ((changeMechanism & ChangeMechanism.Principal) != 0) @@ -914,7 +1053,18 @@ public virtual void Sever_required_one_to_one(ChangeMechanism changeMechanism) { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingle).Load(); + } + old1 = root.RequiredSingle; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + } + old2 = root.RequiredSingle.Single; if ((changeMechanism & ChangeMechanism.Principal) != 0) @@ -967,7 +1117,18 @@ public virtual void Sever_required_non_PK_one_to_one(ChangeMechanism changeMecha { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + } + old1 = root.RequiredNonPkSingle; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + } + old2 = root.RequiredNonPkSingle.Single; if ((changeMechanism & ChangeMechanism.Principal) != 0) @@ -1023,14 +1184,16 @@ public virtual void Sever_required_non_PK_one_to_one(ChangeMechanism changeMecha [InlineData((int)(ChangeMechanism.Principal | ChangeMechanism.Dependent | ChangeMechanism.Fk), true)] public virtual void Reparent_optional_one_to_one(ChangeMechanism changeMechanism, bool useExistingRoot) { - var newRoot = new Root(); - Root root = null; + Root newRoot = null; + Root root; OptionalSingle1 old1 = null; OptionalSingle2 old2 = null; ExecuteWithStrategyInTransaction( context => { + newRoot = context.CreateProxy(); + if (useExistingRoot) { context.AddRange(newRoot); @@ -1043,7 +1206,18 @@ public virtual void Reparent_optional_one_to_one(ChangeMechanism changeMechanism context.Entry(newRoot).State = useExistingRoot ? EntityState.Unchanged : EntityState.Added; + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingle).Load(); + } + old1 = root.OptionalSingle; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + } + old2 = root.OptionalSingle.Single; if ((changeMechanism & ChangeMechanism.Principal) != 0) @@ -1106,11 +1280,13 @@ public virtual void Reparent_optional_one_to_one(ChangeMechanism changeMechanism [InlineData((int)(ChangeMechanism.Principal | ChangeMechanism.Dependent | ChangeMechanism.Fk), true)] public virtual void Reparent_required_one_to_one(ChangeMechanism changeMechanism, bool useExistingRoot) { - var newRoot = new Root(); + Root newRoot = null; ExecuteWithStrategyInTransaction( context => { + newRoot = context.CreateProxy(); + if (useExistingRoot) { context.AddRange(newRoot); @@ -1121,6 +1297,11 @@ public virtual void Reparent_required_one_to_one(ChangeMechanism changeMechanism { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingle).Load(); + } + context.Entry(newRoot).State = useExistingRoot ? EntityState.Unchanged : EntityState.Added; Assert.Equal( @@ -1167,14 +1348,16 @@ public virtual void Reparent_required_one_to_one(ChangeMechanism changeMechanism [InlineData((int)(ChangeMechanism.Principal | ChangeMechanism.Dependent | ChangeMechanism.Fk), true)] public virtual void Reparent_required_non_PK_one_to_one(ChangeMechanism changeMechanism, bool useExistingRoot) { - var newRoot = new Root(); - Root root = null; + Root newRoot = null; + Root root; RequiredNonPkSingle1 old1 = null; RequiredNonPkSingle2 old2 = null; ExecuteWithStrategyInTransaction( context => { + newRoot = context.CreateProxy(); + if (useExistingRoot) { context.AddRange(newRoot); @@ -1187,7 +1370,18 @@ public virtual void Reparent_required_non_PK_one_to_one(ChangeMechanism changeMe context.Entry(newRoot).State = useExistingRoot ? EntityState.Unchanged : EntityState.Added; + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + } + old1 = root.RequiredNonPkSingle; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + } + old2 = root.RequiredNonPkSingle.Single; if ((changeMechanism & ChangeMechanism.Principal) != 0) @@ -1261,10 +1455,8 @@ public virtual void Reparent_to_different_one_to_many(ChangeMechanism changeMech { if (!useExistingParent) { - newParent = new Optional1 - { - CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) - }; + newParent = context.CreateProxy( + e => e.CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance)); context.Set().Add(newParent); context.SaveChanges(); @@ -1276,8 +1468,19 @@ public virtual void Reparent_to_different_one_to_many(ChangeMechanism changeMech compositeCount = context.Set().Count(); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildren).Load(); + context.Entry(root).Collection(e => e.OptionalChildrenAk).Load(); + } + oldParent = root.OptionalChildrenAk.OrderBy(e => e.Id).First(); + if (!DoesLazyLoading) + { + context.Entry(oldParent).Collection(e => e.CompositeChildren).Load(); + } + oldComposite1 = oldParent.CompositeChildren.OrderBy(e => e.Id).First(); oldComposite2 = oldParent.CompositeChildren.OrderBy(e => e.Id).Last(); @@ -1291,6 +1494,11 @@ public virtual void Reparent_to_different_one_to_many(ChangeMechanism changeMech newParent.Parent = root; } + if (!DoesLazyLoading) + { + context.Entry(newParent).Collection(e => e.CompositeChildren).Load(); + } + if ((changeMechanism & ChangeMechanism.Principal) != 0) { oldParent.CompositeChildren.Remove(oldComposite1); @@ -1387,15 +1595,17 @@ public virtual void Reparent_one_to_many_overlapping(ChangeMechanism changeMecha { if (!useExistingParent) { - newParent = new RequiredComposite1 - { - Id = 3, - Parent = context.Set().Single(IsTheRoot), - CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) + newParent = context.CreateProxy( + e => { - new OptionalOverlapping2 { Id = 5 }, new OptionalOverlapping2 { Id = 6 } - } - }; + e.Id = 3; + e.Parent = context.Set().Single(IsTheRoot); + e.CompositeChildren = new ObservableHashSet(ReferenceEqualityComparer.Instance) + { + context.CreateProxy(e => e.Id = 5), + context.CreateProxy(e => e.Id = 6) + }; + }); context.Set().Add(newParent); context.SaveChanges(); @@ -1407,8 +1617,18 @@ public virtual void Reparent_one_to_many_overlapping(ChangeMechanism changeMecha childCount = context.Set().Count(); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredCompositeChildren).Load(); + } + oldParent = root.RequiredCompositeChildren.OrderBy(e => e.Id).First(); + if (!DoesLazyLoading) + { + context.Entry(oldParent).Collection(e => e.CompositeChildren).Load(); + } + oldChild1 = oldParent.CompositeChildren.OrderBy(e => e.Id).First(); oldChild2 = oldParent.CompositeChildren.OrderBy(e => e.Id).Last(); @@ -1424,6 +1644,11 @@ public virtual void Reparent_one_to_many_overlapping(ChangeMechanism changeMecha newParent.Parent = root; } + if (!DoesLazyLoading) + { + context.Entry(newParent).Collection(e => e.CompositeChildren).Load(); + } + if ((changeMechanism & ChangeMechanism.Principal) != 0) { oldParent.CompositeChildren.Remove(oldChild1); @@ -1473,6 +1698,12 @@ public virtual void Reparent_one_to_many_overlapping(ChangeMechanism changeMecha oldChild1 = context.Set().Single(e => e.Id == oldChild1.Id); oldChild2 = context.Set().Single(e => e.Id == oldChild2.Id); + if (!DoesLazyLoading) + { + context.Entry(oldParent).Collection(e => e.CompositeChildren).Load(); + context.Entry(newParent).Collection(e => e.CompositeChildren).Load(); + } + Assert.Same(oldChild2, oldParent.CompositeChildren.Single()); Assert.Same(oldParent, oldChild2.Parent); Assert.Equal(oldParent.Id, oldChild2.ParentId); @@ -1507,19 +1738,29 @@ public virtual void Reparent_one_to_many_overlapping(ChangeMechanism changeMecha public virtual void Save_optional_many_to_one_dependents_with_alternate_key( ChangeMechanism changeMechanism, bool useExistingEntities) { - var new1 = new OptionalAk1 { AlternateId = Guid.NewGuid() }; - var new1d = new OptionalAk1Derived { AlternateId = Guid.NewGuid() }; - var new1dd = new OptionalAk1MoreDerived { AlternateId = Guid.NewGuid() }; - var new2a = new OptionalAk2 { AlternateId = Guid.NewGuid() }; - var new2b = new OptionalAk2 { AlternateId = Guid.NewGuid() }; - var new2ca = new OptionalComposite2(); - var new2cb = new OptionalComposite2(); - var new2d = new OptionalAk2Derived { AlternateId = Guid.NewGuid() }; - var new2dd = new OptionalAk2MoreDerived { AlternateId = Guid.NewGuid() }; + OptionalAk1 new1 = null; + OptionalAk1Derived new1d = null; + OptionalAk1MoreDerived new1dd = null; + OptionalAk2 new2a = null; + OptionalAk2 new2b = null; + OptionalComposite2 new2ca = null; + OptionalComposite2 new2cb = null; + OptionalAk2Derived new2d = null; + OptionalAk2MoreDerived new2dd = null; ExecuteWithStrategyInTransaction( context => { + new1 = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new1d = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new1dd = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2a = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2b = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2ca = context.CreateProxy(); + new2cb = context.CreateProxy(); + new2d = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2dd = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + if (useExistingEntities) { context.AddRange(new1, new1d, new1dd, new2a, new2d, new2dd, new2b, new2ca, new2cb); @@ -1529,6 +1770,12 @@ public virtual void Save_optional_many_to_one_dependents_with_alternate_key( context => { var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildrenAk).Load(); + } + var existing = root.OptionalChildrenAk.OrderBy(e => e.Id).First(); if (useExistingEntities) @@ -1647,20 +1894,59 @@ public virtual void Save_optional_many_to_one_dependents_with_alternate_key( public virtual void Save_required_many_to_one_dependents_with_alternate_key( ChangeMechanism changeMechanism, bool useExistingEntities) { - var newRoot = new Root { AlternateId = Guid.NewGuid() }; - var new1 = new RequiredAk1 { AlternateId = Guid.NewGuid(), Parent = newRoot }; - var new1d = new RequiredAk1Derived { AlternateId = Guid.NewGuid(), Parent = newRoot }; - var new1dd = new RequiredAk1MoreDerived { AlternateId = Guid.NewGuid(), Parent = newRoot }; - var new2a = new RequiredAk2 { AlternateId = Guid.NewGuid(), Parent = new1 }; - var new2b = new RequiredAk2 { AlternateId = Guid.NewGuid(), Parent = new1 }; - var new2ca = new RequiredComposite2 { Parent = new1 }; - var new2cb = new RequiredComposite2 { Parent = new1 }; - var new2d = new RequiredAk2Derived { AlternateId = Guid.NewGuid(), Parent = new1 }; - var new2dd = new RequiredAk2MoreDerived { AlternateId = Guid.NewGuid(), Parent = new1 }; + Root newRoot; + RequiredAk1 new1 = null; + RequiredAk1Derived new1d = null; + RequiredAk1MoreDerived new1dd = null; + RequiredAk2 new2a = null; + RequiredAk2 new2b = null; + RequiredComposite2 new2ca = null; + RequiredComposite2 new2cb = null; + RequiredAk2Derived new2d = null; + RequiredAk2MoreDerived new2dd = null; ExecuteWithStrategyInTransaction( context => { + newRoot = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new1 = context.CreateProxy(e => + { + e.AlternateId = Guid.NewGuid(); + e.Parent = newRoot; + }); + new1d = context.CreateProxy(e => + { + e.AlternateId = Guid.NewGuid(); + e.Parent = newRoot; + }); + new1dd = context.CreateProxy(e => + { + e.AlternateId = Guid.NewGuid(); + e.Parent = newRoot; + }); + new2a = context.CreateProxy(e => + { + e.AlternateId = Guid.NewGuid(); + e.Parent = new1; + }); + new2b = context.CreateProxy(e => + { + e.AlternateId = Guid.NewGuid(); + e.Parent = new1; + }); + new2ca = context.CreateProxy(e => e.Parent = new1); + new2cb = context.CreateProxy(e => e.Parent = new1); + new2d = context.CreateProxy(e => + { + e.AlternateId = Guid.NewGuid(); + e.Parent = new1; + }); + new2dd = context.CreateProxy(e => + { + e.AlternateId = Guid.NewGuid(); + e.Parent = new1; + }); + if (useExistingEntities) { context.AddRange(newRoot, new1, new1d, new1dd, new2a, new2d, new2dd, new2b, new2ca, new2cb); @@ -1670,6 +1956,12 @@ public virtual void Save_required_many_to_one_dependents_with_alternate_key( context => { var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildrenAk).Load(); + } + var existing = root.RequiredChildrenAk.OrderBy(e => e.Id).First(); if (useExistingEntities) @@ -1790,6 +2082,13 @@ public virtual void Save_removed_optional_many_to_one_dependents_with_alternate_ { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildrenAk).Load(); + context.Entry(root.OptionalChildrenAk.First()).Collection(e => e.Children).Load(); + context.Entry(root.OptionalChildrenAk.First()).Collection(e => e.CompositeChildren).Load(); + } + var childCollection = root.OptionalChildrenAk.First().Children; var childCompositeCollection = root.OptionalChildrenAk.First().CompositeChildren; var removed2 = childCollection.First(); @@ -1841,6 +2140,12 @@ public virtual void Save_removed_optional_many_to_one_dependents_with_alternate_ { var loadedRoot = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(loadedRoot).Collection(e => e.OptionalChildrenAk).Load(); + context.Entry(loadedRoot.OptionalChildrenAk.First()).Collection(e => e.Children).Load(); + } + Assert.Single(loadedRoot.OptionalChildrenAk); Assert.Single(loadedRoot.OptionalChildrenAk.First().Children); } @@ -1863,6 +2168,13 @@ public virtual void Save_removed_required_many_to_one_dependents_with_alternate_ { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildrenAk).Load(); + context.Entry(root.RequiredChildrenAk.First()).Collection(e => e.Children).Load(); + context.Entry(root.RequiredChildrenAk.First()).Collection(e => e.CompositeChildren).Load(); + } + var childCollection = root.RequiredChildrenAk.First().Children; var childCompositeCollection = root.RequiredChildrenAk.First().CompositeChildren; removed2 = childCollection.First(); @@ -1906,6 +2218,13 @@ public virtual void Save_removed_required_many_to_one_dependents_with_alternate_ { var loadedRoot = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(loadedRoot).Collection(e => e.RequiredChildrenAk).Load(); + context.Entry(loadedRoot.RequiredChildrenAk.First()).Collection(e => e.Children).Load(); + context.Entry(loadedRoot.RequiredChildrenAk.First()).Collection(e => e.CompositeChildren).Load(); + } + Assert.False(context.Set().Any(e => e.Id == removed1.Id)); Assert.False(context.Set().Any(e => e.Id == removed2.Id)); Assert.False(context.Set().Any(e => e.Id == removed2c.Id)); @@ -1933,18 +2252,13 @@ public virtual void Save_removed_required_many_to_one_dependents_with_alternate_ [InlineData((int)(ChangeMechanism.Principal | ChangeMechanism.Dependent | ChangeMechanism.Fk), true)] public virtual void Save_changed_optional_one_to_one_with_alternate_key(ChangeMechanism changeMechanism, bool useExistingEntities) { - var new2 = new OptionalSingleAk2 { AlternateId = Guid.NewGuid() }; - var new2d = new OptionalSingleAk2Derived { AlternateId = Guid.NewGuid() }; - var new2dd = new OptionalSingleAk2MoreDerived { AlternateId = Guid.NewGuid() }; - var new2c = new OptionalSingleComposite2(); - var new1 = new OptionalSingleAk1 - { - AlternateId = Guid.NewGuid(), - Single = new2, - SingleComposite = new2c - }; - var new1d = new OptionalSingleAk1Derived { AlternateId = Guid.NewGuid(), Single = new2d }; - var new1dd = new OptionalSingleAk1MoreDerived { AlternateId = Guid.NewGuid(), Single = new2dd }; + OptionalSingleAk2 new2 = null; + OptionalSingleAk2Derived new2d = null; + OptionalSingleAk2MoreDerived new2dd = null; + OptionalSingleComposite2 new2c = null; + OptionalSingleAk1 new1 = null; + OptionalSingleAk1Derived new1d = null; + OptionalSingleAk1MoreDerived new1dd = null; OptionalSingleAk1 old1 = null; OptionalSingleAk1Derived old1d = null; OptionalSingleAk1MoreDerived old1dd = null; @@ -1956,6 +2270,30 @@ public virtual void Save_changed_optional_one_to_one_with_alternate_key(ChangeMe ExecuteWithStrategyInTransaction( context => { + new2 = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2d = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2dd = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2c = context.CreateProxy(); + new1 = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = new2; + e.SingleComposite = new2c; + }); + new1d = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = new2d; + }); + new1dd = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = new2dd; + }); + if (useExistingEntities) { context.AddRange(new1, new1d, new1dd, new2, new2d, new2dd, new2c); @@ -1966,9 +2304,25 @@ public virtual void Save_changed_optional_one_to_one_with_alternate_key(ChangeMe { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingleAk).Load(); + context.Entry(root).Reference(e => e.OptionalSingleAkDerived).Load(); + context.Entry(root).Reference(e => e.OptionalSingleAkMoreDerived).Load(); + } + old1 = root.OptionalSingleAk; old1d = root.OptionalSingleAkDerived; old1dd = root.OptionalSingleAkMoreDerived; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + context.Entry(old1).Reference(e => e.SingleComposite).Load(); + context.Entry(old1d).Reference(e => e.Single).Load(); + context.Entry(old1dd).Reference(e => e.Single).Load(); + } + old2 = root.OptionalSingleAk.Single; old2c = root.OptionalSingleAk.SingleComposite; old2d = (OptionalSingleAk2Derived)root.OptionalSingleAkDerived.Single; @@ -2081,18 +2435,13 @@ public virtual void Save_changed_optional_one_to_one_with_alternate_key(ChangeMe [ConditionalFact] public virtual void Save_changed_optional_one_to_one_with_alternate_key_in_store() { - var new2 = new OptionalSingleAk2 { AlternateId = Guid.NewGuid() }; - var new2d = new OptionalSingleAk2Derived { AlternateId = Guid.NewGuid() }; - var new2dd = new OptionalSingleAk2MoreDerived { AlternateId = Guid.NewGuid() }; - var new2c = new OptionalSingleComposite2(); - var new1 = new OptionalSingleAk1 - { - AlternateId = Guid.NewGuid(), - Single = new2, - SingleComposite = new2c - }; - var new1d = new OptionalSingleAk1Derived { AlternateId = Guid.NewGuid(), Single = new2d }; - var new1dd = new OptionalSingleAk1MoreDerived { AlternateId = Guid.NewGuid(), Single = new2dd }; + OptionalSingleAk2 new2; + OptionalSingleAk2Derived new2d; + OptionalSingleAk2MoreDerived new2dd; + OptionalSingleComposite2 new2c; + OptionalSingleAk1 new1; + OptionalSingleAk1Derived new1d; + OptionalSingleAk1MoreDerived new1dd; OptionalSingleAk1 old1 = null; OptionalSingleAk1Derived old1d = null; OptionalSingleAk1MoreDerived old1dd = null; @@ -2104,11 +2453,51 @@ public virtual void Save_changed_optional_one_to_one_with_alternate_key_in_store ExecuteWithStrategyInTransaction( context => { + new2 = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2d = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2dd = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2c = context.CreateProxy(); + new1 = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = new2; + e.SingleComposite = new2c; + }); + new1d = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = new2d; + }); + new1dd = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = new2dd; + }); + var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingleAk).Load(); + context.Entry(root).Reference(e => e.OptionalSingleAkDerived).Load(); + context.Entry(root).Reference(e => e.OptionalSingleAkMoreDerived).Load(); + } + old1 = root.OptionalSingleAk; old1d = root.OptionalSingleAkDerived; old1dd = root.OptionalSingleAkMoreDerived; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + context.Entry(old1).Reference(e => e.SingleComposite).Load(); + context.Entry(old1d).Reference(e => e.Single).Load(); + context.Entry(old1dd).Reference(e => e.Single).Load(); + } + old2 = root.OptionalSingleAk.Single; old2c = root.OptionalSingleAk.SingleComposite; old2d = (OptionalSingleAk2Derived)root.OptionalSingleAkDerived.Single; @@ -2248,15 +2637,10 @@ public virtual void Save_changed_optional_one_to_one_with_alternate_key_in_store public virtual void Save_required_one_to_one_changed_by_reference_with_alternate_key( ChangeMechanism changeMechanism, bool useExistingEntities) { - var new2 = new RequiredSingleAk2 { AlternateId = Guid.NewGuid() }; - var new2c = new RequiredSingleComposite2(); - var new1 = new RequiredSingleAk1 - { - AlternateId = Guid.NewGuid(), - Single = new2, - SingleComposite = new2c - }; - var newRoot = new Root { AlternateId = Guid.NewGuid(), RequiredSingleAk = new1 }; + RequiredSingleAk2 new2 = null; + RequiredSingleComposite2 new2c = null; + RequiredSingleAk1 new1 = null;; + Root newRoot; RequiredSingleAk1 old1 = null; RequiredSingleAk2 old2 = null; RequiredSingleComposite2 old2c = null; @@ -2264,6 +2648,21 @@ public virtual void Save_required_one_to_one_changed_by_reference_with_alternate ExecuteWithStrategyInTransaction( context => { + new2 = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2c = context.CreateProxy(); + new1 = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = new2; + e.SingleComposite = new2c; + }); + newRoot = context.CreateProxy(e => + { + e.AlternateId = Guid.NewGuid(); + e.RequiredSingleAk = new1; + }); + if (useExistingEntities) { context.AddRange(newRoot, new1, new2, new2c); @@ -2274,7 +2673,19 @@ public virtual void Save_required_one_to_one_changed_by_reference_with_alternate { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingleAk).Load(); + } + old1 = root.RequiredSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + context.Entry(old1).Reference(e => e.SingleComposite).Load(); + } + old2 = root.RequiredSingleAk.Single; old2c = root.RequiredSingleAk.SingleComposite; @@ -2353,30 +2764,13 @@ public virtual void Save_required_one_to_one_changed_by_reference_with_alternate public virtual void Save_required_non_PK_one_to_one_changed_by_reference_with_alternate_key( ChangeMechanism changeMechanism, bool useExistingEntities) { - var new2 = new RequiredNonPkSingleAk2 { AlternateId = Guid.NewGuid() }; - var new2d = new RequiredNonPkSingleAk2Derived { AlternateId = Guid.NewGuid() }; - var new2dd = new RequiredNonPkSingleAk2MoreDerived { AlternateId = Guid.NewGuid() }; - var new1 = new RequiredNonPkSingleAk1 { AlternateId = Guid.NewGuid(), Single = new2 }; - var new1d = new RequiredNonPkSingleAk1Derived - { - AlternateId = Guid.NewGuid(), - Single = new2d, - Root = new Root() - }; - var new1dd = new RequiredNonPkSingleAk1MoreDerived - { - AlternateId = Guid.NewGuid(), - Single = new2dd, - Root = new Root(), - DerivedRoot = new Root() - }; - var newRoot = new Root - { - AlternateId = Guid.NewGuid(), - RequiredNonPkSingleAk = new1, - RequiredNonPkSingleAkDerived = new1d, - RequiredNonPkSingleAkMoreDerived = new1dd - }; + RequiredNonPkSingleAk2 new2 = null; + RequiredNonPkSingleAk2Derived new2d = null; + RequiredNonPkSingleAk2MoreDerived new2dd = null; + RequiredNonPkSingleAk1 new1 = null; + RequiredNonPkSingleAk1Derived new1d = null; + RequiredNonPkSingleAk1MoreDerived new1dd = null; + Root newRoot; RequiredNonPkSingleAk1 old1 = null; RequiredNonPkSingleAk1Derived old1d = null; RequiredNonPkSingleAk1MoreDerived old1dd = null; @@ -2387,6 +2781,38 @@ public virtual void Save_required_non_PK_one_to_one_changed_by_reference_with_al ExecuteWithStrategyInTransaction( context => { + new2 = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2d = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new2dd = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + new1 = context.CreateProxy(e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = new2; + }); + new1d = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = new2d; + e.Root = context.CreateProxy(); + }); + new1dd = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.Single = new2dd; + e.Root = context.CreateProxy(); + e.DerivedRoot = context.CreateProxy(); + }); + newRoot = context.CreateProxy( + e => + { + e.AlternateId = Guid.NewGuid(); + e.RequiredNonPkSingleAk = new1; + e.RequiredNonPkSingleAkDerived = new1d; + e.RequiredNonPkSingleAkMoreDerived = new1dd; + }); + if (useExistingEntities) { context.AddRange(newRoot, new1, new1d, new1dd, new2, new2d, new2dd); @@ -2397,9 +2823,27 @@ public virtual void Save_required_non_PK_one_to_one_changed_by_reference_with_al { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).Load(); + context.Entry(root).Reference(e => e.RequiredNonPkSingleAkDerived).Load(); + context.Entry(root).Reference(e => e.RequiredNonPkSingleAkMoreDerived).Load(); + } + old1 = root.RequiredNonPkSingleAk; old1d = root.RequiredNonPkSingleAkDerived; old1dd = root.RequiredNonPkSingleAkMoreDerived; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + context.Entry(old1d).Reference(e => e.Single).Load(); + context.Entry(old1dd).Reference(e => e.Single).Load(); + context.Entry(old1d).Reference(e => e.Root).Load(); + context.Entry(old1dd).Reference(e => e.Root).Load(); + context.Entry(old1dd).Reference(e => e.DerivedRoot).Load(); + } + old2 = root.RequiredNonPkSingleAk.Single; old2d = (RequiredNonPkSingleAk2Derived)root.RequiredNonPkSingleAkDerived.Single; old2dd = (RequiredNonPkSingleAk2MoreDerived)root.RequiredNonPkSingleAkMoreDerived.Single; @@ -2510,7 +2954,19 @@ public virtual void Sever_optional_one_to_one_with_alternate_key(ChangeMechanism { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingleAk).Load(); + } + old1 = root.OptionalSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + context.Entry(old1).Reference(e => e.SingleComposite).Load(); + } + old2 = root.OptionalSingleAk.Single; old2c = root.OptionalSingleAk.SingleComposite; @@ -2581,7 +3037,19 @@ public virtual void Sever_required_one_to_one_with_alternate_key(ChangeMechanism { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingleAk).Load(); + } + old1 = root.RequiredSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + context.Entry(old1).Reference(e => e.SingleComposite).Load(); + } + old2 = root.RequiredSingleAk.Single; old2c = root.RequiredSingleAk.SingleComposite; @@ -2639,7 +3107,18 @@ public virtual void Sever_required_non_PK_one_to_one_with_alternate_key(ChangeMe { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).Load(); + } + old1 = root.RequiredNonPkSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + } + old2 = root.RequiredNonPkSingleAk.Single; if ((changeMechanism & ChangeMechanism.Principal) != 0) @@ -2657,8 +3136,12 @@ public virtual void Sever_required_non_PK_one_to_one_with_alternate_key(ChangeMe throw new ArgumentOutOfRangeException(nameof(changeMechanism)); } - context.ChangeTracker.DetectChanges(); - context.ChangeTracker.DetectChanges(); + if (!DoesChangeTracking) + { + context.ChangeTracker.DetectChanges(); + context.ChangeTracker.DetectChanges(); + } + Assert.False(context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).IsLoaded); Assert.False(context.Entry(old1).Reference(e => e.Root).IsLoaded); Assert.True(context.ChangeTracker.HasChanges()); @@ -2697,8 +3180,8 @@ public virtual void Sever_required_non_PK_one_to_one_with_alternate_key(ChangeMe [InlineData((int)(ChangeMechanism.Principal | ChangeMechanism.Dependent | ChangeMechanism.Fk), true)] public virtual void Reparent_optional_one_to_one_with_alternate_key(ChangeMechanism changeMechanism, bool useExistingRoot) { - var newRoot = new Root { AlternateId = Guid.NewGuid() }; - Root root = null; + Root newRoot = null; + Root root; OptionalSingleAk1 old1 = null; OptionalSingleAk2 old2 = null; OptionalSingleComposite2 old2c = null; @@ -2706,6 +3189,8 @@ public virtual void Reparent_optional_one_to_one_with_alternate_key(ChangeMechan ExecuteWithStrategyInTransaction( context => { + newRoot = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + if (useExistingRoot) { context.Add(newRoot); @@ -2718,7 +3203,19 @@ public virtual void Reparent_optional_one_to_one_with_alternate_key(ChangeMechan context.Entry(newRoot).State = useExistingRoot ? EntityState.Unchanged : EntityState.Added; + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingleAk).Load(); + } + old1 = root.OptionalSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + context.Entry(old1).Reference(e => e.SingleComposite).Load(); + } + old2 = root.OptionalSingleAk.Single; old2c = root.OptionalSingleAk.SingleComposite; @@ -2789,8 +3286,8 @@ public virtual void Reparent_optional_one_to_one_with_alternate_key(ChangeMechan [InlineData((int)(ChangeMechanism.Principal | ChangeMechanism.Dependent | ChangeMechanism.Fk), true)] public virtual void Reparent_required_one_to_one_with_alternate_key(ChangeMechanism changeMechanism, bool useExistingRoot) { - var newRoot = new Root { AlternateId = Guid.NewGuid() }; - Root root = null; + Root newRoot = null; + Root root; RequiredSingleAk1 old1 = null; RequiredSingleAk2 old2 = null; RequiredSingleComposite2 old2c = null; @@ -2798,6 +3295,8 @@ public virtual void Reparent_required_one_to_one_with_alternate_key(ChangeMechan ExecuteWithStrategyInTransaction( context => { + newRoot = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + if (useExistingRoot) { context.Add(newRoot); @@ -2810,7 +3309,19 @@ public virtual void Reparent_required_one_to_one_with_alternate_key(ChangeMechan context.Entry(newRoot).State = useExistingRoot ? EntityState.Unchanged : EntityState.Added; + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingleAk).Load(); + } + old1 = root.RequiredSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(old1).Reference(e => e.Single).Load(); + context.Entry(old1).Reference(e => e.SingleComposite).Load(); + } + old2 = root.RequiredSingleAk.Single; old2c = root.RequiredSingleAk.SingleComposite; @@ -2881,14 +3392,16 @@ public virtual void Reparent_required_one_to_one_with_alternate_key(ChangeMechan [InlineData((int)(ChangeMechanism.Principal | ChangeMechanism.Dependent | ChangeMechanism.Fk), true)] public virtual void Reparent_required_non_PK_one_to_one_with_alternate_key(ChangeMechanism changeMechanism, bool useExistingRoot) { - var newRoot = new Root { AlternateId = Guid.NewGuid() }; - Root root = null; + Root newRoot = null; + Root root; RequiredNonPkSingleAk1 old1 = null; RequiredNonPkSingleAk2 old2 = null; ExecuteWithStrategyInTransaction( context => { + newRoot = context.CreateProxy(e => e.AlternateId = Guid.NewGuid()); + if (useExistingRoot) { context.Add(newRoot); @@ -2901,12 +3414,23 @@ public virtual void Reparent_required_non_PK_one_to_one_with_alternate_key(Chang context.Entry(newRoot).State = useExistingRoot ? EntityState.Unchanged : EntityState.Added; + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).Load(); + } + old1 = root.RequiredNonPkSingleAk; - old2 = root.RequiredNonPkSingleAk.Single; - if ((changeMechanism & ChangeMechanism.Principal) != 0) + if (!DoesLazyLoading) { - newRoot.RequiredNonPkSingleAk = old1; + context.Entry(old1).Reference(e => e.Single).Load(); + } + + old2 = root.RequiredNonPkSingleAk.Single; + + if ((changeMechanism & ChangeMechanism.Principal) != 0) + { + newRoot.RequiredNonPkSingleAk = old1; } if ((changeMechanism & ChangeMechanism.Dependent) != 0) @@ -2972,10 +3496,20 @@ public virtual void Required_many_to_one_dependents_are_cascade_deleted( var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildren).Load(); + } + Assert.Equal(2, root.RequiredChildren.Count()); var removed = root.RequiredChildren.First(); + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + } + removedId = removed.Id; var cascadeRemoved = removed.Children.ToList(); orphanedIds = cascadeRemoved.Select(e => e.Id).ToList(); @@ -3015,6 +3549,11 @@ public virtual void Required_many_to_one_dependents_are_cascade_deleted( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildren).Load(); + } + Assert.Single(root.RequiredChildren); Assert.DoesNotContain(removedId, root.RequiredChildren.Select(e => e.Id)); @@ -3049,10 +3588,20 @@ public virtual void Optional_many_to_one_dependents_are_orphaned( var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildren).Load(); + } + Assert.Equal(2, root.OptionalChildren.Count()); var removed = root.OptionalChildren.First(); + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + } + removedId = removed.Id; var orphaned = removed.Children.ToList(); orphanedIds = orphaned.Select(e => e.Id).ToList(); @@ -3083,6 +3632,11 @@ public virtual void Optional_many_to_one_dependents_are_orphaned( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildren).Load(); + } + Assert.Single(root.OptionalChildren); Assert.DoesNotContain(removedId, root.OptionalChildren.Select(e => e.Id)); @@ -3116,8 +3670,18 @@ public virtual void Optional_one_to_one_are_orphaned( var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingle).Load(); + } + var removed = root.OptionalSingle; + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + removedId = removed.Id; var orphaned = removed.Single; orphanedId = orphaned.Id; @@ -3177,8 +3741,18 @@ public virtual void Required_one_to_one_are_cascade_deleted( var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingle).Load(); + } + var removed = root.RequiredSingle; + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + removedId = removed.Id; var orphaned = removed.Single; orphanedId = orphaned.Id; @@ -3215,6 +3789,11 @@ public virtual void Required_one_to_one_are_cascade_deleted( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingle).Load(); + } + Assert.Null(root.RequiredSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -3248,8 +3827,18 @@ public virtual void Required_non_PK_one_to_one_are_cascade_deleted( var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + } + var removed = root.RequiredNonPkSingle; + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + removedId = removed.Id; var orphaned = removed.Single; orphanedId = orphaned.Id; @@ -3286,6 +3875,11 @@ public virtual void Required_non_PK_one_to_one_are_cascade_deleted( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + } + Assert.Null(root.RequiredNonPkSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -3319,11 +3913,21 @@ public virtual void Optional_many_to_one_dependents_with_alternate_key_are_orpha var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildrenAk).Load(); + } + Assert.Equal(2, root.OptionalChildrenAk.Count()); var removed = root.OptionalChildrenAk.First(); context.Entry(removed).Collection(e => e.CompositeChildren).Load(); + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + } + removedId = removed.Id; var orphaned = removed.Children.ToList(); orphanedIds = orphaned.Select(e => e.Id).ToList(); @@ -3354,6 +3958,11 @@ public virtual void Optional_many_to_one_dependents_with_alternate_key_are_orpha { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildrenAk).Load(); + } + Assert.Single(root.OptionalChildrenAk); Assert.DoesNotContain(removedId, root.OptionalChildrenAk.Select(e => e.Id)); @@ -3388,10 +3997,21 @@ public virtual void Required_many_to_one_dependents_with_alternate_key_are_casca var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildrenAk).Load(); + } + Assert.Equal(2, root.RequiredChildrenAk.Count()); var removed = root.RequiredChildrenAk.First(); + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + context.Entry(removed).Collection(e => e.CompositeChildren).Load(); + } + removedId = removed.Id; var cascadeRemoved = removed.Children.ToList(); var cascadeRemovedC = removed.CompositeChildren.ToList(); @@ -3435,6 +4055,11 @@ public virtual void Required_many_to_one_dependents_with_alternate_key_are_casca { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildrenAk).Load(); + } + Assert.Single(root.RequiredChildrenAk); Assert.DoesNotContain(removedId, root.RequiredChildrenAk.Select(e => e.Id)); @@ -3471,8 +4096,19 @@ public virtual void Optional_one_to_one_with_alternate_key_are_orphaned( var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingleAk).Load(); + } + var removed = root.OptionalSingleAk; + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + context.Entry(removed).Reference(e => e.SingleComposite).Load(); + } + removedId = removed.Id; var orphaned = removed.Single; var orphanedC = removed.SingleComposite; @@ -3504,6 +4140,11 @@ public virtual void Optional_one_to_one_with_alternate_key_are_orphaned( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingleAk).Load(); + } + Assert.Null(root.OptionalSingleAk); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -3538,8 +4179,19 @@ public virtual void Required_one_to_one_with_alternate_key_are_cascade_deleted( var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingleAk).Load(); + } + var removed = root.RequiredSingleAk; + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + context.Entry(removed).Reference(e => e.SingleComposite).Load(); + } + removedId = removed.Id; var orphaned = removed.Single; var orphanedC = removed.SingleComposite; @@ -3580,6 +4232,11 @@ public virtual void Required_one_to_one_with_alternate_key_are_cascade_deleted( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingleAk).Load(); + } + Assert.Null(root.RequiredSingleAk); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -3614,8 +4271,18 @@ public virtual void Required_non_PK_one_to_one_with_alternate_key_are_cascade_de var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).Load(); + } + var removed = root.RequiredNonPkSingleAk; + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + removedId = removed.Id; var orphaned = removed.Single; orphanedId = orphaned.Id; @@ -3652,6 +4319,11 @@ public virtual void Required_non_PK_one_to_one_with_alternate_key_are_cascade_de { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).Load(); + } + Assert.Null(root.RequiredNonPkSingleAk); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -3680,7 +4352,19 @@ public virtual void Required_many_to_one_dependents_are_cascade_deleted_in_store ExecuteWithStrategyInTransaction( context => { - var removed = LoadRoot(context).RequiredChildren.First(); + var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildren).Load(); + } + + var removed = root.RequiredChildren.First(); + + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + } removedId = removed.Id; orphanedIds = removed.Children.Select(e => e.Id).ToList(); @@ -3721,6 +4405,11 @@ public virtual void Required_many_to_one_dependents_are_cascade_deleted_in_store { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildren).Load(); + } + Assert.Single(root.RequiredChildren); Assert.DoesNotContain(removedId, root.RequiredChildren.Select(e => e.Id)); @@ -3749,7 +4438,19 @@ public virtual void Required_one_to_one_are_cascade_deleted_in_store( ExecuteWithStrategyInTransaction( context => { - var removed = LoadRoot(context).RequiredSingle; + var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingle).Load(); + } + + var removed = root.RequiredSingle; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } removedId = removed.Id; orphanedId = removed.Single.Id; @@ -3762,6 +4463,12 @@ public virtual void Required_one_to_one_are_cascade_deleted_in_store( var root = context.Set().Include(e => e.RequiredSingle).Single(IsTheRoot); var removed = root.RequiredSingle; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + var orphaned = removed.Single; context.Remove(removed); @@ -3780,6 +4487,11 @@ public virtual void Required_one_to_one_are_cascade_deleted_in_store( Assert.Equal(EntityState.Detached, context.Entry(removed).State); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingle).Load(); + } + Assert.Null(root.RequiredSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -3795,6 +4507,11 @@ public virtual void Required_one_to_one_are_cascade_deleted_in_store( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingle).Load(); + } + Assert.Null(root.RequiredSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -3823,7 +4540,19 @@ public virtual void Required_non_PK_one_to_one_are_cascade_deleted_in_store( ExecuteWithStrategyInTransaction( context => { - var removed = LoadRoot(context).RequiredNonPkSingle; + var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + } + + var removed = root.RequiredNonPkSingle; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } removedId = removed.Id; orphanedId = removed.Single.Id; @@ -3836,6 +4565,12 @@ public virtual void Required_non_PK_one_to_one_are_cascade_deleted_in_store( var root = context.Set().Include(e => e.RequiredNonPkSingle).Single(IsTheRoot); var removed = root.RequiredNonPkSingle; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + var orphaned = removed.Single; context.Remove(removed); @@ -3854,6 +4589,11 @@ public virtual void Required_non_PK_one_to_one_are_cascade_deleted_in_store( Assert.Equal(EntityState.Detached, context.Entry(removed).State); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + } + Assert.Null(root.RequiredNonPkSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -3869,6 +4609,11 @@ public virtual void Required_non_PK_one_to_one_are_cascade_deleted_in_store( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + } + Assert.Null(root.RequiredNonPkSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -3898,7 +4643,20 @@ public virtual void Required_many_to_one_dependents_with_alternate_key_are_casca ExecuteWithStrategyInTransaction( context => { - var removed = LoadRoot(context).RequiredChildrenAk.First(); + var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildrenAk).Load(); + } + + var removed = root.RequiredChildrenAk.First(); + + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + context.Entry(removed).Collection(e => e.CompositeChildren).Load(); + } removedId = removed.Id; orphanedIds = removed.Children.Select(e => e.Id).ToList(); @@ -3940,6 +4698,11 @@ public virtual void Required_many_to_one_dependents_with_alternate_key_are_casca { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildrenAk).Load(); + } + Assert.Single(root.RequiredChildrenAk); Assert.DoesNotContain(removedId, root.RequiredChildrenAk.Select(e => e.Id)); @@ -3970,7 +4733,20 @@ public virtual void Required_one_to_one_with_alternate_key_are_cascade_deleted_i ExecuteWithStrategyInTransaction( context => { - var removed = LoadRoot(context).RequiredSingleAk; + var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingleAk).Load(); + } + + var removed = root.RequiredSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + context.Entry(removed).Reference(e => e.SingleComposite).Load(); + } removedId = removed.Id; orphanedId = removed.Single.Id; @@ -3984,6 +4760,12 @@ public virtual void Required_one_to_one_with_alternate_key_are_cascade_deleted_i var root = context.Set().Include(e => e.RequiredSingleAk).Single(IsTheRoot); var removed = root.RequiredSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + var orphaned = removed.Single; context.Remove(removed); @@ -4018,6 +4800,11 @@ public virtual void Required_one_to_one_with_alternate_key_are_cascade_deleted_i { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingleAk).Load(); + } + Assert.Null(root.RequiredSingleAk); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -4047,7 +4834,19 @@ public virtual void Required_non_PK_one_to_one_with_alternate_key_are_cascade_de ExecuteWithStrategyInTransaction( context => { - var removed = LoadRoot(context).RequiredNonPkSingleAk; + var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).Load(); + } + + var removed = root.RequiredNonPkSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } removedId = removed.Id; orphanedId = removed.Single.Id; @@ -4060,6 +4859,12 @@ public virtual void Required_non_PK_one_to_one_with_alternate_key_are_cascade_de var root = context.Set().Include(e => e.RequiredNonPkSingleAk).Single(IsTheRoot); var removed = root.RequiredNonPkSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + var orphaned = removed.Single; context.Remove(removed); @@ -4093,6 +4898,11 @@ public virtual void Required_non_PK_one_to_one_with_alternate_key_are_cascade_de { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).Load(); + } + Assert.Null(root.RequiredNonPkSingleAk); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -4121,7 +4931,19 @@ public virtual void Optional_many_to_one_dependents_are_orphaned_in_store( ExecuteWithStrategyInTransaction( context => { - var removed = LoadRoot(context).OptionalChildren.First(); + var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildren).Load(); + } + + var removed = root.OptionalChildren.First(); + + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + } removedId = removed.Id; orphanedIds = removed.Children.Select(e => e.Id).ToList(); @@ -4165,6 +4987,11 @@ public virtual void Optional_many_to_one_dependents_are_orphaned_in_store( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildren).Load(); + } + Assert.Single(root.OptionalChildren); Assert.DoesNotContain(removedId, root.OptionalChildren.Select(e => e.Id)); @@ -4196,7 +5023,19 @@ public virtual void Optional_one_to_one_are_orphaned_in_store( ExecuteWithStrategyInTransaction( context => { - var removed = LoadRoot(context).OptionalSingle; + var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingle).Load(); + } + + var removed = root.OptionalSingle; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } removedId = removed.Id; orphanedId = removed.Single.Id; @@ -4209,6 +5048,12 @@ public virtual void Optional_one_to_one_are_orphaned_in_store( var root = context.Set().Include(e => e.OptionalSingle).Single(IsTheRoot); var removed = root.OptionalSingle; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + var orphaned = removed.Single; context.Remove(removed); @@ -4233,6 +5078,11 @@ public virtual void Optional_one_to_one_are_orphaned_in_store( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingle).Load(); + } + Assert.Null(root.OptionalSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -4261,7 +5111,20 @@ public virtual void Optional_many_to_one_dependents_with_alternate_key_are_orpha ExecuteWithStrategyInTransaction( context => { - var removed = LoadRoot(context).OptionalChildrenAk.First(); + var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildrenAk).Load(); + } + + var removed = root.OptionalChildrenAk.First(); + + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + context.Entry(removed).Collection(e => e.CompositeChildren).Load(); + } removedId = removed.Id; orphanedIds = removed.Children.Select(e => e.Id).ToList(); @@ -4314,6 +5177,11 @@ public virtual void Optional_many_to_one_dependents_with_alternate_key_are_orpha { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildrenAk).Load(); + } + Assert.Single(root.OptionalChildrenAk); Assert.DoesNotContain(removedId, root.OptionalChildrenAk.Select(e => e.Id)); @@ -4350,7 +5218,20 @@ public virtual void Optional_one_to_one_with_alternate_key_are_orphaned_in_store ExecuteWithStrategyInTransaction( context => { - var removed = LoadRoot(context).OptionalSingleAk; + var root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingleAk).Load(); + } + + var removed = root.OptionalSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + context.Entry(removed).Reference(e => e.SingleComposite).Load(); + } removedId = removed.Id; orphanedId = removed.Single.Id; @@ -4364,6 +5245,12 @@ public virtual void Optional_one_to_one_with_alternate_key_are_orphaned_in_store var root = context.Set().Include(e => e.OptionalSingleAk).Single(IsTheRoot); var removed = root.OptionalSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + var orphaned = removed.Single; context.Remove(removed); @@ -4393,6 +5280,11 @@ public virtual void Optional_one_to_one_with_alternate_key_are_orphaned_in_store { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingleAk).Load(); + } + Assert.Null(root.OptionalSingleAk); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -4425,7 +5317,19 @@ public virtual void Required_many_to_one_dependents_are_cascade_deleted_starting context => { root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildren).Load(); + } + removed = root.RequiredChildren.First(); + + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + } + cascadeRemoved = removed.Children.ToList(); Assert.Equal(2, root.RequiredChildren.Count()); @@ -4475,6 +5379,11 @@ public virtual void Required_many_to_one_dependents_are_cascade_deleted_starting { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildren).Load(); + } + Assert.Single(root.RequiredChildren); Assert.DoesNotContain(removedId, root.RequiredChildren.Select(e => e.Id)); @@ -4508,7 +5417,19 @@ public virtual void Optional_many_to_one_dependents_are_orphaned_starting_detach context => { root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildren).Load(); + } + removed = root.OptionalChildren.First(); + + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + } + orphaned = removed.Children.ToList(); Assert.Equal(2, root.OptionalChildren.Count()); @@ -4549,6 +5470,11 @@ public virtual void Optional_many_to_one_dependents_are_orphaned_starting_detach { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildren).Load(); + } + Assert.Single(root.OptionalChildren); Assert.DoesNotContain(removedId, root.OptionalChildren.Select(e => e.Id)); @@ -4581,7 +5507,19 @@ public virtual void Optional_one_to_one_are_orphaned_starting_detached( context => { root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingle).Load(); + } + removed = root.OptionalSingle; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + orphaned = removed.Single; }, context => @@ -4618,6 +5556,11 @@ public virtual void Optional_one_to_one_are_orphaned_starting_detached( { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingle).Load(); + } + Assert.Null(root.OptionalSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -4649,7 +5592,19 @@ public virtual void Required_one_to_one_are_cascade_deleted_starting_detached( context => { root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingle).Load(); + } + removed = root.RequiredSingle; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + orphaned = removed.Single; }, context => @@ -4694,6 +5649,12 @@ public virtual void Required_one_to_one_are_cascade_deleted_starting_detached( if (cascadeDeleteTiming != CascadeTiming.Never) { root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingle).Load(); + } + Assert.Null(root.RequiredSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -4726,7 +5687,19 @@ public virtual void Required_non_PK_one_to_one_are_cascade_deleted_starting_deta context => { root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + } + removed = root.RequiredNonPkSingle; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + orphaned = removed.Single; }, context => @@ -4772,6 +5745,11 @@ public virtual void Required_non_PK_one_to_one_are_cascade_deleted_starting_deta { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + } + Assert.Null(root.RequiredNonPkSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -4806,7 +5784,20 @@ public virtual void Optional_many_to_one_dependents_with_alternate_key_are_orpha context => { root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildrenAk).Load(); + } + removed = root.OptionalChildrenAk.First(); + + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + context.Entry(removed).Collection(e => e.CompositeChildren).Load(); + } + orphaned = removed.Children.ToList(); orphanedC = removed.CompositeChildren.ToList(); @@ -4852,6 +5843,11 @@ public virtual void Optional_many_to_one_dependents_with_alternate_key_are_orpha { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.OptionalChildrenAk).Load(); + } + Assert.Single(root.OptionalChildrenAk); Assert.DoesNotContain(removedId, root.OptionalChildrenAk.Select(e => e.Id)); @@ -4887,7 +5883,20 @@ public virtual void Required_many_to_one_dependents_with_alternate_key_are_casca context => { root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildrenAk).Load(); + } + removed = root.RequiredChildrenAk.First(); + + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + context.Entry(removed).Collection(e => e.CompositeChildren).Load(); + } + cascadeRemoved = removed.Children.ToList(); cascadeRemovedC = removed.CompositeChildren.ToList(); @@ -4941,6 +5950,11 @@ public virtual void Required_many_to_one_dependents_with_alternate_key_are_casca { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildrenAk).Load(); + } + Assert.Single(root.RequiredChildrenAk); Assert.DoesNotContain(removedId, root.RequiredChildrenAk.Select(e => e.Id)); @@ -4977,7 +5991,20 @@ public virtual void Optional_one_to_one_with_alternate_key_are_orphaned_starting context => { root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingleAk).Load(); + } + removed = root.OptionalSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + context.Entry(removed).Reference(e => e.SingleComposite).Load(); + } + orphaned = removed.Single; orphanedC = removed.SingleComposite; }, @@ -5018,6 +6045,11 @@ public virtual void Optional_one_to_one_with_alternate_key_are_orphaned_starting { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.OptionalSingleAk).Load(); + } + Assert.Null(root.OptionalSingleAk); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -5052,7 +6084,20 @@ public virtual void Required_one_to_one_with_alternate_key_are_cascade_deleted_s context => { root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingleAk).Load(); + } + removed = root.RequiredSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + context.Entry(removed).Reference(e => e.SingleComposite).Load(); + } + orphaned = removed.Single; orphanedC = removed.SingleComposite; }, @@ -5102,6 +6147,11 @@ public virtual void Required_one_to_one_with_alternate_key_are_cascade_deleted_s { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingleAk).Load(); + } + Assert.Null(root.RequiredSingleAk); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -5135,7 +6185,19 @@ public virtual void Required_non_PK_one_to_one_with_alternate_key_are_cascade_de context => { root = LoadRoot(context); + + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).Load(); + } + removed = root.RequiredNonPkSingleAk; + + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + orphaned = removed.Single; }, context => @@ -5181,6 +6243,11 @@ public virtual void Required_non_PK_one_to_one_with_alternate_key_are_cascade_de { root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).Load(); + } + Assert.Null(root.RequiredNonPkSingleAk); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -5214,20 +6281,31 @@ public virtual void Required_many_to_one_dependents_are_cascade_detached_when_Ad var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildren).Load(); + } + Assert.Equal(2, root.RequiredChildren.Count()); var removed = root.RequiredChildren.First(); + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + } + removedId = removed.Id; var cascadeRemoved = removed.Children.ToList(); orphanedIds = cascadeRemoved.Select(e => e.Id).ToList(); Assert.Equal(2, orphanedIds.Count); - var added = new Required2(); + var added = context.CreateProxy(); Add(removed.Children, added); - if (context.ChangeTracker.AutoDetectChangesEnabled) + if (context.ChangeTracker.AutoDetectChangesEnabled + && !DoesChangeTracking) { context.ChangeTracker.DetectChanges(); } @@ -5278,6 +6356,11 @@ public virtual void Required_many_to_one_dependents_are_cascade_detached_when_Ad { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildren).Load(); + } + Assert.Single(root.RequiredChildren); Assert.DoesNotContain(removedId, root.RequiredChildren.Select(e => e.Id)); @@ -5312,8 +6395,18 @@ public virtual void Required_one_to_one_are_cascade_detached_when_Added( var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingle).Load(); + } + var removed = root.RequiredSingle; + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + removedId = removed.Id; var orphaned = removed.Single; orphanedId = orphaned.Id; @@ -5358,6 +6451,11 @@ public virtual void Required_one_to_one_are_cascade_detached_when_Added( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingle).Load(); + } + Assert.Null(root.RequiredSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -5391,8 +6489,18 @@ public virtual void Required_non_PK_one_to_one_are_cascade_detached_when_Added( var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + } + var removed = root.RequiredNonPkSingle; + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + removedId = removed.Id; var orphaned = removed.Single; orphanedId = orphaned.Id; @@ -5437,6 +6545,11 @@ public virtual void Required_non_PK_one_to_one_are_cascade_detached_when_Added( { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingle).Load(); + } + Assert.Null(root.RequiredNonPkSingle); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -5471,10 +6584,21 @@ public virtual void Required_many_to_one_dependents_with_alternate_key_are_casca var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildrenAk).Load(); + } + Assert.Equal(2, root.RequiredChildrenAk.Count()); var removed = root.RequiredChildrenAk.First(); + if (!DoesLazyLoading) + { + context.Entry(removed).Collection(e => e.Children).Load(); + context.Entry(removed).Collection(e => e.CompositeChildren).Load(); + } + removedId = removed.Id; var cascadeRemoved = removed.Children.ToList(); var cascadeRemovedC = removed.CompositeChildren.ToList(); @@ -5484,12 +6608,13 @@ public virtual void Required_many_to_one_dependents_with_alternate_key_are_casca Assert.Equal(2, orphanedIds.Count); Assert.Equal(2, orphanedIdCs.Count); - var added = new RequiredAk2(); - var addedC = new RequiredComposite2(); + var added = context.CreateProxy(); + var addedC = context.CreateProxy(); Add(removed.Children, added); Add(removed.CompositeChildren, addedC); - if (context.ChangeTracker.AutoDetectChangesEnabled) + if (context.ChangeTracker.AutoDetectChangesEnabled + && !DoesChangeTracking) { context.ChangeTracker.DetectChanges(); } @@ -5547,6 +6672,11 @@ public virtual void Required_many_to_one_dependents_with_alternate_key_are_casca { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Collection(e => e.RequiredChildrenAk).Load(); + } + Assert.Single(root.RequiredChildrenAk); Assert.DoesNotContain(removedId, root.RequiredChildrenAk.Select(e => e.Id)); @@ -5583,8 +6713,19 @@ public virtual void Required_one_to_one_with_alternate_key_are_cascade_detached_ var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingleAk).Load(); + } + var removed = root.RequiredSingleAk; + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + context.Entry(removed).Reference(e => e.SingleComposite).Load(); + } + removedId = removed.Id; var orphaned = removed.Single; var orphanedC = removed.SingleComposite; @@ -5635,6 +6776,11 @@ public virtual void Required_one_to_one_with_alternate_key_are_cascade_detached_ { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredSingleAk).Load(); + } + Assert.Null(root.RequiredSingleAk); Assert.Empty(context.Set().Where(e => e.Id == removedId)); @@ -5669,8 +6815,18 @@ public virtual void Required_non_PK_one_to_one_with_alternate_key_are_cascade_de var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).Load(); + } + var removed = root.RequiredNonPkSingleAk; + if (!DoesLazyLoading) + { + context.Entry(removed).Reference(e => e.Single).Load(); + } + removedId = removed.Id; var orphaned = removed.Single; orphanedId = orphaned.Id; @@ -5715,6 +6871,11 @@ public virtual void Required_non_PK_one_to_one_with_alternate_key_are_cascade_de { var root = LoadRoot(context); + if (!DoesLazyLoading) + { + context.Entry(root).Reference(e => e.RequiredNonPkSingleAk).Load(); + } + Assert.Null(root.RequiredNonPkSingleAk); Assert.Empty(context.Set().Where(e => e.Id == removedId)); diff --git a/test/EFCore.SqlServer.FunctionalTests/ProxyGraphUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/ProxyGraphUpdatesSqlServerTest.cs index 7405f397bf9..fb4afda25e2 100644 --- a/test/EFCore.SqlServer.FunctionalTests/ProxyGraphUpdatesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/ProxyGraphUpdatesSqlServerTest.cs @@ -35,6 +35,9 @@ public LazyLoading(ProxyGraphUpdatesWithLazyLoadingSqlServerFixture fixture) { } + protected override bool DoesLazyLoading => true; + protected override bool DoesChangeTracking => false; + public class ProxyGraphUpdatesWithLazyLoadingSqlServerFixture : ProxyGraphUpdatesSqlServerFixtureBase { protected override string StoreName { get; } = "ProxyGraphLazyLoadingUpdatesTest"; @@ -53,5 +56,63 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con } } } + + public class ChangeTracking : ProxyGraphUpdatesSqlServerTestBase + { + public ChangeTracking(ProxyGraphUpdatesWithChangeTrackingSqlServerFixture fixture) + : base(fixture) + { + } + + protected override bool DoesLazyLoading => false; + protected override bool DoesChangeTracking => true; + + public class ProxyGraphUpdatesWithChangeTrackingSqlServerFixture : ProxyGraphUpdatesSqlServerFixtureBase + { + protected override string StoreName { get; } = "ProxyGraphChangeTrackingUpdatesTest"; + + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder.UseChangeDetectionProxies()); + + protected override IServiceCollection AddServices(IServiceCollection serviceCollection) + => base.AddServices(serviceCollection.AddEntityFrameworkProxies()); + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + modelBuilder.UseIdentityColumns(); + + base.OnModelCreating(modelBuilder, context); + } + } + } + + public class ChangeTrackingAndLazyLoading : ProxyGraphUpdatesSqlServerTestBase + { + public ChangeTrackingAndLazyLoading(ProxyGraphUpdatesWithChangeTrackingAndLazyLoadingSqlServerFixture fixture) + : base(fixture) + { + } + + protected override bool DoesLazyLoading => true; + protected override bool DoesChangeTracking => true; + + public class ProxyGraphUpdatesWithChangeTrackingAndLazyLoadingSqlServerFixture : ProxyGraphUpdatesSqlServerFixtureBase + { + protected override string StoreName { get; } = "ProxyGraphChangeTrackingAndLazyLoadingUpdatesTest"; + + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder.UseLazyLoadingProxies().UseChangeDetectionProxies()); + + protected override IServiceCollection AddServices(IServiceCollection serviceCollection) + => base.AddServices(serviceCollection.AddEntityFrameworkProxies()); + + protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) + { + modelBuilder.UseIdentityColumns(); + + base.OnModelCreating(modelBuilder, context); + } + } + } } } diff --git a/test/EFCore.Sqlite.FunctionalTests/ProxyGraphUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/ProxyGraphUpdatesSqliteTest.cs index 679410612ea..5c1e13d6c54 100644 --- a/test/EFCore.Sqlite.FunctionalTests/ProxyGraphUpdatesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/ProxyGraphUpdatesSqliteTest.cs @@ -36,6 +36,9 @@ public LazyLoading(ProxyGraphUpdatesWithLazyLoadingSqliteFixture fixture) { } + protected override bool DoesLazyLoading => true; + protected override bool DoesChangeTracking => false; + public class ProxyGraphUpdatesWithLazyLoadingSqliteFixture : ProxyGraphUpdatesSqliteFixtureBase { protected override string StoreName { get; } = "ProxyGraphLazyLoadingUpdatesTest"; @@ -47,5 +50,49 @@ protected override IServiceCollection AddServices(IServiceCollection serviceColl => base.AddServices(serviceCollection.AddEntityFrameworkProxies()); } } + + public class ChangeTracking : ProxyGraphUpdatesSqliteTestBase + { + public ChangeTracking(ProxyGraphUpdatesWithChangeTrackingSqliteFixture fixture) + : base(fixture) + { + } + + protected override bool DoesLazyLoading => false; + protected override bool DoesChangeTracking => true; + + public class ProxyGraphUpdatesWithChangeTrackingSqliteFixture : ProxyGraphUpdatesSqliteFixtureBase + { + protected override string StoreName { get; } = "ProxyGraphChangeTrackingUpdatesTest"; + + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder.UseChangeDetectionProxies()); + + protected override IServiceCollection AddServices(IServiceCollection serviceCollection) + => base.AddServices(serviceCollection.AddEntityFrameworkProxies()); + } + } + + public class ChangeTrackingAndLazyLoading : ProxyGraphUpdatesSqliteTestBase + { + public ChangeTrackingAndLazyLoading(ProxyGraphUpdatesWithChangeTrackingAndLazyLoadingSqliteFixture fixture) + : base(fixture) + { + } + + protected override bool DoesLazyLoading => true; + protected override bool DoesChangeTracking => true; + + public class ProxyGraphUpdatesWithChangeTrackingAndLazyLoadingSqliteFixture : ProxyGraphUpdatesSqliteFixtureBase + { + protected override string StoreName { get; } = "ProxyGraphChangeTrackingAndLazyLoadingUpdatesTest"; + + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder.UseChangeDetectionProxies().UseLazyLoadingProxies()); + + protected override IServiceCollection AddServices(IServiceCollection serviceCollection) + => base.AddServices(serviceCollection.AddEntityFrameworkProxies()); + } + } } }