29409

How to define multiple relationships between two entities?

Question:

I have the scenario where a <strong>user</strong> can upload multiple <strong>photos</strong> (One-to-Many). The <strong>user</strong> also can have <strong>a default photo</strong> (One-to-One). However, I entity framework <strong>core 2.0</strong> tells that he cannot recognize the relationship when I use the following code:

public class User { public Guid Id { get; set; } public ICollection<Photo> Photos{ get; set; } public Photo DefaultPhoto { get; set; } [ForeignKey("DefaultPhoto")] public Guid DefaultPhotoId { get; set; } } public class Photo { public Guid Id { get; set; } public User Owner { get; set; } }

How may I achieve these multiple relationships?

There error shown by EF-Core:

<blockquote>

System.InvalidOperationException: 'Unable to determine the relationship represented by navigation property 'Photo.Owner' of type 'User'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.'

</blockquote> <h3>UPDATE:</h3>

Adding [InverseProperty("Photos")] to the navigation property <strong>Owner</strong> in File Model seems to be working. I am not sure if that is the correct way?

<a href="https://i.stack.imgur.com/b6fcZ.png" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/b6fcZ.png" data-original="https://i.stack.imgur.com/b6fcZ.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a> <em>In this image File=Photo; Uploader=Owner (to be comparable with the above model).</em>

<h3>UPDATE 2:</h3>

I confirm what @Ivan said in the comments, with DataAnnotation approach, I get One-to-Many in two directions instead of One-to-Many and One-to-One. This figure shows the generated database by using InverseProperty (the connection between the to entities show the bi-directional One-to-Many relationship):

<a href="https://i.stack.imgur.com/Ah82h.png" rel="nofollow"><img alt="enter image description here" class="b-lazy" data-src="https://i.stack.imgur.com/Ah82h.png" data-original="https://i.stack.imgur.com/Ah82h.png" src="https://etrip.eimg.top/images/2019/05/07/timg.gif" /></a>

<em>In this image File=Photo; Uploader=Owner (to be comparable with the above model).</em>

Answer1:

Use the Fluent API to establish the one-to-one relationship:

protected override void OnModelCreating(ModelBuilder modelBuilder) { // Establish a one to one relationship with the // User as the dependent End modelBuilder.Entity<User>() .HasOne(u => u.DefaultPhoto) .WithOne(p => p.Owner) .HasForeignKey<User>(u => u.DefaultPhotoId); }

For a relationship to be a one-to-one (1:0..1) relationship, the relationship must be established between two primary keys or two candidate keys. (Check <a href="https://gldraphael.com/blog/notes-on-database-relationships/" rel="nofollow">this blog post</a> for more info on this.)

EF has no way of setting a candidate key (also called unique or alternate key) via Annotations right now, so this is your only option. Check the EF docs here at Microsoft Docs: <a href="https://docs.microsoft.com/en-us/ef/core/modeling/relationships#other-relationship-patterns" rel="nofollow">One to One relationships</a>

<hr />

<strong>Update:</strong> The earlier code would automatically generate a UserId column and sets up relationships correctly. I've added the OwnerId field to the Photo entity to manually set the relationship, like you wanted:

public class User { public Guid Id { get; set; } [InverseProperty("Owner")] public ICollection<Photo> Photos{ get; set; } public Photo DefaultPhoto { get; set; } public Guid DefaultPhotoId { get; set; } } public class Photo { public Guid Id { get; set; } public Guid OwnerId { get; set; } public User Owner { get; set; } } public class MyDbContext : DbContext { public DbSet<User> Users { get; set; } public DbSet<Photo> Photos { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // You may use this instead of the inverse property // // The One to Many relationship between // // User.Id (Principal End) and Photo.OwnerId // modelBuilder.Entity<Photo>() // .HasOne(p => p.Owner) // .WithMany(u => u.Photos) // .HasForeignKey(p => p.OwnerId); // Establishes 1:0..1 relationship between // Photo.Id (Principal End) and User.DefaultPhoto (Dependent end) modelBuilder.Entity<User>() .HasOne(u => u.DefaultPhoto) .WithOne() // we leave this empty because it doesn't correspond to a navigation property in the Photos table .HasForeignKey<User>(u => u.DefaultPhotoId); } }

The trick is in figuring the relationships (especially the principal end and the dependent ends) and in figuring which ones require navigation properties.

The Inverse property is required because EF doesn't know which property to map to Photo.Owner. It's just a way of making a relationship explicit in cases like these.

Recommend