Невозможно создать отношение «многие ко многим» с помощью настраиваемой таблицы соединений. В отношениях «многие ко многим» EF управляет внутренней и скрытой таблицей соединений. Это таблица без класса Entity в вашей модели. Чтобы работать с такой таблицей соединений с дополнительными свойствами, вам фактически необходимо создать два отношения «один ко многим». Это может выглядеть так:
public class Member
{
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<MemberComment> MemberComments { get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public string Message { get; set; }
public virtual ICollection<MemberComment> MemberComments { get; set; }
}
public class MemberComment
{
[Key, Column(Order = 0)]
public int MemberID { get; set; }
[Key, Column(Order = 1)]
public int CommentID { get; set; }
public virtual Member Member { get; set; }
public virtual Comment Comment { get; set; }
public int Something { get; set; }
public string SomethingElse { get; set; }
}
Если вы теперь хотите найти все комментарии участников с помощью LastName
= "Smith", например, вы можете написать запрос следующим образом:
var commentsOfMembers = context.Members
.Where(m => m.LastName == "Smith")
.SelectMany(m => m.MemberComments.Select(mc => mc.Comment))
.ToList();
... или ...
var commentsOfMembers = context.MemberComments
.Where(mc => mc.Member.LastName == "Smith")
.Select(mc => mc.Comment)
.ToList();
Или для создания списка участников с именем «Смит» (мы предполагаем, что их больше одного) вместе с их комментариями вы можете использовать проекцию:
var membersWithComments = context.Members
.Where(m => m.LastName == "Smith")
.Select(m => new
{
Member = m,
Comments = m.MemberComments.Select(mc => mc.Comment)
})
.ToList();
Если вы хотите найти все комментарии участника с MemberId
= 1:
var commentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1)
.Select(mc => mc.Comment)
.ToList();
Теперь вы также можете фильтровать по свойствам в вашей соединительной таблице (что было бы невозможно при соотношении «многие ко многим»), например: Отфильтровать все комментарии члена 1, которые имеют свойство 99 Something
:
var filteredCommentsOfMember = context.MemberComments
.Where(mc => mc.MemberId == 1 && mc.Something == 99)
.Select(mc => mc.Comment)
.ToList();
Из-за ленивой загрузки все может стать проще. Если у вас есть загруженный, Member
вы сможете получить комментарии без явного запроса:
var commentsOfMember = member.MemberComments.Select(mc => mc.Comment);
Я думаю, что ленивая загрузка будет автоматически получать комментарии за кулисами.
редактировать
Просто для удовольствия еще несколько примеров, как добавить сущности и отношения и как удалить их в этой модели:
1) Создайте одного участника и два комментария этого участника:
var member1 = new Member { FirstName = "Pete" };
var comment1 = new Comment { Message = "Good morning!" };
var comment2 = new Comment { Message = "Good evening!" };
var memberComment1 = new MemberComment { Member = member1, Comment = comment1,
Something = 101 };
var memberComment2 = new MemberComment { Member = member1, Comment = comment2,
Something = 102 };
context.MemberComments.Add(memberComment1); // will also add member1 and comment1
context.MemberComments.Add(memberComment2); // will also add comment2
context.SaveChanges();
2) Добавить третий комментарий member1:
var member1 = context.Members.Where(m => m.FirstName == "Pete")
.SingleOrDefault();
if (member1 != null)
{
var comment3 = new Comment { Message = "Good night!" };
var memberComment3 = new MemberComment { Member = member1,
Comment = comment3,
Something = 103 };
context.MemberComments.Add(memberComment3); // will also add comment3
context.SaveChanges();
}
3) Создать нового участника и связать его с существующим комментарием2:
var comment2 = context.Comments.Where(c => c.Message == "Good evening!")
.SingleOrDefault();
if (comment2 != null)
{
var member2 = new Member { FirstName = "Paul" };
var memberComment4 = new MemberComment { Member = member2,
Comment = comment2,
Something = 201 };
context.MemberComments.Add(memberComment4);
context.SaveChanges();
}
4) Создайте отношения между существующим member2 и comment3:
var member2 = context.Members.Where(m => m.FirstName == "Paul")
.SingleOrDefault();
var comment3 = context.Comments.Where(c => c.Message == "Good night!")
.SingleOrDefault();
if (member2 != null && comment3 != null)
{
var memberComment5 = new MemberComment { Member = member2,
Comment = comment3,
Something = 202 };
context.MemberComments.Add(memberComment5);
context.SaveChanges();
}
5) Удалить это отношение снова:
var memberComment5 = context.MemberComments
.Where(mc => mc.Member.FirstName == "Paul"
&& mc.Comment.Message == "Good night!")
.SingleOrDefault();
if (memberComment5 != null)
{
context.MemberComments.Remove(memberComment5);
context.SaveChanges();
}
6) Удалить member1 и все его связи с комментариями:
var member1 = context.Members.Where(m => m.FirstName == "Pete")
.SingleOrDefault();
if (member1 != null)
{
context.Members.Remove(member1);
context.SaveChanges();
}
Это также приводит к удалению отношений, MemberComments
поскольку отношения «один ко многим» между Member
и MemberComments
и между ними Comment
и MemberComments
устанавливаются с помощью каскадного удаления по соглашению. И это так, потому что MemberId
и CommentId
in MemberComment
обнаруживаются как свойства внешнего ключа для свойств Member
и Comment
навигации, и так как свойства FK имеют тип, не допускающий значения NULL int
, требуется связь, которая в конечном итоге вызывает каскадную-delete-setup. Имеет смысл в этой модели, я думаю.
OnModelCreating
. Пример опирается только на правила отображения и аннотации данных.MemberId
иCommentId
столбцов , а не дополнительный третий столбецMember_CommentId
(или что - то подобное) - это означает , что вы не имеете точных имен , соответствующих между объектами для ваших ключейОтличный ответ от Slauma.
Я просто опубликую код, чтобы сделать это, используя свободное отображение API .
На вашем
DbContext
производном классе вы можете сделать это:Он имеет тот же эффект, что и принятый ответ, с другим подходом, который не лучше и не хуже.
РЕДАКТИРОВАТЬ:
я изменил CreatedDate с bool на DateTime.РЕДАКТИРОВАТЬ 2: из-за нехватки времени я поместил пример из приложения, над которым я работаю, чтобы убедиться, что это работает.
источник
In your classes you can easily describe a many to many relationship with properties that point to each other.
взято с: msdn.microsoft.com/en-us/data/hh134698.aspx . Джули Лерман не может ошибаться.Comments
свойстваMember
. И вы не можете просто исправить это, переименовавHasMany
вызов,MemberComments
потому что уMemberComment
сущности нет обратной коллекции дляWithMany
. Фактически вам нужно настроить два отношения «один ко многим», чтобы получить правильное отображение.@Esteban, код, который ты предоставил, правильный, спасибо, но не полный, я проверил это. В классе "UserEmail" отсутствуют свойства:
Выкладываю проверенный код, если кому-то интересно. С уважением
источник
Я хочу предложить решение, в котором могут быть достигнуты оба варианта конфигурации «многие ко многим».
«Подвох» заключается в том, что нам нужно создать представление, предназначенное для таблицы соединений, поскольку EF проверяет, что таблица схемы может отображаться не более одного раза в
EntitySet
.Этот ответ добавляет к тому, что уже было сказано в предыдущих ответах, и не отменяет ни один из этих подходов, он основывается на них.
Модель:
Конфигурация:
Контекст:
Из (@Saluma) Saluma в ответ
Это все еще работает ...
... но теперь тоже может быть ...
Это все еще работает ...
... но теперь тоже может быть ...
Если вы хотите удалить комментарий от участника
Если вы хотите, чтобы
Include()
комментарии пользователяВсе это похоже на синтаксический сахар, однако оно даст вам несколько льгот, если вы захотите пройти через дополнительную конфигурацию. В любом случае, вы, кажется, в состоянии получить лучшее от обоих подходов.
источник
EntityTypeConfiguration<EntityType>
ключ и свойства типа сущности. Например,Property(x => x.MemberID).HasColumnType("int").IsRequired();
кажется, излишним сpublic int MemberID { get; set; }
. Не могли бы вы очистить мое запутанное понимание, пожалуйста?TLDR; (частично связано с ошибкой редактора EF в EF6 / VS2012U5) если вы генерируете модель из БД и не видите атрибутивной таблицы m: m: удалите две связанные таблицы -> Сохранить .edmx -> Создать / добавить из базы данных - > Сохранить.
Тем, кто пришел сюда, интересно, как получить отношение «многие ко многим» со столбцами атрибутов для отображения в файле EF .edmx (поскольку в настоящее время он не будет отображаться и рассматриваться как набор навигационных свойств), И вы сгенерировали эти классы из вашей таблицы базы данных (или, я полагаю, сначала из базы данных в MS lingo)
Удалите две рассматриваемые таблицы (чтобы взять пример OP, Member и Comment) в своем .edmx и добавьте их снова через «Создать модель из базы данных». (т.е. не пытайтесь позволить Visual Studio обновить их - удалите, сохраните, добавьте, сохраните)
Затем он создаст 3-ю таблицу в соответствии с тем, что предлагается здесь.
Это актуально в тех случаях, когда сначала добавляется чистое отношение «многие ко многим», а атрибуты создаются в БД позже.
Это было не сразу понятно из этой темы / Googling. Так что просто разместите это, так как это ссылка № 1 в Google, которая ищет проблему, но сначала идет со стороны БД.
источник
Один из способов решения этой ошибки - поместить
ForeignKey
атрибут поверх свойства, которое вы хотите использовать в качестве внешнего ключа, и добавить свойство навигации.Примечание: в
ForeignKey
атрибуте между круглыми скобками и двойными кавычками поместите имя класса, на который ссылаются таким образом.источник