Skip to content

Commit

Permalink
Refactor ConventionDispatcher to allow delayed execution of conventio…
Browse files Browse the repository at this point in the history
…ns and tracking objects modified by conventions.

  When set on ConventionDispatcher ConventionScope captures the conventions to be executed.
  ConventionBatch sets a ConventionScope and executed the stored conventions when it's run or disposed.
  When conventions captured in a ConventionScope are executed any triggered conventions are captured in a new ConventionScope to avoid keeping all the convention chain on the stack.
  This changes the order of convention execution which exposed some issues in code and tests
Allow specifying the dependend end without specifying the FK properties,
Throw when the same property is specified several times when adding a key, foreign key or indexes.
Remove Mock usage from ConventionDispatcherTest

Part of #214
  • Loading branch information
AndriySvyryd committed Feb 28, 2017
1 parent 338bac4 commit 037cd41
Show file tree
Hide file tree
Showing 51 changed files with 3,135 additions and 1,972 deletions.
26 changes: 26 additions & 0 deletions src/Microsoft.EntityFrameworkCore/Internal/IReferenceRoot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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 JetBrains.Annotations;

namespace Microsoft.EntityFrameworkCore.Internal
{
/// <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 interface IReferenceRoot<T>
{
/// <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>
Reference<T> Track([NotNull] T @object);

/// <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>
void Release([NotNull] Reference<T> reference);
}
}
53 changes: 53 additions & 0 deletions src/Microsoft.EntityFrameworkCore/Internal/Reference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Internal
{
/// <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 class Reference<T> : IDisposable
{
private readonly IReferenceRoot<T> _root;

/// <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 Reference([NotNull] T @object)
: this(@object, null)
{
}

/// <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 Reference([CanBeNull] T @object, [CanBeNull] IReferenceRoot<T> root)
{
Object = @object;
_root = root;
}

/// <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 T Object { get; [param: NotNull]set; }

/// <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 Dispose()
{
_root?.Release(this);
Object = default(T);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Diagnostics;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal;

namespace Microsoft.EntityFrameworkCore.Metadata.Builders
{
Expand Down Expand Up @@ -40,7 +42,7 @@ public EntityTypeBuilder([NotNull] InternalEntityTypeBuilder builder)
protected virtual EntityTypeBuilder New([NotNull] InternalEntityTypeBuilder builder)
=> new EntityTypeBuilder(builder);

private InternalEntityTypeBuilder Builder { get; }
private InternalEntityTypeBuilder Builder { [DebuggerStepThrough] get; }

/// <summary>
/// Gets the internal builder being used to configure the entity type.
Expand Down Expand Up @@ -355,11 +357,16 @@ private InternalRelationshipBuilder HasOneBuilder(EntityType relatedEntityType,
var navigationProperty = navigation.Property;
if (relatedEntityType == Metadata)
{
var relationship = Builder.Relationship(relatedEntityType.Builder, ConfigurationSource.Explicit)
.RelatedEntityTypes(relatedEntityType, Builder.Metadata, ConfigurationSource.Explicit);
return navigationProperty != null
? relationship.DependentToPrincipal(navigationProperty, ConfigurationSource.Explicit)
: relationship.DependentToPrincipal(navigation.Name, ConfigurationSource.Explicit);
using (var batch = relatedEntityType.Model.ConventionDispatcher.StartBatch())
{
var relationship = Builder.Relationship(relatedEntityType.Builder, ConfigurationSource.Explicit)
.RelatedEntityTypes(relatedEntityType, Builder.Metadata, ConfigurationSource.Explicit);
relationship = navigationProperty != null
? relationship.DependentToPrincipal(navigationProperty, ConfigurationSource.Explicit)
: relationship.DependentToPrincipal(navigation.Name, ConfigurationSource.Explicit);

return batch.Run(relationship);
}
}

return navigationProperty != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal;

namespace Microsoft.EntityFrameworkCore.Metadata.Builders
{
Expand Down Expand Up @@ -194,66 +195,78 @@ private InternalRelationshipBuilder WithOneBuilder(PropertyIdentity reference)
ThrowForConflictingNavigation(Builder.Metadata, referenceName, false);
}

var builder = Builder.IsUnique(true, ConfigurationSource.Explicit);
var foreingKey = builder.Metadata;
if (foreingKey.IsSelfReferencing()
&& referenceName != null
&& ReferenceName == referenceName)
using (var batch = Builder.Metadata.DeclaringEntityType.Model.ConventionDispatcher.StartBatch())
{
throw new InvalidOperationException(CoreStrings.DuplicateNavigation(
referenceName, RelatedEntityType.DisplayName(), RelatedEntityType.DisplayName()));
}
var builder = Builder.IsUnique(true, ConfigurationSource.Explicit);
var foreingKey = builder.Metadata;
if (foreingKey.IsSelfReferencing()
&& referenceName != null
&& ReferenceName == referenceName)
{
throw new InvalidOperationException(CoreStrings.DuplicateNavigation(
referenceName, RelatedEntityType.DisplayName(), RelatedEntityType.DisplayName()));
}

var pointsToPrincipal = !foreingKey.IsSelfReferencing()
&& (!foreingKey.DeclaringEntityType.IsAssignableFrom(DeclaringEntityType)
|| !foreingKey.PrincipalEntityType.IsAssignableFrom(RelatedEntityType)
|| (foreingKey.DeclaringEntityType.IsAssignableFrom(RelatedEntityType)
&& foreingKey.PrincipalEntityType.IsAssignableFrom(DeclaringEntityType)
&& foreingKey.PrincipalToDependent != null
&& foreingKey.PrincipalToDependent.Name == ReferenceName));
var pointsToPrincipal = !foreingKey.IsSelfReferencing()
&& (!foreingKey.DeclaringEntityType.IsAssignableFrom(DeclaringEntityType)
|| !foreingKey.PrincipalEntityType.IsAssignableFrom(RelatedEntityType)
|| (foreingKey.DeclaringEntityType.IsAssignableFrom(RelatedEntityType)
&& foreingKey.PrincipalEntityType.IsAssignableFrom(DeclaringEntityType)
&& foreingKey.PrincipalToDependent != null
&& foreingKey.PrincipalToDependent.Name == ReferenceName));

if (referenceName != null
&& ((pointsToPrincipal
&& foreingKey.DependentToPrincipal != null
&& foreingKey.GetDependentToPrincipalConfigurationSource() == ConfigurationSource.Explicit
&& foreingKey.DependentToPrincipal.Name != referenceName)
|| (!pointsToPrincipal
&& foreingKey.PrincipalToDependent != null
&& foreingKey.GetPrincipalToDependentConfigurationSource() == ConfigurationSource.Explicit
&& foreingKey.PrincipalToDependent.Name != referenceName)))
{
ThrowForConflictingNavigation(foreingKey, referenceName, pointsToPrincipal);
}
if (referenceName != null
&& ((pointsToPrincipal
&& foreingKey.DependentToPrincipal != null
&& foreingKey.GetDependentToPrincipalConfigurationSource() == ConfigurationSource.Explicit
&& foreingKey.DependentToPrincipal.Name != referenceName)
|| (!pointsToPrincipal
&& foreingKey.PrincipalToDependent != null
&& foreingKey.GetPrincipalToDependentConfigurationSource() == ConfigurationSource.Explicit
&& foreingKey.PrincipalToDependent.Name != referenceName)))
{
ThrowForConflictingNavigation(foreingKey, referenceName, pointsToPrincipal);
}

if (referenceName != null)
{
if (pointsToPrincipal
if (referenceName != null
&& pointsToPrincipal
&& RelatedEntityType != foreingKey.DeclaringEntityType)
{
return reference.Property == null && ReferenceProperty == null
? builder.Navigations(reference.Name, ReferenceName, DeclaringEntityType, RelatedEntityType, ConfigurationSource.Explicit)
: builder.Navigations(reference.Property, ReferenceProperty, DeclaringEntityType, RelatedEntityType, ConfigurationSource.Explicit);
builder = reference.Property == null && ReferenceProperty == null
? builder.Navigations(
reference.Name, ReferenceName, DeclaringEntityType, RelatedEntityType, ConfigurationSource.Explicit)
: builder.Navigations(
reference.Property, ReferenceProperty, DeclaringEntityType, RelatedEntityType, ConfigurationSource.Explicit);
}
if (!pointsToPrincipal
&& RelatedEntityType != foreingKey.PrincipalEntityType)
else if (referenceName != null
&& !pointsToPrincipal
&& RelatedEntityType != foreingKey.PrincipalEntityType)
{
return reference.Property == null && ReferenceProperty == null
? builder.Navigations(ReferenceName, reference.Name, RelatedEntityType, DeclaringEntityType, ConfigurationSource.Explicit)
: builder.Navigations(ReferenceProperty, reference.Property, RelatedEntityType, DeclaringEntityType, ConfigurationSource.Explicit);
builder = reference.Property == null && ReferenceProperty == null
? builder.Navigations(
ReferenceName, reference.Name, RelatedEntityType, DeclaringEntityType, ConfigurationSource.Explicit)
: builder.Navigations(
ReferenceProperty, reference.Property, RelatedEntityType, DeclaringEntityType, ConfigurationSource.Explicit);
}
else
{
var referenceProperty = reference.Property;
if (referenceProperty != null)
{
builder = pointsToPrincipal
? builder.DependentToPrincipal(referenceProperty, ConfigurationSource.Explicit)
: builder.PrincipalToDependent(referenceProperty, ConfigurationSource.Explicit);
}
else
{
builder = pointsToPrincipal
? builder.DependentToPrincipal(reference.Name, ConfigurationSource.Explicit)
: builder.PrincipalToDependent(reference.Name, ConfigurationSource.Explicit);
}
}
}

var referenceProperty = reference.Property;
if (referenceProperty != null)
{
return pointsToPrincipal
? builder.DependentToPrincipal(referenceProperty, ConfigurationSource.Explicit)
: builder.PrincipalToDependent(referenceProperty, ConfigurationSource.Explicit);
return batch.Run(builder);
}

return pointsToPrincipal
? builder.DependentToPrincipal(reference.Name, ConfigurationSource.Explicit)
: builder.PrincipalToDependent(reference.Name, ConfigurationSource.Explicit);
}

private void ThrowForConflictingNavigation(ForeignKey foreingKey, string newInverseName, bool newToPrincipal)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;

Expand Down Expand Up @@ -42,6 +43,9 @@ public ReferenceReferenceBuilder(
: this(builder, null)
{
Check.NotNull(builder, nameof(builder));
Check.NotNull(declaringEntityType, nameof(declaringEntityType));
Check.NotNull(relatedEntityType, nameof(relatedEntityType));

_declaringEntityType = declaringEntityType;
_relatedEntityType = relatedEntityType;
}
Expand Down Expand Up @@ -167,7 +171,7 @@ public virtual ReferenceReferenceBuilder HasForeignKey(
=> new ReferenceReferenceBuilder(
HasForeignKeyBuilder(ResolveEntityType(Check.NotNull(dependentEntityType, nameof(dependentEntityType))),
dependentEntityType.ShortDisplayName(),
Check.NotEmpty(foreignKeyPropertyNames, nameof(foreignKeyPropertyNames))),
Check.NotNull(foreignKeyPropertyNames, nameof(foreignKeyPropertyNames))),
this,
Builder.Metadata.DeclaringEntityType.ClrType != dependentEntityType,
true);
Expand Down Expand Up @@ -235,7 +239,7 @@ public virtual ReferenceReferenceBuilder HasForeignKey(
=> new ReferenceReferenceBuilder(
HasForeignKeyBuilder(ResolveEntityType(Check.NotNull(dependentEntityTypeName, nameof(dependentEntityTypeName))),
dependentEntityTypeName,
Check.NotEmpty(foreignKeyPropertyNames, nameof(foreignKeyPropertyNames))),
Check.NotNull(foreignKeyPropertyNames, nameof(foreignKeyPropertyNames))),
this,
Builder.Metadata.DeclaringEntityType.Name != ResolveEntityType(dependentEntityTypeName).Name,
true);
Expand Down Expand Up @@ -276,9 +280,13 @@ private InternalRelationshipBuilder HasForeignKeyBuilder(
}
var principalEntityType = GetOtherEntityType(dependentEntityType);

var builder = Builder.RelatedEntityTypes(principalEntityType, dependentEntityType, ConfigurationSource.Explicit);
using (var batch = dependentEntityType.Model.ConventionDispatcher.StartBatch())
{
var builder = Builder.RelatedEntityTypes(principalEntityType, dependentEntityType, ConfigurationSource.Explicit);
builder = hasForeignKey(builder, dependentEntityType);

return hasForeignKey(builder, dependentEntityType);
return batch.Run(builder);
}
}

/// <summary>
Expand All @@ -300,7 +308,7 @@ public virtual ReferenceReferenceBuilder HasPrincipalKey(
HasPrincipalKeyBuilder(
ResolveEntityType(Check.NotNull(principalEntityType, nameof(principalEntityType))),
principalEntityType.ShortDisplayName(),
Check.NotEmpty(keyPropertyNames, nameof(keyPropertyNames))),
Check.NotNull(keyPropertyNames, nameof(keyPropertyNames))),
this,
inverted: Builder.Metadata.PrincipalEntityType.ClrType != principalEntityType,
principalKeySet: true);
Expand Down Expand Up @@ -341,7 +349,7 @@ public virtual ReferenceReferenceBuilder HasPrincipalKey(
HasPrincipalKeyBuilder(
ResolveEntityType(Check.NotEmpty(principalEntityTypeName, nameof(principalEntityTypeName))),
principalEntityTypeName,
Check.NotEmpty(keyPropertyNames, nameof(keyPropertyNames))),
Check.NotNull(keyPropertyNames, nameof(keyPropertyNames))),
this,
inverted: Builder.Metadata.PrincipalEntityType.Name != ResolveEntityType(principalEntityTypeName).Name,
principalKeySet: true);
Expand Down Expand Up @@ -381,9 +389,14 @@ private InternalRelationshipBuilder HasPrincipalKeyBuilder(
principalEntityTypeName));
}

var builder = Builder.RelatedEntityTypes(principalEntityType, GetOtherEntityType(principalEntityType), ConfigurationSource.Explicit);
using (var batch = principalEntityType.Model.ConventionDispatcher.StartBatch())
{
var builder = Builder.RelatedEntityTypes(
principalEntityType, GetOtherEntityType(principalEntityType), ConfigurationSource.Explicit);
builder = hasPrincipalKey(builder);

return hasPrincipalKey(builder);
return batch.Run(builder);
}
}

/// <summary>
Expand Down
Loading

0 comments on commit 037cd41

Please sign in to comment.