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

OwnsMany with fully-defined relationship #16016

Closed
UKDeveloper99 opened this issue Jun 10, 2019 · 4 comments
Closed

OwnsMany with fully-defined relationship #16016

UKDeveloper99 opened this issue Jun 10, 2019 · 4 comments
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@UKDeveloper99
Copy link

UKDeveloper99 commented Jun 10, 2019

This issue is a few questions and an issue wrapped in to one, the main issue being - I don't know the difference and can't find the answer online or within the docs. You'll have to forgive me if I'm missing anything obvious, I'm fairly new to EF Core.

Let's assume I have these models:

public class Parent
{
	public Parent()
	{
	}

	public int Id { get; set; }	
	public List<Child> Children { get; set; }
}

public class Child
{
	public Child(int parentId, Parent parent)
	{
		ParentId = parentId;
		Parent = parent;
	}
	
	public int Id { get; set; }
	public string SomeProp { get; set; }
	public int ParentId { get; set; }
	public Parent Parent { get; set; }
}

with this entity type configuration.

   public class ParentTypeConfiguration : IEntityTypeConfiguration<Parent>
    {
        public void Configure(EntityTypeBuilder<Parent> builder)
        {
            builder
                .OwnsMany(p => p.Children, c =>      
                {
                    c.HasForeignKey(child => child.ParentId);
                    c.Property<int>("Id");
                    c.HasKey("ParentId", "Id");  
                    c.HasOne(child => child.Parent);
                }
         });
    }

Firstly a one-to-many question... What is the purpose/advantages/disadvantages of defining a complex key? From the docs "It is common to use a complex key for these type of entities incorporating the foreign key to the owner and an additional unique property that can also be in shadow state". It's completely unclear what the relationship infers and the explanation is vague at best. Specifically in an Owned one-to-many object scenario.

Secondly I want to add a navigation property to the parent, is the above example correctly defined within the context of an owned object?

Thirdly, why can't I pass the relationship object "Parent" through the constructor. This seems to throw an exception.

System.InvalidOperationException
No suitable constructor found for entity type 'Child'. The following constructors had parameters that could not be bound to properties of the entity type: cannot bind 'parent' in 'Child(int parentId, Parent parent)'.

I've seen some posts online about using a private constructor for EFCore and a public one for your domain model (DDD concept).

E.g.

public class Child
{
	private Child()
	{
        }
        
        .....
}

which seems to work, is this a valid solution?

Finally while I'm here; a bit of feedback on the documentation. I'm finding the documentation a bit lacking and the explanations of features are fairly loose with hardly any code examples. For example code to demonstrate using nested OwnsMany seems like an obvious one. With a bit more depth and complexity than just an object with an Id. Also I'm finding it a bit confusing with the naming of methods that are similar to older style methods within a CollectionOwnershipBuilder. E.g. HasOne, HasKey. It's not immediately obvious which methods are relevant to an Owned Relationship and which aren't.

Thanks in advance EF Core wizards.

Further technical details

EF Core version: 2.2.4
Database Provider: Microsoft.EntityFrameworkCore.Sqlite
Operating system: MacOS Mojave
Built for: Xamarin.Android
IDE: Visual Studio for Mac

@AndriySvyryd
Copy link
Member

AndriySvyryd commented Jun 11, 2019

Firstly a one-to-many question... What is the purpose/advantages/disadvantages of defining a complex key?

The key has to be unique, but the foreign key is not, so we can't use just the foreign key as the primary key as we do for OwnsOne. The two most straightforward solutions are:

  1. Use a single non-FK property. The contained values would need to be unique across all owners (e.g. if Parent 1 has Child 1, then Parent 2 cannot have Child 1), so the value doesn't have any inherent meaning. Since the FK is not part of the PK its values can be changed, so you could move a child from one parent to another one, however this usually goes against aggregate semantics.

  2. Use the FK and an additional property. The additional property value now only needs to be unique for a given parent (so there can be a Parent 1 with Child 1,1 and Parent 2 with Child 2,1). By making the FK part of the PK the relationship between the owner and the owned entity becomes immutable and reflects aggregate semantics better.

You can choose either option, but we show the latter one because it's both harder to configure and is better suited for the expected use of an owned type.

@divega Would you like to add something else?

Secondly I want to add a navigation property to the parent, is the above example correctly defined within the context of an owned object?

Yes, but you need to change c.HasKey("Parent", "Id"); to c.HasKey("ParentId", "Id");

Thirdly, why can't I pass the relationship object "Parent" through the constructor. This seems to throw an exception.

This is tracked in #12078. Using a private constructor is the best workaround for now.

Finally while I'm here; a bit of feedback on the documentation.

Filed dotnet/EntityFramework.Docs#1518

@UKDeveloper99
Copy link
Author

Yes, but you need to change c.HasKey("Parent", "Id"); to c.HasKey("ParentId", "Id");

Sorry that's a typo, I wrote the code freehand. Haven't had time to properly digest your response yet but I'll add any further questions regarding what you said then you can close the issue off. Thank you!

@divega
Copy link
Contributor

divega commented Jun 12, 2019

@divega Would you like to add something else?

@AndriySvyryd No, this is a great explanation. We should copy it into the docs. Thanks!

@UKDeveloper99
Copy link
Author

@divega Would you like to add something else?

@AndriySvyryd No, this is a great explanation. We should copy it into the docs. Thanks!

Agreed! To the docs!!

@ajcvickers ajcvickers added the closed-no-further-action The issue is closed and no further action is planned. label Jun 13, 2019
@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

4 participants