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 Mar 3, 2017
1 parent e59b583 commit cc24376
Show file tree
Hide file tree
Showing 55 changed files with 4,544 additions and 3,038 deletions.
2 changes: 1 addition & 1 deletion EntityFramework.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FElsewhere/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FSimpleTypes/@EntryIndexedValue">WARNING</s:String>

<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=EntityFramework/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="EntityFramework"&gt;&lt;HtmlReformatCode&gt;True&lt;/HtmlReformatCode&gt;&lt;CSArrangeThisQualifier&gt;True&lt;/CSArrangeThisQualifier&gt;&lt;CSRemoveCodeRedundancies&gt;True&lt;/CSRemoveCodeRedundancies&gt;&lt;CSUseAutoProperty&gt;True&lt;/CSUseAutoProperty&gt;&lt;CSMakeFieldReadonly&gt;True&lt;/CSMakeFieldReadonly&gt;&lt;CSUseVar&gt;&lt;BehavourStyle&gt;CAN_CHANGE_TO_IMPLICIT&lt;/BehavourStyle&gt;&lt;LocalVariableStyle&gt;ALWAYS_IMPLICIT&lt;/LocalVariableStyle&gt;&lt;ForeachVariableStyle&gt;ALWAYS_IMPLICIT&lt;/ForeachVariableStyle&gt;&lt;/CSUseVar&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSShortenReferences&gt;True&lt;/CSShortenReferences&gt;&lt;CSReformatCode&gt;True&lt;/CSReformatCode&gt;&lt;XMLReformatCode&gt;True&lt;/XMLReformatCode&gt;&lt;CSUpdateFileHeader&gt;True&lt;/CSUpdateFileHeader&gt;&lt;CSharpFormatDocComments&gt;True&lt;/CSharpFormatDocComments&gt;&lt;RemoveCodeRedundancies&gt;True&lt;/RemoveCodeRedundancies&gt;&lt;CSMakeAutoPropertyGetOnly&gt;True&lt;/CSMakeAutoPropertyGetOnly&gt;&lt;CSArrangeQualifiers&gt;True&lt;/CSArrangeQualifiers&gt;&lt;CSEnforceVarKeywordUsageSettings&gt;True&lt;/CSEnforceVarKeywordUsageSettings&gt;&lt;CSFixBuiltinTypeReferences&gt;True&lt;/CSFixBuiltinTypeReferences&gt;&lt;CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="True" ArrangeArgumentsStyle="False" /&gt;&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=EntityFramework/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="EntityFramework"&gt;&lt;HtmlReformatCode&gt;True&lt;/HtmlReformatCode&gt;&lt;CSArrangeThisQualifier&gt;True&lt;/CSArrangeThisQualifier&gt;&lt;CSRemoveCodeRedundancies&gt;True&lt;/CSRemoveCodeRedundancies&gt;&lt;CSUseAutoProperty&gt;True&lt;/CSUseAutoProperty&gt;&lt;CSMakeFieldReadonly&gt;True&lt;/CSMakeFieldReadonly&gt;&lt;CSUseVar&gt;&lt;BehavourStyle&gt;CAN_CHANGE_TO_IMPLICIT&lt;/BehavourStyle&gt;&lt;LocalVariableStyle&gt;ALWAYS_IMPLICIT&lt;/LocalVariableStyle&gt;&lt;ForeachVariableStyle&gt;ALWAYS_IMPLICIT&lt;/ForeachVariableStyle&gt;&lt;/CSUseVar&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSShortenReferences&gt;True&lt;/CSShortenReferences&gt;&lt;CSReformatCode&gt;True&lt;/CSReformatCode&gt;&lt;XMLReformatCode&gt;True&lt;/XMLReformatCode&gt;&lt;CSUpdateFileHeader&gt;True&lt;/CSUpdateFileHeader&gt;&lt;CSharpFormatDocComments&gt;True&lt;/CSharpFormatDocComments&gt;&lt;RemoveCodeRedundancies&gt;True&lt;/RemoveCodeRedundancies&gt;&lt;CSMakeAutoPropertyGetOnly&gt;True&lt;/CSMakeAutoPropertyGetOnly&gt;&lt;CSArrangeQualifiers&gt;True&lt;/CSArrangeQualifiers&gt;&lt;CSEnforceVarKeywordUsageSettings&gt;True&lt;/CSEnforceVarKeywordUsageSettings&gt;&lt;CSFixBuiltinTypeReferences&gt;True&lt;/CSFixBuiltinTypeReferences&gt;&lt;CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="False" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="True" ArrangeArgumentsStyle="False" /&gt;&lt;/Profile&gt;</s:String>

<s:String x:Key="/Default/CodeStyle/CodeCleanup/RecentlyUsedProfile/@EntryValue">EntityFramework</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue">EntityFramework</s:String>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ protected virtual Annotation SetAnnotation([NotNull] string name, [NotNull] Anno

_annotations.Value[name] = annotation;

return OnAnnotationSet(name, annotation, oldAnnotation);
return oldAnnotation != null
&& oldAnnotation.Value.Equals(annotation.Value)
? annotation
: OnAnnotationSet(name, annotation, oldAnnotation);
}

/// <summary>
Expand Down
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);
}
}
52 changes: 52 additions & 0 deletions src/Microsoft.EntityFrameworkCore/Internal/Reference.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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;

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([CanBeNull] 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,10 +2,12 @@
// 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.Conventions.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;

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 @@ -6,6 +6,7 @@
using JetBrains.Annotations;
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 @@ -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
Loading

0 comments on commit cc24376

Please sign in to comment.