Многие ко многим отношения разрешены только частично

У меня возникли некоторые проблемы с EF 5 Code First, главным образом потому, что я не знаю, какое поведение мне нужно искать.

Это о написании календарного бэкэнда.

Calendar включает в себя список CalendarEntry. Calendar принадлежит пользователю. CalendarEntry имеет владельца (так же, как и его календарь). CalendarEntry имеет необязательный список приглашений. У пользователя есть список всех его приглашений.

Проблема заключается в следующем: когда я добавляю пользователей в запись календаря, это работает, но когда требуется добавить того же пользователя в другую запись, первая запись календаря теряет пользователя (CalendarEnty.Invitees)

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

Любой намек на то, как решить эту проблему?

    public class Calendar
{
    [Key]
    public int CalendarId { get; set; }

    [Required]
    public virtual User Owner { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public virtual ICollection<CalendarEntry> CalendarEntries { get; set; }
    }

 public class CalendarEntry
{
    public CalendarEntry()
    {
        Invitees = new List<User>();
    }
    public virtual ICollection<User> Invitees { get; set; }


    public bool IsEmpty
    {
        get
        {
            if (Invitees.Count == 0)
            {
                return true;
            }
            return false;
        }
    }


    [Key]
    public int CalendarEntryId { get; set; }
    [Required]
    public DateTime StartDate { get; set; }
    [Required]
    public DateTime EndDate { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public double Duration
    {
        get
        {
            return EndDate.Subtract(StartDate).TotalMinutes;
        }
    }

    public int OwnerId { get; set; }
    public virtual User Owner { get; set; }
    public int CalendarId { get; set; }
    public virtual Calendar Calendar { get; set; }
    public virtual Room Room { get; set; }
}

   public class Invite
{

   [Key]
   public int InviteId { get; set; }
   public virtual CalendarEntry CalendarEntry { get; set; }
   public int Accepted { get; set; }
   public virtual User Owner {get; set;}

}

    public class User
{
    [Key]

    public int UserId { get; set; }
    [Required]       
    public string GivenName { get; set; }
    [Required]    
    public string Surname { get; set; }
    [Required]       
    public string MailAddress { get; set; }
    public string PhoneNumber { get; set; }

    public virtual Calendar Calendar { get; set; }
    public int? CalendarId { get; set; }

    //SHA512Hashed

    private string _password = null;
    public string Password
    {
        get
        {
            return _password;
        }
        set
        {
            _password = value;
        }
    }

    public virtual ICollection<Group> Groups { get; set; }
    public virtual ICollection<Invite> Invites { get; set; }
}

Идентификаторы пользователя и приглашения используются для более легкого доступа к элементам из службы WCF. Поэтому я не использую комбинированные ключи.

Следующим методом я гарантирую, что пользователь будет добавлен только один раз, я подозреваю, что это вызовет некоторые проблемы? К сожалению, я действительно не знаю, как обойти это.

        public int AddUser(User dbUser)
    {
        var users = this.GetAllUser();

        if (users != null && users.Where(p => p.MailAddress == dbUser.MailAddress).Count() > 0)
        {
            return 0;
        }

        try
        {
            _calendarDatabase.User.Add(dbUser);
            _calendarDatabase.SaveChanges();
            return dbUser.UserId;
        }
        catch (Exception ex)
        {
            _logger.Error(ex.ToString());
        }
        return 0;
    }

1 ответ

Решение

Вы не показываете свою модель User, но я подозреваю, что вы не объявили в ней ссылку на (коллекцию) моделей CalendarEntry. Убедитесь, что вы объявили что-то вроде:

public class User
{
  public ICollection<CalendarEntry> CalendarEntries { get; set; }
  // ... other User properties
}

Если вы не укажете эту ссылку, то EF будет предполагать, что связь между CalendarEntry и User является многозначной, что означает, что любой данный пользователь может быть связан только с одним CalendarEntry (хотя у каждого CalendarEntry может быть несколько пользователей). Таким образом, когда вы назначаете пользователя для CalendarEntry, он удаляется из любых предыдущих отношений.

Поместив свойство навигации (как коллекцию) в User модель, вы говорите EF, что на самом деле это отношение многие-ко-многим, и в этом случае оно сгенерирует таблицу сопоставления для вас за кулисами, чтобы позволить пользователю быть связанным с любым числом CalendarEntries.

РЕДАКТИРОВАТЬ -

Когда у вас есть отношение "многие ко многим" в EF, вы можете либо явно указать таблицу сопоставления для отношения, либо просто позволить EF сгенерировать для вас таблицу на основе свойств навигации, определенных в ваших объектах. Помещая свойство коллекции в каждый объект, который ссылается на другой, EF может вывести отношение "многие ко многим" и действовать соответственно.

Если у вас есть дополнительная информация для отношения, например, бит, указывающий, User принял Invite для CalendarEntry как и в вашем коде, вам нужно явно указать таблицу сопоставления. Когда вы делаете это, самый простой способ кодировать его в EF - это иметь у каждой из сторон отношения свойство навигации, которое является ссылкой на коллекцию объекта сопоставления, в отличие от другого объекта в отношении.

Итак, в вашем случае эта строка в CalendarEntry:

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

должно быть должно быть:

public virtual ICollection<Invite> Invitations { get; set; }

Чтобы получить ожидаемое поведение (например, позволяя User быть связанным с несколькими объектами CalendarEntry через объект отображения Пригласить).

Другие вопросы по тегам