Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to configure the navigation on dependent side of Owned-types #20282

Merged
merged 2 commits into from
Mar 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/EFCore/Metadata/Builders/OwnedNavigationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,20 @@ public virtual PropertyBuilder IndexedProperty([NotNull] Type propertyType, [Not
Check.NotNull(propertyType, nameof(propertyType)),
Check.NotEmpty(propertyName, nameof(propertyName)), ConfigurationSource.Explicit).Metadata);

/// <summary>
/// <para>
/// Returns an object that can be used to configure an existing navigation property
/// from the owned type to its owner. It is an error for the navigation property
/// not to exist.
/// </para>
/// </summary>
/// <param name="navigationName"> The name of the navigation property to be configured. </param>
/// <returns> An object that can be used to configure the navigation property. </returns>
public virtual NavigationBuilder Navigation([NotNull] string navigationName)
=> new NavigationBuilder(
DependentEntityType.Builder.Navigation(
Check.NotEmpty(navigationName, nameof(navigationName))));

/// <summary>
/// Excludes the given property from the entity type. This method is typically used to remove properties
/// or navigations from the owned entity type that were added by convention.
Expand Down
17 changes: 17 additions & 0 deletions src/EFCore/Metadata/Builders/OwnedNavigationBuilder`.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,23 @@ public virtual PropertyBuilder<TProperty> Property<TProperty>(
Check.NotNull(propertyExpression, nameof(propertyExpression)).GetPropertyAccess(),
ConfigurationSource.Explicit).Metadata));

/// <summary>
/// <para>
/// Returns an object that can be used to configure an existing navigation property
/// from the owned type to its owner. It is an error for the navigation property
/// not to exist.
/// </para>
/// </summary>
/// <param name="navigationExpression">
/// A lambda expression representing the navigation property to be configured (
/// <c>blog => blog.Posts</c>).
/// </param>
/// <returns> An object that can be used to configure the navigation property. </returns>
public virtual NavigationBuilder Navigation<TNavigation>(
[NotNull] Expression<Func<TDependentEntity, TNavigation>> navigationExpression)
=> new NavigationBuilder(DependentEntityType.Builder.Navigation(
Check.NotNull(navigationExpression, nameof(navigationExpression)).GetPropertyAccess()));

/// <summary>
/// Excludes the given property from the entity type. This method is typically used to remove properties
/// or navigations from the owned entity type that were added by convention.
Expand Down
21 changes: 21 additions & 0 deletions test/EFCore.Tests/ModelBuilding/ModelBuilderGenericTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,14 @@ public override TestIndexBuilder HasIndex(params string[] propertyNames)
public override TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsOne<TRelatedEntity>(string navigationName)
=> new GenericTestOwnedNavigationBuilder<TEntity, TRelatedEntity>(EntityTypeBuilder.OwnsOne<TRelatedEntity>(navigationName));

public override TestEntityTypeBuilder<TEntity> OwnsOne<TRelatedEntity>(
string navigationName,
Action<TestOwnedNavigationBuilder<TEntity, TRelatedEntity>> buildAction)
=> Wrap(
EntityTypeBuilder.OwnsOne<TRelatedEntity>(
navigationName,
r => buildAction(new GenericTestOwnedNavigationBuilder<TEntity, TRelatedEntity>(r))));

public override TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsOne<TRelatedEntity>(
Expression<Func<TEntity, TRelatedEntity>> navigationExpression)
=> new GenericTestOwnedNavigationBuilder<TEntity, TRelatedEntity>(EntityTypeBuilder.OwnsOne(navigationExpression));
Expand All @@ -241,6 +249,13 @@ public override TestEntityTypeBuilder<TEntity> OwnsOne<TRelatedEntity>(
public override TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsMany<TRelatedEntity>(string navigationName)
=> new GenericTestOwnedNavigationBuilder<TEntity, TRelatedEntity>(
EntityTypeBuilder.OwnsMany<TRelatedEntity>(navigationName));
public override TestEntityTypeBuilder<TEntity> OwnsMany<TRelatedEntity>(
string navigationName,
Action<TestOwnedNavigationBuilder<TEntity, TRelatedEntity>> buildAction)
=> Wrap(
EntityTypeBuilder.OwnsMany<TRelatedEntity>(
navigationName,
r => buildAction(new GenericTestOwnedNavigationBuilder<TEntity, TRelatedEntity>(r))));

public override TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsMany<TRelatedEntity>(
Expression<Func<TEntity, IEnumerable<TRelatedEntity>>> navigationExpression)
Expand Down Expand Up @@ -729,6 +744,12 @@ public override TestPropertyBuilder<TProperty> Property<TProperty>(
Expression<Func<TDependentEntity, TProperty>> propertyExpression)
=> new GenericTestPropertyBuilder<TProperty>(OwnedNavigationBuilder.Property(propertyExpression));

public override TestNavigationBuilder Navigation<TNavigation>(string navigationName)
=> new GenericTestNavigationBuilder(OwnedNavigationBuilder.Navigation(navigationName));
public override TestNavigationBuilder Navigation<TNavigation>(
Expression<Func<TDependentEntity, TNavigation>> navigationExpression)
=> new GenericTestNavigationBuilder(OwnedNavigationBuilder.Navigation(navigationExpression));

public override TestOwnedNavigationBuilder<TEntity, TDependentEntity> Ignore(string propertyName)
=> Wrap(OwnedNavigationBuilder.Ignore(propertyName));

Expand Down
33 changes: 28 additions & 5 deletions test/EFCore.Tests/ModelBuilding/ModelBuilderNonGenericTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,14 @@ public override TestIndexBuilder HasIndex(params string[] propertyNames)
public override TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsOne<TRelatedEntity>(string navigationName)
=> new NonGenericTestOwnedNavigationBuilder<TEntity, TRelatedEntity>(
EntityTypeBuilder.OwnsOne(typeof(TRelatedEntity), navigationName));
public override TestEntityTypeBuilder<TEntity> OwnsOne<TRelatedEntity>(
string navigationName,
Action<TestOwnedNavigationBuilder<TEntity, TRelatedEntity>> buildAction)
=> Wrap(
EntityTypeBuilder.OwnsOne(
typeof(TRelatedEntity),
navigationName,
r => buildAction(new NonGenericTestOwnedNavigationBuilder<TEntity, TRelatedEntity>(r))));

public override TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsOne<TRelatedEntity>(
Expression<Func<TEntity, TRelatedEntity>> navigationExpression)
Expand All @@ -191,6 +199,14 @@ public override TestEntityTypeBuilder<TEntity> OwnsOne<TRelatedEntity>(
public override TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsMany<TRelatedEntity>(string navigationName)
=> new NonGenericTestOwnedNavigationBuilder<TEntity, TRelatedEntity>(
EntityTypeBuilder.OwnsMany(typeof(TRelatedEntity), navigationName));
public override TestEntityTypeBuilder<TEntity> OwnsMany<TRelatedEntity>(
string navigationName,
Action<TestOwnedNavigationBuilder<TEntity, TRelatedEntity>> buildAction)
=> Wrap(
EntityTypeBuilder.OwnsMany(
typeof(TRelatedEntity),
navigationName,
r => buildAction(new NonGenericTestOwnedNavigationBuilder<TEntity, TRelatedEntity>(r))));

public override TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsMany<TRelatedEntity>(
Expression<Func<TEntity, IEnumerable<TRelatedEntity>>> navigationExpression)
Expand Down Expand Up @@ -407,18 +423,18 @@ public override TestPropertyBuilder<TProperty> HasConversion(ValueConverter conv

protected class NonGenericTestNavigationBuilder : TestNavigationBuilder
{
public NonGenericTestNavigationBuilder(NavigationBuilder navigationIdentityBuilder)
public NonGenericTestNavigationBuilder(NavigationBuilder navigationBuilder)
{
NavigationIdentityBuilder = navigationIdentityBuilder;
NavigationBuilder = navigationBuilder;
}

private NavigationBuilder NavigationIdentityBuilder { get; }
private NavigationBuilder NavigationBuilder { get; }

public override TestNavigationBuilder HasAnnotation(string annotation, object value)
=> new NonGenericTestNavigationBuilder(NavigationIdentityBuilder.HasAnnotation(annotation, value));
=> new NonGenericTestNavigationBuilder(NavigationBuilder.HasAnnotation(annotation, value));

public override TestNavigationBuilder UsePropertyAccessMode(PropertyAccessMode propertyAccessMode)
=> new NonGenericTestNavigationBuilder(NavigationIdentityBuilder.UsePropertyAccessMode(propertyAccessMode));
=> new NonGenericTestNavigationBuilder(NavigationBuilder.UsePropertyAccessMode(propertyAccessMode));
}

protected class
Expand Down Expand Up @@ -725,6 +741,13 @@ public override TestPropertyBuilder<TProperty> Property<TProperty>(
OwnedNavigationBuilder.Property(propertyInfo.PropertyType, propertyInfo.GetSimpleMemberName()));
}

public override TestNavigationBuilder Navigation<TNavigation>(string navigationName)
=> new NonGenericTestNavigationBuilder(OwnedNavigationBuilder.Navigation(navigationName));
public override TestNavigationBuilder Navigation<TNavigation>(
Expression<Func<TDependentEntity, TNavigation>> navigationExpression)
=> new NonGenericTestNavigationBuilder(
OwnedNavigationBuilder.Navigation(navigationExpression.GetPropertyAccess().GetSimpleMemberName()));

public override TestOwnedNavigationBuilder<TEntity, TDependentEntity> Ignore(string propertyName)
=> Wrap<TEntity, TDependentEntity>(OwnedNavigationBuilder.Ignore(propertyName));

Expand Down
14 changes: 14 additions & 0 deletions test/EFCore.Tests/ModelBuilding/ModelBuilderTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ public abstract TestEntityTypeBuilder<TEntity> Ignore(

public abstract TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsOne<TRelatedEntity>(string navigationName)
where TRelatedEntity : class;
public abstract TestEntityTypeBuilder<TEntity> OwnsOne<TRelatedEntity>(
string navigationName,
Action<TestOwnedNavigationBuilder<TEntity, TRelatedEntity>> buildAction)
where TRelatedEntity : class;

public abstract TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsOne<TRelatedEntity>(
Expression<Func<TEntity, TRelatedEntity>> navigationExpression)
where TRelatedEntity : class;
Expand All @@ -219,6 +224,11 @@ public abstract TestEntityTypeBuilder<TEntity> OwnsOne<TRelatedEntity>(

public abstract TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsMany<TRelatedEntity>(string navigationName)
where TRelatedEntity : class;
public abstract TestEntityTypeBuilder<TEntity> OwnsMany<TRelatedEntity>(
string navigationName,
Action<TestOwnedNavigationBuilder<TEntity, TRelatedEntity>> buildAction)
where TRelatedEntity : class;

public abstract TestOwnedNavigationBuilder<TEntity, TRelatedEntity> OwnsMany<TRelatedEntity>(
Expression<Func<TEntity, IEnumerable<TRelatedEntity>>> navigationExpression)
where TRelatedEntity : class;
Expand Down Expand Up @@ -521,6 +531,10 @@ public abstract TestOwnedNavigationBuilder<TEntity, TDependentEntity> HasAnnotat
public abstract TestPropertyBuilder<TProperty> Property<TProperty>(
Expression<Func<TDependentEntity, TProperty>> propertyExpression);

public abstract TestNavigationBuilder Navigation<TNavigation>(string navigationName);
public abstract TestNavigationBuilder Navigation<TNavigation>(
Expression<Func<TDependentEntity, TNavigation>> navigationExpression);

public abstract TestOwnedNavigationBuilder<TEntity, TDependentEntity> Ignore(string propertyName);

public abstract TestOwnedNavigationBuilder<TEntity, TDependentEntity> Ignore(
Expand Down
54 changes: 44 additions & 10 deletions test/EFCore.Tests/ModelBuilding/OwnedTypesTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1331,77 +1331,111 @@ public virtual void Inheritance_where_base_has_multiple_owned_types_works()
}

[ConditionalFact]
public virtual void Principal_end_of_navigations_to_OneToOne_Owned_types_can_set_access_mode_using_expressions()
public virtual void Navigations_on_OwnsOne_Owned_types_can_set_access_mode_using_expressions()
{
var modelBuilder = CreateModelBuilder();
var model = modelBuilder.Model;

modelBuilder.Entity<OneToOneNavPrincipalOwner>()
.OwnsOne(e => e.OwnedDependent)
.WithOwner(e => e.OneToOneOwner);
.OwnsOne(
e => e.OwnedDependent,
a =>
{
a.WithOwner(owned => owned.OneToOneOwner);
a.Navigation(owned => owned.OneToOneOwner)
.UsePropertyAccessMode(PropertyAccessMode.Property);
});

modelBuilder.Entity<OneToOneNavPrincipalOwner>()
.Navigation(e => e.OwnedDependent)
.UsePropertyAccessMode(PropertyAccessMode.Field);

var principal = (IEntityType)model.FindEntityType(typeof(OneToOneNavPrincipalOwner));
var dependent = (IEntityType)model.FindEntityType(typeof(OwnedNavDependent));

Assert.Equal(PropertyAccessMode.Field, principal.FindNavigation("OwnedDependent").GetPropertyAccessMode());
Assert.Equal(PropertyAccessMode.Property, dependent.FindNavigation("OneToOneOwner").GetPropertyAccessMode());
}

[ConditionalFact]
public virtual void Principal_end_of_navigations_to_OneToOne_Owned_types_can_set_access_mode_using_navigation_names()
public virtual void Navigations_on_OwnsOne_Owned_types_can_set_access_mode_using_navigation_names()
{
var modelBuilder = CreateModelBuilder();
var model = modelBuilder.Model;

modelBuilder.Entity<OneToOneNavPrincipalOwner>()
.OwnsOne<OwnedNavDependent>("OwnedDependent")
.WithOwner("OneToOneOwner");
.OwnsOne<OwnedNavDependent>(
"OwnedDependent",
a =>
{
a.WithOwner("OneToOneOwner");
a.Navigation<OneToOneNavPrincipalOwner>("OneToOneOwner")
.UsePropertyAccessMode(PropertyAccessMode.Property);
});

modelBuilder.Entity<OneToOneNavPrincipalOwner>()
.Navigation("OwnedDependent")
.UsePropertyAccessMode(PropertyAccessMode.Field);

var principal = (IEntityType)model.FindEntityType(typeof(OneToOneNavPrincipalOwner));
var dependent = (IEntityType)model.FindEntityType(typeof(OwnedNavDependent));

Assert.Equal(PropertyAccessMode.Field, principal.FindNavigation("OwnedDependent").GetPropertyAccessMode());
Assert.Equal(PropertyAccessMode.Property, dependent.FindNavigation("OneToOneOwner").GetPropertyAccessMode());
}

[ConditionalFact]
public virtual void Principal_end_of_navigations_to_OneToMany_Owned_types_can_set_access_mode_using_expressions()
public virtual void Navigations_on_OwnsMany_Owned_types_can_set_access_mode_using_expressions()
{
var modelBuilder = CreateModelBuilder();
var model = modelBuilder.Model;

modelBuilder.Entity<OneToManyNavPrincipalOwner>()
.OwnsMany(e => e.OwnedDependents);
.OwnsMany(
e => e.OwnedDependents,
a =>
{
a.WithOwner(owned => owned.OneToManyOwner);
a.Navigation(owned => owned.OneToManyOwner)
.UsePropertyAccessMode(PropertyAccessMode.Property);
});

modelBuilder.Entity<OneToManyNavPrincipalOwner>()
.Navigation(e => e.OwnedDependents)
.UsePropertyAccessMode(PropertyAccessMode.Field);

var principal = (IEntityType)model.FindEntityType(typeof(OneToManyNavPrincipalOwner));
var dependent = (IEntityType)model.FindEntityType(typeof(OwnedOneToManyNavDependent));

Assert.Equal(PropertyAccessMode.Field, principal.FindNavigation("OwnedDependents").GetPropertyAccessMode());
Assert.Equal(PropertyAccessMode.Property, dependent.FindNavigation("OneToManyOwner").GetPropertyAccessMode());
}

[ConditionalFact]
public virtual void Principal_end_of_navigations_to_OneToMany_Owned_types_can_set_access_mode_using_navigation_names()
public virtual void Navigations_on_OwnsMany_Owned_types_can_set_access_mode_using_navigation_names()
{
var modelBuilder = CreateModelBuilder();
var model = modelBuilder.Model;

modelBuilder.Entity<OneToManyNavPrincipalOwner>()
.OwnsMany<OwnedOneToManyNavDependent>("OwnedDependents");
.OwnsMany<OwnedOneToManyNavDependent>(
"OwnedDependents",
a =>
{
a.WithOwner("OneToManyOwner");
a.Navigation<OneToManyNavPrincipalOwner>("OneToManyOwner")
.UsePropertyAccessMode(PropertyAccessMode.Property);
});

modelBuilder.Entity<OneToManyNavPrincipalOwner>()
.Navigation("OwnedDependents")
.UsePropertyAccessMode(PropertyAccessMode.Field);

var principal = (IEntityType)model.FindEntityType(typeof(OneToManyNavPrincipalOwner));
var dependent = (IEntityType)model.FindEntityType(typeof(OwnedOneToManyNavDependent));

Assert.Equal(PropertyAccessMode.Field, principal.FindNavigation("OwnedDependents").GetPropertyAccessMode());
Assert.Equal(PropertyAccessMode.Property, dependent.FindNavigation("OneToManyOwner").GetPropertyAccessMode());
}
}
}
Expand Down