Многие ко многим отношения разрешены только частично
У меня возникли некоторые проблемы с 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 через объект отображения Пригласить).