Entity Framework 4.1 Code First Самоссылающиеся ассоциации «один ко многим» и «многие ко многим»

У меня есть пользователь, у которого может быть коллекция пользователей, которые ему нравятся...

У другого пользователя может быть коллекция пользователей, которые ему нравятся....

Если пользователю А нравится пользователь Б, а пользователю Б нравится пользователь А, то они могут проводить время вместе. Мне нужно отправить друг другу их контактную информацию. Как представить такую ​​модель в 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_Target» отношения «UserLike_LikeUser».

Как лучше представить такую ​​модель?


person Preetham Reddy    schedule 17.06.2011    source источник


Ответы (1)


На самом деле вам не нужна отдельная сущность для описания отношений, объектная модель ниже сделает свое дело:

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

Теперь предположим, что у вас есть идентификатор пользователя и вы хотите найти другого пользователя, которому нравится этот пользователь, который также нравится этому пользователю:

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) для представления дополнительной информации, прикрепленной к вашей самореферентной ассоциации многие ко многим, но ассоциации из этого промежуточного класса неверны, поскольку нам нужно определить ровно 2 ассоциации «многие к одному» от UserLike к User, как я показал в следующей объектной модели:

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

Надеюсь это поможет.

person Morteza Manavi    schedule 17.06.2011
comment
Мортеза, Спасибо за быстрый ответ... Это потрясающе... Теперь предположим, что если пользователь может лайкать многих пользователей, и многие пользователи могут лайкать этого пользователя, как это изменится? - person Preetham Reddy; 18.06.2011
comment
Последний вопрос.... Что делать, если я хочу зафиксировать лайки пользователей для каждого события... В приведенном выше режиме создается соединительная таблица со столбцами UserId и OtherUserId... Но я хочу зафиксировать, на каком событии они нравятся друг другу. Как я могу добавить еще один столбец eventId в соединительную таблицу? - person Preetham Reddy; 19.06.2011
comment
Пожалуйста, взгляните на обновление 2, где я задал вам последний вопрос. - person Morteza Manavi; 20.06.2011
comment
Обновление 2 выдает мне эту ошибку: «CollectionCollectionBuilder‹User, User›» не содержит определения для «Map», и не найден доступный метод расширения «Map», принимающий первый аргумент типа «CollectionCollectionBuilder‹User, User›». (вам не хватает директивы использования или ссылки на сборку?)... Любая идея? - person OneClutteredMind; 16.11.2020