Skip to content

Commit

Permalink
Add change tracking support for shadow navigations to non-shadow enti…
Browse files Browse the repository at this point in the history
…ty types

Part of #749
  • Loading branch information
AndriySvyryd committed Sep 29, 2017
1 parent e3e76e1 commit 7dc35f4
Show file tree
Hide file tree
Showing 37 changed files with 1,545 additions and 2,472 deletions.
3 changes: 1 addition & 2 deletions src/EFCore/ChangeTracking/CollectionEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

namespace Microsoft.EntityFrameworkCore.ChangeTracking
{
Expand Down Expand Up @@ -117,6 +116,6 @@ public override IQueryable Query()
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
protected virtual void EnsureInitialized()
=> Metadata.GetCollectionAccessor().GetOrCreate(InternalEntry.Entity);
=> InternalEntry.GetOrCreateCollection(Metadata);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class EmptyShadowValuesFactoryFactory : SnapshotFactoryFactory
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
protected override int GetPropertyIndex(IPropertyBase propertyBase)
=> (propertyBase as IProperty)?.GetShadowIndex() ?? -1;
=> propertyBase.GetShadowIndex();

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
Expand Down
50 changes: 46 additions & 4 deletions src/EFCore/ChangeTracking/Internal/InternalEntityEntry.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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;
Expand Down Expand Up @@ -522,6 +522,50 @@ protected virtual void WritePropertyValue([NotNull] IPropertyBase propertyBase,
propertyBase.GetSetter().SetClrValue(Entity, value);
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual object GetOrCreateCollection([NotNull] INavigation navigation)
{
Debug.Assert(!navigation.IsShadowProperty);

return navigation.GetCollectionAccessor().GetOrCreate(Entity);
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual bool CollectionContains([NotNull] INavigation navigation, [NotNull] InternalEntityEntry value)
{
Debug.Assert(!navigation.IsShadowProperty);

return navigation.GetCollectionAccessor().Contains(Entity, value.Entity);
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual bool AddToCollection([NotNull] INavigation navigation, [NotNull] InternalEntityEntry value)
{
Debug.Assert(!navigation.IsShadowProperty);

return navigation.GetCollectionAccessor().Add(Entity, value.Entity);
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual void RemoveFromCollection([NotNull] INavigation navigation, [NotNull] InternalEntityEntry value)
{
Debug.Assert(!navigation.IsShadowProperty);

navigation.GetCollectionAccessor().Remove(Entity, value.Entity);
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
Expand Down Expand Up @@ -673,9 +717,7 @@ public virtual object this[[NotNull] IPropertyBase propertyBase]
{
get
{
return propertyBase is INavigation && propertyBase.IsShadowProperty // Remove when issue #749 is fixed
? null
: _storeGeneratedValues.TryGetValue(propertyBase, out var value)
return _storeGeneratedValues.TryGetValue(propertyBase, out var value)
? value
: ReadPropertyValue(propertyBase);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;

// ReSharper disable ParameterOnlyUsedForPreconditionCheck.Local
namespace Microsoft.EntityFrameworkCore.ChangeTracking.Internal
{
/// <summary>
Expand Down Expand Up @@ -90,8 +91,7 @@ private static INotifyCollectionChanged AsINotifyCollectionChanged(
IEntityType entityType,
ChangeTrackingStrategy changeTrackingStrategy)
{
var notifyingCollection = navigation.GetCollectionAccessor().GetOrCreate(entry.Entity) as INotifyCollectionChanged;
if (notifyingCollection == null)
if (!(navigation.GetCollectionAccessor()?.GetOrCreate(entry.Entity) is INotifyCollectionChanged notifyingCollection))
{
throw new InvalidOperationException(
CoreStrings.NonNotifyingCollection(navigation.Name, entityType.DisplayName(), changeTrackingStrategy));
Expand All @@ -105,8 +105,7 @@ private static INotifyPropertyChanged AsINotifyPropertyChanged(
IEntityType entityType,
ChangeTrackingStrategy changeTrackingStrategy)
{
var changed = entry.Entity as INotifyPropertyChanged;
if (changed == null)
if (!(entry.Entity is INotifyPropertyChanged changed))
{
throw new InvalidOperationException(
CoreStrings.ChangeTrackingInterfaceMissing(
Expand All @@ -121,8 +120,7 @@ private static INotifyPropertyChanging AsINotifyPropertyChanging(
IEntityType entityType,
ChangeTrackingStrategy changeTrackingStrategy)
{
var changing = entry.Entity as INotifyPropertyChanging;
if (changing == null)
if (!(entry.Entity is INotifyPropertyChanging changing))
{
throw new InvalidOperationException(
CoreStrings.ChangeTrackingInterfaceMissing(
Expand Down
73 changes: 73 additions & 0 deletions src/EFCore/ChangeTracking/Internal/InternalMixedEntityEntry.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// 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 JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
Expand Down Expand Up @@ -85,5 +87,76 @@ protected override void WritePropertyValue(IPropertyBase propertyBase, object va
_shadowValues[propertyBase.GetShadowIndex()] = value;
}
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public override object GetOrCreateCollection(INavigation navigation)
=> navigation.IsShadowProperty
? GetOrCreateCollectionTyped(navigation)
: base.GetOrCreateCollection(navigation);

private ICollection<object> GetOrCreateCollectionTyped(INavigation navigation)
{
if (!(_shadowValues[navigation.GetShadowIndex()] is ICollection<object> collection))
{
collection = new HashSet<object>();
_shadowValues[navigation.GetShadowIndex()] = collection;
}

return collection;
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public override bool CollectionContains(INavigation navigation, InternalEntityEntry value)
=> navigation.IsShadowProperty
? GetOrCreateCollectionTyped(navigation).Contains(value.Entity)
: base.CollectionContains(navigation, value);

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public override bool AddToCollection(INavigation navigation, InternalEntityEntry value)
{
if (!navigation.IsShadowProperty)
{
return base.AddToCollection(navigation, value);
}

if (navigation.GetTargetType().ClrType == null)
{
return false;
}

var collection = GetOrCreateCollectionTyped(navigation);
if (!collection.Contains(value.Entity))
{
collection.Add(value.Entity);
return true;
}

return false;
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public override void RemoveFromCollection(INavigation navigation, InternalEntityEntry value)
{
if (navigation.IsShadowProperty)
{
GetOrCreateCollectionTyped(navigation).Remove(value.Entity);
}
else
{
base.RemoveFromCollection(navigation, value);
}
}
}
}
55 changes: 55 additions & 0 deletions src/EFCore/ChangeTracking/Internal/InternalShadowEntityEntry.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// 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 JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
Expand Down Expand Up @@ -70,5 +72,58 @@ protected override object ReadPropertyValue(IPropertyBase propertyBase)
/// </summary>
protected override void WritePropertyValue(IPropertyBase propertyBase, object value)
=> _propertyValues[propertyBase.GetShadowIndex()] = value;

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public override object GetOrCreateCollection(INavigation navigation)
=> GetOrCreateCollectionTyped(navigation);

private ICollection<object> GetOrCreateCollectionTyped(INavigation navigation)
{
if (!(_propertyValues[navigation.GetShadowIndex()] is ICollection<object> collection))
{
collection = new HashSet<object>();
_propertyValues[navigation.GetShadowIndex()] = collection;
}

return collection;
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public override bool CollectionContains(INavigation navigation, InternalEntityEntry value)
=> GetOrCreateCollectionTyped(navigation).Contains(value.Entity);

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public override bool AddToCollection(INavigation navigation, InternalEntityEntry value)
{
if (navigation.GetTargetType().ClrType == null)
{
return false;
}

var collection = GetOrCreateCollectionTyped(navigation);
if (!collection.Contains(value.Entity))
{
collection.Add(value.Entity);
return true;
}

return false;
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public override void RemoveFromCollection(INavigation navigation, InternalEntityEntry value)
=> GetOrCreateCollectionTyped(navigation).Remove(value.Entity);
}
}
5 changes: 2 additions & 3 deletions src/EFCore/ChangeTracking/Internal/KeyPropagator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,9 @@ private static InternalEntityEntry TryPropagateValue(InternalEntityEntry entry,
{
if (property == foreignKey.Properties[propertyIndex])
{
// Remove when issue #749 is fixed
var principal = foreignKey.DependentToPrincipal?.IsShadowProperty ?? true
var principal = foreignKey.DependentToPrincipal == null
? null
: foreignKey.DependentToPrincipal.GetGetter().GetClrValue(entry.Entity);
: entry[foreignKey.DependentToPrincipal];
InternalEntityEntry principalEntry = null;
if (principal != null)
{
Expand Down
Loading

0 comments on commit 7dc35f4

Please sign in to comment.