Skip to content

Commit

Permalink
Create explicit index object for Keys & ForeignKeys
Browse files Browse the repository at this point in the history
  • Loading branch information
smitpatel committed Nov 12, 2015
1 parent 44ae307 commit f5df44f
Show file tree
Hide file tree
Showing 34 changed files with 488 additions and 274 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ protected virtual void GenerateEntityType(

GenerateKeys(entityType.GetDeclaredKeys(), entityType.FindDeclaredPrimaryKey(), stringBuilder);

GenerateIndexes(entityType.GetDeclaredIndexes(), stringBuilder);
GenerateIndexes(entityType.GetDeclaredIndexes().Except(entityType.GetKeys().Select(key => key.Index)), stringBuilder);

GenerateEntityTypeAnnotations(entityType, stringBuilder);
}
Expand Down
5 changes: 5 additions & 0 deletions src/EntityFramework.Core/Metadata/IForeignKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public interface IForeignKey : IAnnotatable
/// </summary>
INavigation PrincipalToDependent { get; }

/// <summary>
/// Gets the index object defined on the foreign key properties.
/// </summary>
IIndex Index { get; }

/// <summary>
/// Gets a value indicating whether the values assigned to the foreign key properties are unique.
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions src/EntityFramework.Core/Metadata/IKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,10 @@ public interface IKey : IAnnotatable
/// may be defined on a base type).
/// </summary>
IEntityType DeclaringEntityType { get; }

/// <summary>
/// Gets the index object defined on the key properties.
/// </summary>
IIndex Index { get; }
}
}
9 changes: 7 additions & 2 deletions src/EntityFramework.Core/Metadata/IMutableForeignKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,14 @@ public interface IMutableForeignKey : IForeignKey, IMutableAnnotatable
IMutableNavigation HasPrincipalToDependent([CanBeNull] string name);

/// <summary>
/// Gets or sets a value indicating whether the values assigned to the foreign key properties are unique.
/// Gets the index object defined on the foreign key properties.
/// </summary>
new bool? IsUnique { get; set; }
new IMutableIndex Index { get; }

/// <summary>
/// Gets a value indicating whether the values assigned to the foreign key properties are unique.
/// </summary>
new bool? IsUnique { get; }

/// <summary>
/// Gets or sets a value indicating if this relationship is required. If true, the dependent entity must always be
Expand Down
5 changes: 5 additions & 0 deletions src/EntityFramework.Core/Metadata/IMutableKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,10 @@ public interface IMutableKey : IMutableAnnotatable, IKey
/// may be defined on a base type).
/// </summary>
new IMutableEntityType DeclaringEntityType { get; }

/// <summary>
/// Gets the index object defined on the key properties.
/// </summary>
new IMutableIndex Index { get; }
}
}
5 changes: 4 additions & 1 deletion src/EntityFramework.Core/Metadata/Internal/ForeignKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ public virtual Navigation HasPrincipalToDependent([CanBeNull] string name)
public virtual EntityType DeclaringEntityType { get; }
public virtual EntityType PrincipalEntityType { get; }

public virtual bool? IsUnique { get; set; }
public virtual Index Index => DeclaringEntityType.FindIndex(Properties);
public virtual bool? IsUnique => Index?.IsUnique;
protected virtual bool DefaultIsUnique => false;

public virtual bool? IsRequired
Expand Down Expand Up @@ -190,6 +191,8 @@ public virtual EntityType ResolveEntityTypeInHierarchy([NotNull] EntityType enti
IMutableNavigation IMutableForeignKey.PrincipalToDependent => PrincipalToDependent;
IMutableNavigation IMutableForeignKey.HasPrincipalToDependent(string name) => HasPrincipalToDependent(name);

IIndex IForeignKey.Index => Index;
IMutableIndex IMutableForeignKey.Index => Index;
bool IForeignKey.IsUnique => IsUnique ?? DefaultIsUnique;
bool IForeignKey.IsRequired => IsRequired ?? DefaultIsRequired;
DeleteBehavior IForeignKey.DeleteBehavior => DeleteBehavior ?? DefaultDeleteBehavior;
Expand Down
3 changes: 3 additions & 0 deletions src/EntityFramework.Core/Metadata/Internal/Index.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using JetBrains.Annotations;
using Microsoft.Data.Entity.Utilities;

Expand Down Expand Up @@ -49,5 +50,7 @@ public virtual ConfigurationSource UpdateConfigurationSource(ConfigurationSource

[UsedImplicitly]
private string DebuggerDisplay => Property.Format(Properties);

public virtual bool IsInUse() => (DeclaringEntityType.FindKey(Properties) != null) || DeclaringEntityType.FindForeignKeys(Properties).Any();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,13 @@ public virtual InternalKeyBuilder HasKey([NotNull] IReadOnlyList<PropertyInfo> c
=> HasKey(GetOrCreateProperties(clrProperties, configurationSource), configurationSource);

private InternalKeyBuilder HasKey(IReadOnlyList<Property> properties, ConfigurationSource configurationSource)
=> properties == null
? null
: GetOrAdd(
{
if (properties == null)
{
return null;
}

var keyBuilder = GetOrAdd(
GetOrCreateProperties(properties, configurationSource),
Metadata.FindDeclaredKey,
(e, c) => e.UpdateConfigurationSource(c),
Expand All @@ -97,6 +101,17 @@ private InternalKeyBuilder HasKey(IReadOnlyList<Property> properties, Configurat
configurationSource,
onNewKeyAdded: ModelBuilder.ConventionDispatcher.OnKeyAdded);

if (keyBuilder != null)
{
ModelBuilder.Entity(keyBuilder.Metadata.DeclaringEntityType.Name, ConfigurationSource.Convention)
.HasIndex(keyBuilder.Metadata.Properties, configurationSource)
.IsUnique(true, configurationSource);
}

return keyBuilder;
}


public virtual ConfigurationSource? RemoveKey(
[NotNull] Key key, ConfigurationSource configurationSource, bool canOverrideSameSource = true)
{
Expand All @@ -113,6 +128,11 @@ private InternalKeyBuilder HasKey(IReadOnlyList<Property> properties, Configurat
Debug.Assert(removed.HasValue);
}

if (key.Index != null)
{
RemoveIndex(key.Index, configurationSource);
}

var removedKey = Metadata.RemoveKey(key.Properties);
if (removedKey == null)
{
Expand Down Expand Up @@ -600,12 +620,6 @@ private Dictionary<EntityType, List<RelationshipSnapshot>> GroupRelationshipsByT
return null;
}

foreach (var index in Metadata.GetIndexes().Where(i => i.Properties.Contains(property)).ToList())
{
var removed = RemoveIndex(index, configurationSource);
Debug.Assert(removed.HasValue);
}

var detachedRelationships = property.FindContainingForeignKeys().ToList()
.Select(DetachRelationship).ToList();

Expand All @@ -617,6 +631,12 @@ private Dictionary<EntityType, List<RelationshipSnapshot>> GroupRelationshipsByT
Debug.Assert(removed.HasValue);
}

foreach (var index in Metadata.GetIndexes().Where(i => i.Properties.Contains(property)).ToList())
{
var removed = RemoveIndex(index, configurationSource);
Debug.Assert(removed.HasValue);
}

if (Metadata.GetProperties().Contains(property))
{
var removedProperty = Metadata.RemoveProperty(property.Name);
Expand All @@ -636,10 +656,11 @@ private RelationshipBuilderSnapshot DetachRelationship([NotNull] ForeignKey fore
var navigationToPrincipalName = foreignKey.DependentToPrincipal?.Name;
var navigationToDependentName = foreignKey.PrincipalToDependent?.Name;
var relationshipBuilder = foreignKey.Builder;
var indexBuilder = foreignKey.Index?.Builder;
var relationshipConfigurationSource = RemoveForeignKey(foreignKey, ConfigurationSource.Explicit, runConventions: false);
Debug.Assert(relationshipConfigurationSource != null);

return new RelationshipBuilderSnapshot(relationshipBuilder, navigationToPrincipalName, navigationToDependentName, relationshipConfigurationSource.Value);
return new RelationshipBuilderSnapshot(relationshipBuilder, navigationToPrincipalName, navigationToDependentName, indexBuilder, relationshipConfigurationSource.Value);
}

public virtual ConfigurationSource? RemoveForeignKey([NotNull] ForeignKey foreignKey, ConfigurationSource configurationSource)
Expand Down Expand Up @@ -670,7 +691,13 @@ private RelationshipBuilderSnapshot DetachRelationship([NotNull] ForeignKey fore
var removedForeignKey = Metadata.RemoveForeignKey(foreignKey);
Debug.Assert(removedForeignKey == foreignKey);

RemoveShadowPropertiesIfUnused(foreignKey.Properties);
var index = foreignKey.Index;
if (index != null && !index.IsInUse())
{
index.DeclaringEntityType.Builder.RemoveIndex(index, configurationSource);
}

RemoveShadowPropertiesIfUnused(foreignKey.Properties.Where(p => p.DeclaringEntityType.FindDeclaredProperty(p.Name) != null).ToList());
foreignKey.PrincipalKey.DeclaringEntityType.Builder?.RemoveKeyIfUnused(foreignKey.PrincipalKey);

if (runConventions)
Expand Down Expand Up @@ -980,6 +1007,11 @@ private InternalRelationshipBuilder CreateRelationshipBuilder(
value = ModelBuilder.ConventionDispatcher.OnForeignKeyAdded(value);
}

if (value != null)
{
HasIndex(value.Metadata.Properties, configurationSource);
}

return value;
}

Expand Down Expand Up @@ -1372,23 +1404,27 @@ public RelationshipBuilderSnapshot(
InternalRelationshipBuilder relationship,
string navigationToPrincipalName,
string navigationToDependentName,
InternalIndexBuilder indexBuilder,
ConfigurationSource relationshipConfigurationSource)
{
Relationship = relationship;
RelationshipConfigurationSource = relationshipConfigurationSource;
NavigationToPrincipalName = navigationToPrincipalName;
NavigationToDependentName = navigationToDependentName;
IndexBuilder = indexBuilder;
}

private InternalRelationshipBuilder Relationship { get; }
private ConfigurationSource RelationshipConfigurationSource { get; }
private string NavigationToPrincipalName { get; }
private string NavigationToDependentName { get; }
private InternalIndexBuilder IndexBuilder { get; }

public InternalRelationshipBuilder Attach()
=> Relationship.Attach(
NavigationToPrincipalName,
NavigationToDependentName,
IndexBuilder,
RelationshipConfigurationSource);
}
}
Expand Down
86 changes: 72 additions & 14 deletions src/EntityFramework.Core/Metadata/Internal/InternalIndexBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,82 @@ public InternalIndexBuilder([NotNull] Index index, [NotNull] InternalModelBuilde
{
}

public virtual bool IsUnique(bool? isUnique, ConfigurationSource configurationSource)
public virtual bool IsUnique(bool unique, ConfigurationSource configurationSource)
=> IsUnique(unique, configurationSource, runConventions: true);

public virtual bool IsUnique(bool unique, ConfigurationSource configurationSource, bool runConventions)
{
if (configurationSource.CanSet(_isUniqueConfigurationSource, Metadata.IsUnique.HasValue)
|| Metadata.IsUnique.Value == isUnique)
if (!CanSetUnique(unique, configurationSource))
{
if (_isUniqueConfigurationSource == null
&& Metadata.IsUnique != null)
{
_isUniqueConfigurationSource = ConfigurationSource.Explicit;
}
else
return false;
}

var foreignKeys = Metadata.DeclaringEntityType.FindForeignKeys(Metadata.Properties);
foreach (var foreignKey in foreignKeys)
{
var relationshipBuilder = foreignKey.Builder;
if (foreignKey.PrincipalToDependent != null
&& !Navigation.IsCompatible(
foreignKey.PrincipalToDependent.Name,
foreignKey.PrincipalEntityType,
foreignKey.DeclaringEntityType,
shouldBeCollection: !unique,
shouldThrow: false))
{
_isUniqueConfigurationSource = configurationSource.Max(_isUniqueConfigurationSource);
relationshipBuilder.Navigation(null, /* pointsToPrincipal: */ false, configurationSource, runConventions);
}
}

var newIndexBuilder = Metadata.DeclaringEntityType.Builder.HasIndex(Metadata.Properties.Select(p => p.Name).ToList(), ConfigurationSource.Convention);

if (newIndexBuilder.Metadata != Metadata)
{
return newIndexBuilder.IsUnique(unique, configurationSource);
}

if (_isUniqueConfigurationSource == null
&& Metadata.IsUnique != null)
{
_isUniqueConfigurationSource = ConfigurationSource.Explicit;
}
else
{
_isUniqueConfigurationSource = configurationSource.Max(_isUniqueConfigurationSource);
}
Metadata.IsUnique = unique;
return true;
}

Metadata.IsUnique = isUnique;
public virtual bool CanSetUnique(bool unique, ConfigurationSource configurationSource)
{
if (((IIndex)Metadata).IsUnique == unique)
{
return true;
}

return false;
if (!configurationSource.CanSet(_isUniqueConfigurationSource, Metadata.IsUnique.HasValue))
{
return false;
}

var foreignKeys = Metadata.DeclaringEntityType.FindForeignKeys(Metadata.Properties);
foreach (var foreignKey in foreignKeys)
{
var relationshipBuilder = foreignKey.Builder;
if (foreignKey.PrincipalToDependent != null
&& !relationshipBuilder.CanSetNavigation(null, /* pointsToPrincipal */ false, configurationSource)
&& !Navigation.IsCompatible(
foreignKey.PrincipalToDependent.Name,
foreignKey.PrincipalEntityType,
foreignKey.DeclaringEntityType,
shouldBeCollection: !unique,
shouldThrow: false))
{
return false;
}
}

return true;
}

public virtual InternalIndexBuilder Attach(ConfigurationSource configurationSource)
Expand All @@ -46,12 +102,14 @@ public virtual InternalIndexBuilder Attach(ConfigurationSource configurationSour

newIndexBuilder.MergeAnnotationsFrom(this);

if (_isUniqueConfigurationSource.HasValue)
if (_isUniqueConfigurationSource.HasValue && Metadata.IsUnique.HasValue)
{
newIndexBuilder.IsUnique(Metadata.IsUnique, _isUniqueConfigurationSource.Value);
newIndexBuilder.IsUnique(Metadata.IsUnique.Value, _isUniqueConfigurationSource.Value);
}

return newIndexBuilder;
}

public ConfigurationSource? GetIsUniqueConfigurationSource() => _isUniqueConfigurationSource;
}
}
Loading

0 comments on commit f5df44f

Please sign in to comment.