Сначала создайте код, многие для многих, с дополнительными полями в таблице объединений

У меня такой сценарий:

public class Member { public int MemberID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual ICollection Comments { get; set; } } public class Comment { public int CommentID { get; set; } public string Message { get; set; } public virtual ICollection Members { get; set; } } public class MemberComment { public int MemberID { get; set; } public int CommentID { get; set; } public int Something { get; set; } public string SomethingElse { get; set; } } 

Как настроить связь с белым API ? Или есть лучший способ создать таблицу ассоциаций?

6 Solutions collect form web for “Сначала создайте код, многие для многих, с дополнительными полями в таблице объединений”

Невозможно создать отношения «многие ко многим» с персонализированной таблицей соединений. В отношениях «многие ко многим» EF управляет таблицей соединений внутри и снаружи. Это таблица без classа Entity в вашей модели. Чтобы работать с такой таблицей соединений с дополнительными свойствами, вам нужно будет создать фактически два отношения «один ко многим». Это может выглядеть так:

 public class Member { public int MemberID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual ICollection MemberComments { get; set; } } public class Comment { public int CommentID { get; set; } public string Message { get; set; } public virtual ICollection 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 = «Смит», например, вы можете написать запрос следующим образом:

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

Или создать список членов с именем «Смит» (мы предполагаем, что их более одного) вместе с их комментариями вы можете использовать outlook:

 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) Добавьте третий комментарий участника1:

 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) Создайте отношения между существующим элементом2 и комментарием 3:

 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) Удалите член 1 и все его отношения с комментариями:

 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 в MemberComment обнаруживаются как свойства внешнего ключа для свойств навигации для Member и Comment а поскольку свойства FK имеют тип, не имеющий значения null, то требуется связь, которая в конечном итоге вызывает каскадное удаление-установку. Я думаю, имеет смысл в этой модели.

Отличный ответ Slauma.

Я просто отправлю код, чтобы сделать это, используя свободное API- сопоставление.

 public class User { public int UserID { get; set; } public string Username { get; set; } public string Password { get; set; } public ICollection UserEmails { get; set; } } public class Email { public int EmailID { get; set; } public string Address { get; set; } public ICollection UserEmails { get; set; } } public class UserEmail { public int UserID { get; set; } public int EmailID { get; set; } public bool IsPrimary { get; set; } } 

На свой производный class DbContext вы можете сделать это:

 public class MyContext : DbContext { protected override void OnModelCreating(DbModelBuilder builder) { // Primary keys builder.Entity().HasKey(q => q.UserID); builder.Entity().HasKey(q => q.EmailID); builder.Entity().HasKey(q => new { q.UserID, q.EmailID }); // Relationships builder.Entity() .HasRequired(t => t.Email) .WithMany(t => t.UserEmails) .HasForeignKey(t => t.EmailID) builder.Entity() .HasRequired(t => t.User) .WithMany(t => t.UserEmails) .HasForeignKey(t => t.UserID) } } 

Он имеет тот же эффект, что и принятый ответ, с другим подходом, который не лучше и не хуже.

EDIT: я изменил CreateDate с bool на DateTime.

EDIT 2: Из-за нехватки времени я привел пример из приложения, над которым я работаю, чтобы убедиться, что это работает.

@Esteban, код, который вы предоставили, прав, спасибо, но неполный, я его протестировал. В classе «UserEmail» отсутствуют свойства:

  public UserTest UserTest { get; set; } public EmailTest EmailTest { get; set; } 

Я отправляю код, который я тестировал, если кто-то заинтересован. С уважением

 using System.Data.Entity; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Web; #region example2 public class UserTest { public int UserTestID { get; set; } public string UserTestname { get; set; } public string Password { get; set; } public ICollection UserTestEmailTests { get; set; } public static void DoSomeTest(ApplicationDbContext context) { for (int i = 0; i < 5; i++) { var user = context.UserTest.Add(new UserTest() { UserTestname = "Test" + i }); var address = context.EmailTest.Add(new EmailTest() { Address = "address@" + i }); } context.SaveChanges(); foreach (var user in context.UserTest.Include(t => t.UserTestEmailTests)) { foreach (var address in context.EmailTest) { user.UserTestEmailTests.Add(new UserTestEmailTest() { UserTest = user, EmailTest = address, n1 = user.UserTestID, n2 = address.EmailTestID }); } } context.SaveChanges(); } } public class EmailTest { public int EmailTestID { get; set; } public string Address { get; set; } public ICollection UserTestEmailTests { get; set; } } public class UserTestEmailTest { public int UserTestID { get; set; } public UserTest UserTest { get; set; } public int EmailTestID { get; set; } public EmailTest EmailTest { get; set; } public int n1 { get; set; } public int n2 { get; set; } //Call this code from ApplicationDbContext.ConfigureMapping //and add this lines as well: //public System.Data.Entity.DbSet UserTest { get; set; } //public System.Data.Entity.DbSet EmailTest { get; set; } internal static void RelateFluent(System.Data.Entity.DbModelBuilder builder) { // Primary keys builder.Entity().HasKey(q => q.UserTestID); builder.Entity().HasKey(q => q.EmailTestID); builder.Entity().HasKey(q => new { q.UserTestID, q.EmailTestID }); // Relationships builder.Entity() .HasRequired(t => t.EmailTest) .WithMany(t => t.UserTestEmailTests) .HasForeignKey(t => t.EmailTestID); builder.Entity() .HasRequired(t => t.UserTest) .WithMany(t => t.UserTestEmailTests) .HasForeignKey(t => t.UserTestID); } } #endregion 

TLDR; (частично связанный с ошибкой редактора EF6 в EF6 / VS2012U5), если вы создаете модель из БД, и вы не можете увидеть атрибутную таблицу m: m: удалить две связанные таблицы -> Сохранить .edmx -> Сгенерировать / добавить из базы данных – > Сохранить.

Для тех, кто пришел сюда, задаваясь вопросом, как получить отношение «многие ко многим» с столбцами атрибутов для отображения в EF .edmx-файле (поскольку в настоящее время он не отображается и не рассматривается как набор навигационных свойств), И вы сгенерировали эти classы из вашей таблицы базы данных (или базы данных – сначала в MS lingo, я считаю.)

Удалите 2 таблицы, о которых идет речь (чтобы взять пример OP, член и комментарий) в вашем .edmx и добавить их снова через «Сгенерировать модель из базы данных». (т. е. не пытайтесь обновить Visual Studio – удалять, сохранять, добавлять, сохранять)

Затем он создаст третью таблицу в соответствии с тем, что предлагается здесь.

Это актуально в тех случаях, когда сначала добавляются отношения «чистый ко многим», а атрибуты впоследствии создаются в БД.

Это не было сразу видно из этой темы / Googling. Поэтому просто поместите его там, так как это ссылка № 1 на Google, которая ищет проблему, но, прежде всего, из стороны БД.

Один из способов решить эту ошибку – поместить атрибут ForeignKey поверх свойства, которое вы хотите в качестве внешнего ключа, и добавить свойство навигации.

Примечание. В ForeignKey , между круглыми и двойными кавычками, ForeignKey имя classа, упомянутого таким образом.

введите описание изображения здесь

Я хочу предложить решение, в котором могут быть достигнуты оба варианта конфигурации «многие-ко-многим».

«Улов» – это то, что нам нужно создать представление, которое нацелено на таблицу объединения, поскольку EF проверяет, что таблица схемы может отображаться не более одного раза на EntitySet .

Этот ответ добавляет к тому, что уже было сказано в предыдущих ответах, и не отменяет ни один из этих подходов, он основывается на них.

Модель:

 public class Member { public int MemberID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual ICollection Comments { get; set; } public virtual ICollection MemberComments { get; set; } } public class Comment { public int CommentID { get; set; } public string Message { get; set; } public virtual ICollection Members { get; set; } public virtual ICollection MemberComments { get; set; } } public class MemberCommentView { public int MemberID { get; set; } public int CommentID { get; set; } public int Something { get; set; } public string SomethingElse { get; set; } public virtual Member Member { get; set; } public virtual Comment Comment { get; set; } } 

Конфигурация:

 using System.ComponentModel.DataAnnotations.Schema; using System.Data.Entity.ModelConfiguration; public class MemberConfiguration : EntityTypeConfiguration { public MemberConfiguration() { HasKey(x => x.MemberID); Property(x => x.MemberID).HasColumnType("int").IsRequired(); Property(x => x.FirstName).HasColumnType("varchar(512)"); Property(x => x.LastName).HasColumnType("varchar(512)") // configure many-to-many through internal EF EntitySet HasMany(s => s.Comments) .WithMany(c => c.Members) .Map(cs => { cs.ToTable("MemberComment"); cs.MapLeftKey("MemberID"); cs.MapRightKey("CommentID"); }); } } public class CommentConfiguration : EntityTypeConfiguration { public CommentConfiguration() { HasKey(x => x.CommentID); Property(x => x.CommentID).HasColumnType("int").IsRequired(); Property(x => x.Message).HasColumnType("varchar(max)"); } } public class MemberCommentViewConfiguration : EntityTypeConfiguration { public MemberCommentViewConfiguration() { ToTable("MemberCommentView"); HasKey(x => new { x.MemberID, x.CommentID }); Property(x => x.MemberID).HasColumnType("int").IsRequired(); Property(x => x.CommentID).HasColumnType("int").IsRequired(); Property(x => x.Something).HasColumnType("int"); Property(x => x.SomethingElse).HasColumnType("varchar(max)"); // configure one-to-many targeting the Join Table view // making all of its properties available HasRequired(a => a.Member).WithMany(b => b.MemberComments); HasRequired(a => a.Comment).WithMany(b => b.MemberComments); } } 

Контекст:

 using System.Data.Entity; public class MyContext : DbContext { public DbSet Members { get; set; } public DbSet Comments { get; set; } public DbSet MemberComments { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Configurations.Add(new MemberConfiguration()); modelBuilder.Configurations.Add(new CommentConfiguration()); modelBuilder.Configurations.Add(new MemberCommentViewConfiguration()); OnModelCreatingPartial(modelBuilder); } } 

От ответа Салумы (@ Салума)

Если теперь вы хотите найти все комментарии участников с LastName = «Смит», например, вы можете написать запрос следующим образом:

Это все еще работает …

 var commentsOfMembers = context.Members .Where(m => m.LastName == "Smith") .SelectMany(m => m.MemberComments.Select(mc => mc.Comment)) .ToList(); 

… но теперь можно также …

 var commentsOfMembers = context.Members .Where(m => m.LastName == "Smith") .SelectMany(m => m.Comments) .ToList(); 

Или создать список членов с именем «Смит» (мы предполагаем, что их более одного) вместе с их комментариями вы можете использовать outlook:

Это все еще работает …

 var membersWithComments = context.Members .Where(m => m.LastName == "Smith") .Select(m => new { Member = m, Comments = m.MemberComments.Select(mc => mc.Comment) }) .ToList(); 

… но теперь можно также …

 var membersWithComments = context.Members .Where(m => m.LastName == "Smith") .Select(m => new { Member = m, m.Comments }) .ToList(); 

Если вы хотите удалить комментарий от пользователя

 var comment = ... // assume comment from member John Smith var member = ... // assume member John Smith member.Comments.Remove(comment); 

Если вы хотите Include() комментарии участника

 var member = context.Members .Where(m => m.FirstName == "John", m.LastName == "Smith") .Include(m => m.Comments); 

Все это похоже на синтаксический сахар, но при этом вы получаете несколько привилегий, если вы захотите пройти дополнительную настройку. В любом случае вы, похоже, сможете получить лучшее из обоих подходов.

Interesting Posts

Что такое функторы C ++ и их использование?

Как встраивать веб-страницу в документ MS Word

Подтверждение того, что Windows 7 использует оптимизацию SSD

Как вернуть только заголовок Http из tshark?

Как я могу разрешить ошибку «Не удалось загрузить файл или сборку: Microsoft.IdentityModel» во время конфигурации?

Есть ли способ заставить компьютер в спящий режим в определенное время?

Утилита для контроля скорости вращения вентилятора и т. Д.

Есть ли способ переименовать файлы в нижний регистр?

Как вызвать код Objective-C из Swift

Каков путь к каталогу корзины?

Как обмануть Windows 10, чтобы подумать, что у нее больше RAM, а потом?

Сортировка строк в Notepad ++ без плагина TextFx

Каковы «измененные» и «резервные» области ОЗУ в Мониторе ресурсов?

Командная строка SVG и просмотрщик файлов изображений в Linux?

Я получаю красную полосу при попытке установить Windows 7 DVD, и она зависает

Давайте будем гением компьютера.