Skip to content

Commit

Permalink
Metadata: Add overloads for configuring shared type as owned type
Browse files Browse the repository at this point in the history
- Also fix issue in propery mapping convention to exclude "shadow" properties on property bag which are no longer "shadow"

Resolves #21892
  • Loading branch information
smitpatel committed Aug 5, 2020
1 parent 40d99f0 commit 077cc7b
Show file tree
Hide file tree
Showing 12 changed files with 1,244 additions and 95 deletions.
2 changes: 1 addition & 1 deletion src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ protected virtual void ValidatePropertyMapping(
{
var unmappedProperty = entityType.GetProperties().FirstOrDefault(
p => (!ConfigurationSource.Convention.Overrides(p.GetConfigurationSource())
|| !p.IsShadowProperty())
|| !(p.IsShadowProperty() || (p.DeclaringEntityType.IsPropertyBag && p.IsIndexerProperty())))
&& p.FindTypeMapping() == null);

if (unmappedProperty != null)
Expand Down
167 changes: 159 additions & 8 deletions src/EFCore/Metadata/Builders/EntityTypeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,39 @@ public virtual OwnedNavigationBuilder OwnsOne(
[NotNull] string navigationName)
=> OwnsOneBuilder(
new TypeIdentity(Check.NotEmpty(ownedTypeName, nameof(ownedTypeName))),
clrType: null,
Check.NotEmpty(navigationName, nameof(navigationName)));

/// <summary>
/// <para>
/// Configures a relationship where the target entity is owned by (or part of) this entity.
/// </para>
/// <para>
/// The target entity type for each ownership relationship is treated as a different entity type
/// even if the navigation is of the same type. Configuration of the target entity type
/// isn't applied to the target entity type of other ownership relationships.
/// </para>
/// <para>
/// Most operations on an owned entity require accessing it through the owner entity using the corresponding navigation.
/// </para>
/// <para>
/// After calling this method, you should chain a call to
/// <see cref="OwnedNavigationBuilder.WithOwner" /> to fully configure the relationship.
/// </para>
/// </summary>
/// <param name="ownedTypeName"> The name of the entity type that this relationship targets. </param>
/// <param name="ownedType"> The CLR type of the entity type that this relationship targets. </param>
/// <param name="navigationName">
/// The name of the reference navigation property on this entity type that represents the relationship.
/// </param>
/// <returns> An object that can be used to configure the owned type and the relationship. </returns>
public virtual OwnedNavigationBuilder OwnsOne(
[NotNull] string ownedTypeName,
[NotNull] Type ownedType,
[NotNull] string navigationName)
=> OwnsOneBuilder(
new TypeIdentity(Check.NotEmpty(ownedTypeName, nameof(ownedTypeName))),
Check.NotNull(ownedType, nameof(ownedType)),
Check.NotEmpty(navigationName, nameof(navigationName)));

/// <summary>
Expand Down Expand Up @@ -342,6 +375,7 @@ public virtual OwnedNavigationBuilder OwnsOne(
[NotNull] string navigationName)
=> OwnsOneBuilder(
new TypeIdentity(Check.NotNull(ownedType, nameof(ownedType)), (Model)Metadata.Model),
clrType: null,
Check.NotEmpty(navigationName, nameof(navigationName)));

/// <summary>
Expand Down Expand Up @@ -376,7 +410,47 @@ public virtual EntityTypeBuilder OwnsOne(
Check.NotEmpty(navigationName, nameof(navigationName));
Check.NotNull(buildAction, nameof(buildAction));

buildAction(OwnsOneBuilder(new TypeIdentity(ownedTypeName), navigationName));
buildAction(OwnsOneBuilder(new TypeIdentity(ownedTypeName), clrType: null, navigationName));
return this;
}

/// <summary>
/// <para>
/// Configures a relationship where the target entity is owned by (or part of) this entity.
/// </para>
/// <para>
/// The target entity type for each ownership relationship is treated as a different entity type
/// even if the navigation is of the same type. Configuration of the target entity type
/// isn't applied to the target entity type of other ownership relationships.
/// </para>
/// <para>
/// Most operations on an owned entity require accessing it through the owner entity using the corresponding navigation.
/// </para>
/// <para>
/// After calling this method, you should chain a call to
/// <see cref="OwnedNavigationBuilder.WithOwner" /> to fully configure the relationship.
/// </para>
/// </summary>
/// <param name="ownedTypeName"> The name of the entity type that this relationship targets. </param>
/// <param name="ownedType"> The CLR type of the entity type that this relationship targets. </param>
/// <param name="navigationName">
/// The name of the reference navigation property on this entity type that represents the relationship.
/// </param>
/// <param name="buildAction"> An action that performs configuration of the owned type and the relationship. </param>
/// <returns> An object that can be used to configure the entity type. </returns>
public virtual EntityTypeBuilder OwnsOne(
[NotNull] string ownedTypeName,
[NotNull] Type ownedType,
[NotNull] string navigationName,
[NotNull] Action<OwnedNavigationBuilder> buildAction)
{
Check.NotEmpty(ownedTypeName, nameof(ownedTypeName));
Check.NotNull(ownedType, nameof(ownedType));
Check.NotNull(ownedType, nameof(ownedType));
Check.NotEmpty(navigationName, nameof(navigationName));
Check.NotNull(buildAction, nameof(buildAction));

buildAction(OwnsOneBuilder(new TypeIdentity(ownedTypeName), ownedType, navigationName));
return this;
}

Expand Down Expand Up @@ -412,17 +486,19 @@ public virtual EntityTypeBuilder OwnsOne(
Check.NotEmpty(navigationName, nameof(navigationName));
Check.NotNull(buildAction, nameof(buildAction));

buildAction(OwnsOneBuilder(new TypeIdentity(ownedType, (Model)Metadata.Model), navigationName));
buildAction(OwnsOneBuilder(new TypeIdentity(ownedType, (Model)Metadata.Model), clrType: null, navigationName));
return this;
}

private OwnedNavigationBuilder OwnsOneBuilder(in TypeIdentity ownedType, string navigationName)
private OwnedNavigationBuilder OwnsOneBuilder(in TypeIdentity ownedType, Type clrType, string navigationName)
{
InternalForeignKeyBuilder relationship;
using (Builder.Metadata.Model.ConventionDispatcher.DelayConventions())
{
relationship = ownedType.Type == null
? Builder.HasOwnership(ownedType.Name, navigationName, ConfigurationSource.Explicit)
? clrType == null
? Builder.HasOwnership(ownedType.Name, navigationName, ConfigurationSource.Explicit)
: Builder.HasOwnership(ownedType.Name, clrType, MemberIdentity.Create(navigationName), ConfigurationSource.Explicit)
: Builder.HasOwnership(ownedType.Type, navigationName, ConfigurationSource.Explicit);
relationship.IsUnique(true, ConfigurationSource.Explicit);
}
Expand Down Expand Up @@ -460,6 +536,39 @@ public virtual OwnedNavigationBuilder OwnsMany(
[NotNull] string navigationName)
=> OwnsManyBuilder(
new TypeIdentity(Check.NotEmpty(ownedTypeName, nameof(ownedTypeName))),
clrType: null,
Check.NotEmpty(navigationName, nameof(navigationName)));

/// <summary>
/// <para>
/// Configures a relationship where the target entity is owned by (or part of) this entity.
/// </para>
/// <para>
/// The target entity type for each ownership relationship is treated as a different entity type
/// even if the navigation is of the same type. Configuration of the target entity type
/// isn't applied to the target entity type of other ownership relationships.
/// </para>
/// <para>
/// Most operations on an owned entity require accessing it through the owner entity using the corresponding navigation.
/// </para>
/// <para>
/// After calling this method, you should chain a call to
/// <see cref="OwnedNavigationBuilder.WithOwner" /> to fully configure the relationship.
/// </para>
/// </summary>
/// <param name="ownedTypeName"> The name of the entity type that this relationship targets. </param>
/// <param name="ownedType"> The CLR type of the entity type that this relationship targets. </param>
/// <param name="navigationName">
/// The name of the reference navigation property on this entity type that represents the relationship.
/// </param>
/// <returns> An object that can be used to configure the owned type and the relationship. </returns>
public virtual OwnedNavigationBuilder OwnsMany(
[NotNull] string ownedTypeName,
[NotNull] Type ownedType,
[NotNull] string navigationName)
=> OwnsManyBuilder(
new TypeIdentity(Check.NotEmpty(ownedTypeName, nameof(ownedTypeName))),
Check.NotNull(ownedType, nameof(ownedType)),
Check.NotEmpty(navigationName, nameof(navigationName)));

/// <summary>
Expand Down Expand Up @@ -489,6 +598,7 @@ public virtual OwnedNavigationBuilder OwnsMany(
[NotNull] string navigationName)
=> OwnsManyBuilder(
new TypeIdentity(Check.NotNull(ownedType, nameof(ownedType)), (Model)Metadata.Model),
clrType: null,
Check.NotEmpty(navigationName, nameof(navigationName)));

/// <summary>
Expand Down Expand Up @@ -523,7 +633,46 @@ public virtual EntityTypeBuilder OwnsMany(
Check.NotEmpty(navigationName, nameof(navigationName));
Check.NotNull(buildAction, nameof(buildAction));

buildAction(OwnsManyBuilder(new TypeIdentity(ownedTypeName), navigationName));
buildAction(OwnsManyBuilder(new TypeIdentity(ownedTypeName), clrType: null, navigationName));
return this;
}

/// <summary>
/// <para>
/// Configures a relationship where the target entity is owned by (or part of) this entity.
/// </para>
/// <para>
/// The target entity type for each ownership relationship is treated as a different entity type
/// even if the navigation is of the same type. Configuration of the target entity type
/// isn't applied to the target entity type of other ownership relationships.
/// </para>
/// <para>
/// Most operations on an owned entity require accessing it through the owner entity using the corresponding navigation.
/// </para>
/// <para>
/// After calling this method, you should chain a call to
/// <see cref="OwnedNavigationBuilder.WithOwner" /> to fully configure the relationship.
/// </para>
/// </summary>
/// <param name="ownedTypeName"> The name of the entity type that this relationship targets. </param>
/// <param name="ownedType"> The CLR type of the entity type that this relationship targets. </param>
/// <param name="navigationName">
/// The name of the reference navigation property on this entity type that represents the relationship.
/// </param>
/// <param name="buildAction"> An action that performs configuration of the owned type and the relationship. </param>
/// <returns> An object that can be used to configure the entity type. </returns>
public virtual EntityTypeBuilder OwnsMany(
[NotNull] string ownedTypeName,
[NotNull] Type ownedType,
[NotNull] string navigationName,
[NotNull] Action<OwnedNavigationBuilder> buildAction)
{
Check.NotEmpty(ownedTypeName, nameof(ownedTypeName));
Check.NotNull(ownedType, nameof(ownedType));
Check.NotEmpty(navigationName, nameof(navigationName));
Check.NotNull(buildAction, nameof(buildAction));

buildAction(OwnsManyBuilder(new TypeIdentity(ownedTypeName), ownedType, navigationName));
return this;
}

Expand Down Expand Up @@ -559,17 +708,19 @@ public virtual EntityTypeBuilder OwnsMany(
Check.NotEmpty(navigationName, nameof(navigationName));
Check.NotNull(buildAction, nameof(buildAction));

buildAction(OwnsManyBuilder(new TypeIdentity(ownedType, (Model)Metadata.Model), navigationName));
buildAction(OwnsManyBuilder(new TypeIdentity(ownedType, (Model)Metadata.Model), clrType: null, navigationName));
return this;
}

private OwnedNavigationBuilder OwnsManyBuilder(in TypeIdentity ownedType, string navigationName)
private OwnedNavigationBuilder OwnsManyBuilder(in TypeIdentity ownedType, Type clrType, string navigationName)
{
InternalForeignKeyBuilder relationship;
using (Builder.Metadata.Model.ConventionDispatcher.DelayConventions())
{
relationship = ownedType.Type == null
? Builder.HasOwnership(ownedType.Name, navigationName, ConfigurationSource.Explicit)
? clrType == null
? Builder.HasOwnership(ownedType.Name, navigationName, ConfigurationSource.Explicit)
: Builder.HasOwnership(ownedType.Name, clrType, MemberIdentity.Create(navigationName), ConfigurationSource.Explicit)
: Builder.HasOwnership(ownedType.Type, navigationName, ConfigurationSource.Explicit);
relationship.IsUnique(false, ConfigurationSource.Explicit);
}
Expand Down
Loading

0 comments on commit 077cc7b

Please sign in to comment.