Entity Framework 4.1 Code First Self-Referencing One-to-Many and Many-to-Many Associations

后端 未结 1 1423
死守一世寂寞
死守一世寂寞 2020-12-13 10:44

I have a User that can have collection of users he likes...

Another user can have collection of users he likes....

If User A likes User B and if User B likes

相关标签:
1条回答
  • 2020-12-13 11:17

    You don't really need a separate entity to describe the relationship, the object model below will do the trick:

    public class User
    {
        public int UserId { get; set; }
        public string Name { get; set; }
    
        public int? ThisUserLikesId { get; set; }
        public virtual User ThisUserLikes { get; set; }
        public virtual ICollection<User> LikeThisUser { get; set; }
    }
    
    public class Context : DbContext
    {
        public DbSet<User> Users { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>()
                        .HasOptional(u => u.ThisUserLikes)
                        .WithMany(u => u.LikeThisUser)
                        .HasForeignKey(u => u.ThisUserLikesId);
        }
    }
    

    Now let's say you have a UserId in your hand and want to find the other User who likes this user which this user also like him:

    using (var context = new Context())
    {
        // For a given user id = 1
        var friends = (from u in context.Users
                       where u.UserId == 1
                       from v in u.LikeThisUser
                       where v.UserId == u.ThisUserLikesId
                       select new 
                       { 
                           OurUser = u, 
                           HerFriend = v 
                       })
                       .SingleOrDefault();
    
        ExchangeContactInfo(friends.OurUser, friends.HerFriend);
    }                
    


    Update 1:

    A self referencing many-to-many association will be mapped to database using a join table which require a different object model and fluent API altogether:

    public class User
    {
        public int UserId { get; set; }
        public string Name { get; set; }
    
        public virtual ICollection<User> ThisUserLikes { get; set; }
        public virtual ICollection<User> UsersLikeThisUser { get; set; }
    }
    
    public class Context : DbContext
    {
        public DbSet<User> Users { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>()
                        .HasMany(u => u.ThisUserLikes)
                        .WithMany(u => u.UsersLikeThisUser)
                        .Map(c => 
                        {
                            c.MapLeftKey("UserId");
                            c.MapRightKey("OtherUserId");
                            c.ToTable("UserLikes");
                        });
        }
    }
    


    Update 2:

    As I explained in this post, a many-to-many association cannot have a payload (e.g EventId), and if that’s the case then we have to break it down to two one-to-many associations to an intervening class and I can see you’ve correctly created this class (UserLike) to represent the extra information attached to your self-referencing many-to-many association but the associations from this intermediate class are not correct as we need to define exactly 2 many-to-one association from UserLike to User like I showed in the following object model:

    public class User
    {        
        public int UserId { get; set; }
        public string Email { get; set; }       
    
        public virtual ICollection ThisUserLikes { get; set; }
        public virtual ICollection UsersLikeThisUser { get; set; }
    }       
    
    public class UserLike
    {
        public int UserLikeId { get; set; }
        public int LikerId { get; set; }
        public int LikeeId { get; set; }
        public int EventId { get; set; }
    
        public User Liker { get; set; }
        public User Likee { get; set; }
        public virtual Event Event { get; set; }
    }
    
    public class Event
    {
        public int EventId { get; set; }
        public string Name { get; set; }
    }  
    
    public class Context : DbContext 
    {
        public DbSet Users { get; set; } 
        public DbSet Events { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity()
                        .HasMany(u => u.ThisUserLikes)
                        .WithRequired(ul => ul.Liker)
                        .HasForeignKey(ul => ul.LikerId);
            modelBuilder.Entity()                        
                        .HasMany(u => u.UsersLikeThisUser)
                        .WithRequired(ul => ul.Likee)
                        .HasForeignKey(ul => ul.LikeeId)
                        .WillCascadeOnDelete(false);
        }
    }
    

    Now you can use the following LINQ query to retrieve all the users who like each other:

    using (var context = new Context())
    {                
        var friends = (from u1 in context.Users
                       from likers in u1.UsersLikeThisUser
                       from u2 in u1.ThisUserLikes 
                       where u2.LikeeId == likers.LikerId
                       select new
                       {
                           OurUser = u1.UserId,
                           HerFriend = u2.LikeeId 
                       })
                       .ToList();
    }
    

    Hope this helps.

    0 讨论(0)
提交回复
热议问题