How to have multiple One-To-Many relations using EF Core model first?

c# entity-framework-core

Question

I have two models named "User" and "IPSet". The user model is something like this:

public class Users
    {
        [Key]
        public int UserCode { get; set; }
        public string UserName { get; set; }
        public ICollection<IPSet> IPSets { get; set; }
    }

and IPSet model:

public class IPSet
    {
        [Key]
        public int IPSetCode { get; set; }
        public string IP { get; set; }
        public Users Users { get; set; }
        [ForeignKey("Users")]
        public int UsersFK { get; set; }
    }

The goal is setting Some IPs to a user in order to log in only from that IP addresses, So I need a One-To_Many relation between models. I've tried above codes and they're true.

But I want to have a column in IPSet named SetterUser that means which user has set an IP with a user.

SetterUser has also a relation with User table. How I can implement this relation?

I've tried:

public class Users
    {
        [Key]
        public int UserCode { get; set; }
        public string UserName { get; set; }
        public ICollection<IPSet> IPSets { get; set; }
        public ICollection<IPSet> IPSets1 { get; set; }
    }

and

public class IPSet
    {
        [Key]
        public int IPSetCode { get; set; }

        public string IP { get; set; }

        public Users Users { get; set; }

        [ForeignKey("Users")]
        public int UsersFK { get; set; }

        public Users SetterUser{ get; set; }

        [ForeignKey("SetterUser")]
        public int SetterUserFK { get; set; }
    }

But this error will appearance:

Unable to determine the relationship represented by navigation property 'IPSet.Users' of type 'Users'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
1
0
11/5/2018 9:06:28 AM

Accepted Answer

The post you are following is definitely wrong.

Every collection or reference navigation property can only be a part of a single relationship. While many to many relationship with explicit join entity is implemented with two one to many relationships. The join entity contains two reference navigation properties, but the main entity has only single collection navigation property, which has to be associated with one of them, but not with both.

One way to resolve the issue is to add a second collection navigation property:

public class WordEntity
{
    public long Id { get; set; }
    public string Name { get; set; }
    public string Json { get; set; }

    public virtual List<WordSinonymEntity> Sinonyms { get; set; }
    public virtual List<WordSinonymEntity> SinonymOf { get; set; } // <--
}

and specify the associations via fluent API:

modelBuilder.Entity<WordSinonymEntity>()
     .HasOne(pt => pt.Sinonym)
     .WithMany(p => p.SinonymOf) // <--
     .HasForeignKey(pt => pt.SinonymId)
     .OnDelete(DeleteBehavior.Restrict); // see the note at the end

modelBuilder.Entity<WordSinonymEntity>()
    .HasOne(pt => pt.Word)
    .WithMany(t => t.Sinonyms)
    .HasForeignKey(pt => pt.WordId); 

Another way is to leave the model as is, but map the WordSinonymEntity.Sinonym to unidirectional association (with refeference navigation property and no corresponding collection navigation property):

modelBuilder.Entity<WordSinonymEntity>()
     .HasOne(pt => pt.Sinonym)
     .WithMany() // <--
     .HasForeignKey(pt => pt.SinonymId)
     .OnDelete(DeleteBehavior.Restrict); // see the note at the end

modelBuilder.Entity<WordSinonymEntity>()
    .HasOne(pt => pt.Word)
    .WithMany(t => t.Sinonyms)
    .HasForeignKey(pt => pt.WordId); 

Just make sure that WithMany exactly matches the presence/absence of the corresponding navigation property.

Note that in both cases you have to turn the delete cascade off for at least one of the relationships and manually delete the related join entities before deleting the main entity, because self referencing relationships always introduce possible cycles or multiple cascade path issue, preventing the usage of cascade delete.

17
3/11/2018 10:46:32 AM

Popular Answer

The model you are describing represents two one-to-many relationships between the User and UserNotifications (btw, the entity should be named UserNotification) entities. Each EF relationship can be mapped to 0 or 1 unique navigation properties at each side.

You already have two User and Sender reference navigation properties (and corresponding FKs) in UserNotifications. What you need is two corresponding collection navigation properties in User:

public class User : IdentityUser
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public virtual ICollection<UserNotifications> ReceivedNotifications { get; set; }
    public virtual ICollection<UserNotifications> SentNotifications { get; set; }
}

and map the with fluent API:

builder.Entity<UserNotifications>(entity =>
{
    entity.HasKey(n => n.Id);

    entity.HasOne(n => u.User)
        .WithMany(u => u.ReceivedNotifications)
        .HasForeignKey(n => u.UserId)
        .IsRequired()
        .OnDelete(DeleteBehavior.Delete);

    entity.HasOne(n => n.Sender)
        .WithMany(u => u.SentNotifications)
        .HasForeignKey(n => n.SenderId)
        .IsRequired()
        .OnDelete(DeleteBehavior.Restrict);
 });

Note that since such model introduces the so called multiple cascade paths, you need to turn at least one of the cascade delete off and handle it manually.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow