Skip to content

Commit

Permalink
Metadata: Fix few Shared Type entity type bugs
Browse files Browse the repository at this point in the history
- Add DefaultPropertyBagType in shared type by convention
- Throw exception message for nonconfigured navigation pointing to shared type
- Skip Shared type navigation when searching for FKAttribute on property
  • Loading branch information
smitpatel committed Jul 30, 2020
1 parent f7fddae commit 12d75ce
Show file tree
Hide file tree
Showing 21 changed files with 459 additions and 427 deletions.
7 changes: 7 additions & 0 deletions src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,13 @@ protected virtual void ValidatePropertyMapping(
continue;
}

if (model.IsShared(propertyType)
|| (targetSequenceType != null && model.IsShared(targetSequenceType)))
{
throw new InvalidOperationException(
CoreStrings.NonconfiguredNavigationToSharedType(actualProperty.Name, entityType.DisplayName()));
}

var targetType = FindCandidateNavigationPropertyType(actualProperty);

var isTargetWeakOrOwned
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ private MemberInfo FindForeignKeyAttributeOnProperty(IConventionEntityType entit

if (attribute.Name != navigationName
|| (memberInfo is PropertyInfo propertyInfo
&& FindCandidateNavigationPropertyType(propertyInfo) != null))
&& (FindCandidateNavigationPropertyType(propertyInfo) != null
|| IsNavigationToSharedType(entityType.Model, propertyInfo))))
{
continue;
}
Expand Down Expand Up @@ -374,6 +375,11 @@ private MemberInfo FindForeignKeyAttributeOnProperty(IConventionEntityType entit
private Type FindCandidateNavigationPropertyType([NotNull] PropertyInfo propertyInfo)
=> Dependencies.MemberClassifier.FindCandidateNavigationPropertyType(propertyInfo);

private bool IsNavigationToSharedType(IConventionModel model, PropertyInfo propertyInfo)
=> model.IsShared(propertyInfo.PropertyType)
|| (propertyInfo.PropertyType.TryGetSequenceType() is Type elementType
&& model.IsShared(elementType));

private static IReadOnlyList<string> FindCandidateDependentPropertiesThroughNavigation(
IConventionForeignKeyBuilder relationshipBuilder,
bool pointsToPrincipal)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public virtual ConventionSet CreateConventionSet()
conventionSet.SkipNavigationInverseChangedConventions.Add(manyToManyJoinEntityTypeConvention);

conventionSet.SkipNavigationForeignKeyChangedConventions.Add(manyToManyJoinEntityTypeConvention);
conventionSet.SkipNavigationForeignKeyChangedConventions.Add(keyDiscoveryConvention);
conventionSet.SkipNavigationForeignKeyChangedConventions.Add(keyDiscoveryConvention);

conventionSet.NavigationRemovedConventions.Add(relationshipDiscoveryConvention);

Expand Down
3 changes: 2 additions & 1 deletion src/EFCore/Metadata/Internal/Model.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ private readonly SortedDictionary<string, SortedSet<EntityType>> _entityTypesWit
private readonly Dictionary<string, ConfigurationSource> _ignoredTypeNames
= new Dictionary<string, ConfigurationSource>(StringComparer.Ordinal);

private readonly Dictionary<Type, ConfigurationSource> _sharedTypes = new Dictionary<Type, ConfigurationSource>();
private readonly Dictionary<Type, ConfigurationSource> _sharedTypes =
new Dictionary<Type, ConfigurationSource> { { DefaultPropertyBagType, ConfigurationSource.Convention } };

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
8 changes: 8 additions & 0 deletions src/EFCore/Properties/CoreStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/EFCore/Properties/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1444,4 +1444,7 @@
<data name="SharedTypeDerivedType" xml:space="preserve">
<value>The shared type entity type '{entityType}' cannot have a base type.</value>
</data>
<data name="NonconfiguredNavigationToSharedType" xml:space="preserve">
<value>The navigation '{navigation}' on '{entityType}' must be configured using Fluent API with an explicit name for the target shared type entity type or excluded by calling 'EntityTypeBuilder.Ignore'.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -147,31 +147,6 @@ public IReadOnlyDictionary<Type, object> GetEntityAsserters()

protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
{
modelBuilder.SharedTypeEntity<Dictionary<string, object>>(
"JoinOneToTwoShared",
b =>
{
b.IndexerProperty<int>("OneId");
b.IndexerProperty<int>("TwoId");
});

modelBuilder.SharedTypeEntity<Dictionary<string, object>>(
"JoinOneToThreePayloadFullShared",
b =>
{
b.IndexerProperty<int>("OneId");
b.IndexerProperty<int>("ThreeId");
b.IndexerProperty<string>("Payload");
});

modelBuilder.SharedTypeEntity<Dictionary<string, object>>(
"JoinTwoSelfShared",
b =>
{
b.IndexerProperty<int>("LeftId");
b.IndexerProperty<int>("RightId");
});

modelBuilder.Entity<EntityOne>().Property(e => e.Id).ValueGeneratedNever();
modelBuilder.Entity<EntityTwo>().Property(e => e.Id).ValueGeneratedNever();
modelBuilder.Entity<EntityThree>().Property(e => e.Id).ValueGeneratedNever();
Expand Down Expand Up @@ -209,24 +184,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
.HasKey(e => new { e.OneId, e.ThreeId });

// Nav:2 Payload:No Join:Shared Extra:None
modelBuilder.Entity<EntityOne>()
.HasMany(e => e.TwoSkipShared)
.WithMany(e => e.OneSkipShared)
.UsingEntity<Dictionary<string, object>>(
"JoinOneToTwoShared",
r => r.HasOne<EntityTwo>().WithMany().HasForeignKey("TwoId"),
l => l.HasOne<EntityOne>().WithMany().HasForeignKey("OneId"))
.HasKey("OneId", "TwoId");
modelBuilder.Entity<EntityOne>().HasMany(e => e.TwoSkipShared).WithMany(e => e.OneSkipShared);

// Nav:4 Payload:Yes Join:Shared Extra:None
modelBuilder.Entity<EntityOne>()
.HasMany(e => e.ThreeSkipPayloadFullShared)
.WithMany(e => e.OneSkipPayloadFullShared)
.UsingEntity<Dictionary<string, object>>(
"JoinOneToThreePayloadFullShared",
r => r.HasOne<EntityThree>().WithMany(/*"JoinOnePayloadFullShared"*/).HasForeignKey("ThreeId"), // #13729
l => l.HasOne<EntityOne>().WithMany(/*"JoinThreePayloadFullShared"*/).HasForeignKey("OneId")) // #13729
.HasKey("OneId", "ThreeId");
r => r.HasOne<EntityThree>().WithMany(e => e.JoinOnePayloadFullShared).HasForeignKey("ThreeId"),
l => l.HasOne<EntityOne>().WithMany(e => e.JoinThreePayloadFullShared).HasForeignKey("OneId"))
.IndexerProperty<string>("Payload");

// Nav:6 Payload:Yes Join:Concrete Extra:Self-Ref
modelBuilder.Entity<EntityOne>()
Expand Down Expand Up @@ -272,8 +240,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
.UsingEntity<Dictionary<string, object>>(
"JoinTwoSelfShared",
l => l.HasOne<EntityTwo>().WithMany().HasForeignKey("LeftId"),
r => r.HasOne<EntityTwo>().WithMany().OnDelete(DeleteBehavior.NoAction).HasForeignKey("RightId"))
.HasKey("LeftId", "RightId");
r => r.HasOne<EntityTwo>().WithMany().HasForeignKey("RightId").OnDelete(DeleteBehavior.NoAction));

// TODO: convert to shared type
// Nav:2 Payload:No Join:Shared Extra:CompositeKey
Expand All @@ -294,15 +261,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
r => r.HasOne(x => x.Three).WithMany(x => x.JoinCompositeKeyFull))
.HasKey(e => new { e.ThreeId, e.CompositeId1, e.CompositeId2, e.CompositeId3 });

// TODO: convert to shared type
// Nav:2 Payload:No Join:Shared Extra:Inheritance
modelBuilder.Entity<EntityThree>()
.HasMany(e => e.RootSkipShared)
.WithMany(e => e.ThreeSkipShared)
.UsingEntity<JoinThreeToRootShared>(
r => r.HasOne<EntityRoot>().WithMany().HasForeignKey(e => e.RootId),
l => l.HasOne<EntityThree>().WithMany().HasForeignKey(e => e.ThreeId))
.HasKey(e => new { e.ThreeId, e.RootId });
modelBuilder.Entity<EntityThree>().HasMany(e => e.RootSkipShared).WithMany(e => e.ThreeSkipShared);

// TODO: convert to shared type
// Nav:2 Payload:No Join:Shared Extra:Inheritance,CompositeKey
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ public class EntityOne

public virtual ICollection<EntityThree> ThreeSkipPayloadFullShared { get; } = new ObservableCollection<EntityThree>(); // #21684

// public virtual ICollection<Dictionary<string, object>> JoinThreePayloadFullShared { get; } // #13729
// = new ObservableCollection<Dictionary<string, object>>(); // #21684
public virtual ICollection<Dictionary<string, object>> JoinThreePayloadFullShared { get; }
= new ObservableCollection<Dictionary<string, object>>(); // #21684

public virtual ICollection<EntityOne> SelfSkipPayloadLeft { get; } = new ObservableCollection<EntityOne>(); // #21684

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public class EntityThree

public virtual ICollection<EntityOne> OneSkipPayloadFullShared { get; } = new ObservableCollection<EntityOne>(); // #21684

// public virtual ICollection<Dictionary<string, object>> JoinOnePayloadFullShared { get; } // #13729
// = new ObservableCollection<Dictionary<string, object>>(); // #21684
public virtual ICollection<Dictionary<string, object>> JoinOnePayloadFullShared { get; }
= new ObservableCollection<Dictionary<string, object>>(); // #21684

public virtual ICollection<EntityCompositeKey> CompositeKeySkipFull { get; }
= new ObservableCollection<EntityCompositeKey>(); // #21684
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations.Schema;

namespace Microsoft.EntityFrameworkCore.TestModels.ManyToManyModel
{
public class ImplicitManyToManyA
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public virtual int Id { get; set; }
public virtual string Name { get; set; }

public virtual ICollection<ImplicitManyToManyB> Bs { get; } = new ObservableCollection<ImplicitManyToManyB>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations.Schema;

namespace Microsoft.EntityFrameworkCore.TestModels.ManyToManyModel
{
public class ImplicitManyToManyB
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public virtual int Id { get; set; }
public virtual string Name { get; set; }

public virtual ICollection<ImplicitManyToManyA> As { get; } = new ObservableCollection<ImplicitManyToManyA>();
}
}
Loading

0 comments on commit 12d75ce

Please sign in to comment.