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 10, 2015
1 parent 76f0912 commit 5cd1f5a
Show file tree
Hide file tree
Showing 33 changed files with 466 additions and 269 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
16 changes: 16 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,20 @@ public virtual ConfigurationSource UpdateConfigurationSource(ConfigurationSource

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

public virtual bool NotInUse()
{
if (DeclaringEntityType.FindKey(Properties) != null)
{
return false;
}

if (DeclaringEntityType.FindForeignKeys(Properties).Any())
{
return false;
}

return true;
}
}
}
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,15 +620,15 @@ private Dictionary<EntityType, List<RelationshipSnapshot>> GroupRelationshipsByT
return null;
}

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

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();

foreach (var key in Metadata.GetKeys().Where(i => i.Properties.Contains(property)).ToList())
{
detachedRelationships.AddRange(key.FindReferencingForeignKeys().ToList()
Expand Down Expand Up @@ -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.NotInUse())
{
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
85 changes: 71 additions & 14 deletions src/EntityFramework.Core/Metadata/Internal/InternalIndexBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,95 @@ 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 isUnique, ConfigurationSource configurationSource, bool runConventions)
{
if (configurationSource.CanSet(_isUniqueConfigurationSource, Metadata.IsUnique.HasValue)
|| Metadata.IsUnique.Value == isUnique)
if (!CanSetUnique(isUnique, 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: !isUnique,
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(isUnique, configurationSource);
}

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

Metadata.IsUnique = isUnique;
public virtual bool CanSetUnique(bool isUnique, ConfigurationSource configurationSource)
{
if (((IIndex)Metadata).IsUnique == isUnique)
{
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: !isUnique,
shouldThrow: false))
{
return false;
}
}

return true;
}


public virtual InternalIndexBuilder Attach(ConfigurationSource configurationSource)
{
var entityTypeBuilder = ModelBuilder.Entity(Metadata.DeclaringEntityType.Name, ConfigurationSource.Convention);
var newIndexBuilder = entityTypeBuilder.HasIndex(Metadata.Properties.Select(p => p.Name).ToList(), configurationSource);

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;
Expand Down
Loading

0 comments on commit 5cd1f5a

Please sign in to comment.