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

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

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

QuickLookSatellite MAC OS, высокая производительность процессора

Windows 7 застряла в «Запуск Windows» при попытке загрузки компьютера

Как я могу поделиться своим проводным подключением к ноутбуку wifi?

Как удалить парольные фразы ключей SSH из памяти Mac OS?

Ошибки FFMPEG: «max_analyze_duration» «переполнение буфера» «слишком большой пакет» Что делать?

Как я могу сделать изображение с белым фоном и сделать его прозрачным

Форматирование списка маркеров при переносе текста рядом с изображением

Отключить звуковое сопровождение звуковой карты Windows

Являются ли адаптеры HDMI для VGA действительно специфичными для устройства?

Как изменить размер диска VirtualBox?

Как обновить с PHP v5.4 до PHP v5.5 в bitnami ubuntu

В Excel, сделав «Стоп-кавычки» в правом столбце вместо самого левого?

Как Mac OSX уделяет приоритетное внимание сетевым интерфейсам при маршрутизации?

Жесткий диск Bad Sector marking utility

Пакетная программа не работает так, как должна!

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