实体框架 4.1 Code First 自引用一对多和多对多关联

21

我有一个用户,他可以有一组他喜欢的用户...

另一个用户也可以有一组他喜欢的用户...

如果用户A喜欢用户B,并且用户B也喜欢用户A,则他们可以联系并交流信息。我该如何在Entity Framework Code First中表示这样的模型?

public class User
{
    public int UserId { get; set; }

    public int? UserLikeId { get; set; }
    public virtual UserLike UserLike { get; set; }
}

public class UserLike
{
    public int UserLikeId { get; set; }

    public int UserId { get; set; }
    public virtual User User { get; set; }

    public virtual ICollection<User> LikeUsers { get; set; }
}

这个模型正确吗?我无法让它工作。

我尝试了另外一种方法,但那也不起作用...

我试图将用户集合添加到用户表中。

例如:

public virtual ICollection<User> userlike { get; set; }

public class User
{
    public int UserId { get; set; }
    public virtual ICollection<UserLike> UserLikes { get; set; }
}

public class UserLike
{
    public int UserLikeId { get; set; }

    public int UserId { get; set; }
    public virtual User User { get; set; }

    public int LikeUserId { get; set; }
    public virtual User LikeUser { get; set; }
}

当我尝试添加用户和他们所喜欢的人时,出现了以下错误:

检测到对关系“UserLike_LikeUser”的角色“UserLike_LikeUser_Target”存在冲突更改。

如何最好地表示这样的模型?

1个回答

27

你不需要一个单独的实体来描述关系,下面的对象模型就可以达到效果:

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);
    }
}

现在假设你手头有一个UserId,想要找到喜欢这个用户的其他用户,并且这些用户也被该用户所喜欢:

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);
}                

更新 1:

一个自引用的多对多关联将使用联接表映射到数据库中,这需要完全不同的对象模型和流畅的 API:

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");
                    });
    }
}

更新2:

如我在这篇文章中所解释的,多对多关联不能有有效载荷(例如EventId),如果确实需要,则需要将其拆分为两个一对多关联到一个介入类,并且我可以看到您已经正确创建了该类(UserLike)来表示附加到自身引用的多对多关联的额外信息,但是从该中间类到User的关联不正确,因为我们需要从UserLike到User定义恰好2个多对一关联,就像我在下面的对象模型中展示的那样:

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);
    }
}

现在,您可以使用以下 LINQ 查询来检索彼此喜欢的所有用户:

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();
}
希望这可以帮助到你。

Morteza,感谢您的快速回复...这太棒了...现在,假设一个用户可以喜欢许多用户,许多用户也可以喜欢这个用户,那么这会如何改变? - Preetham Reddy
最后一个问题...如果我想要记录每个事件中用户的喜欢,该怎么办?上述模式创建了一个包含UserId和OtherUserId列的连接表...但是我想要记录他们在哪个事件中互相喜欢。我该如何向连接表添加另一列eventId? - Preetham Reddy
请查看更新2,我在那里回答了你的最后一个问题。 - Morteza Manavi
更新2给我报错:'CollectionCollectionBuilder<User, User>'不包含'Map'的定义,也没有可访问的扩展方法'Map'接受类型为'CollectionCollectionBuilder<User, User>'的第一个参数(你是否缺少using指令或程序集引用?)... 有任何想法吗? - OneClutteredMind

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接