이 시나리오가 있습니다.
public class Member
{
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public string Message { get; set; }
public virtual ICollection<Member> 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 와의 연관성을 어떻게 구성 합니까? 아니면 연관 테이블을 작성하는 더 좋은 방법이 있습니까?
답변
사용자 정의 된 조인 테이블을 사용하여 다 대다 관계를 작성할 수 없습니다. 다 대다 관계에서 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();
또는 “Smith”라는 이름을 가진 멤버 목록을 만들려면 (둘 이상 있다고 가정) 투영을 사용할 수 있습니다.
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();
이제 조인 테이블의 속성을 기준으로 필터링 할 수 있습니다 (다 대다 관계에서는 불가능). 예 : 속성이 99 인 멤버 1의 모든 주석 필터링 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) 기존 멤버 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) 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
속성에 대한 외래 키 속성으로 감지되고 FK 속성이 Null을 허용하지 않는 유형이기 때문에 결국 계단식 삭제 삭제를 일으키는 관계가 필요합니다. 이 모델에서는 의미가 있다고 생각합니다.Member
Comment
int
답변
Slauma의 훌륭한 답변.
유창한 API 매핑을 사용 하여이 작업을 수행하는 코드를 게시합니다 .
public class User {
public int UserID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public ICollection<UserEmail> UserEmails { get; set; }
}
public class Email {
public int EmailID { get; set; }
public string Address { get; set; }
public ICollection<UserEmail> UserEmails { get; set; }
}
public class UserEmail {
public int UserID { get; set; }
public int EmailID { get; set; }
public bool IsPrimary { get; set; }
}
당신에 DbContext
파생 클래스 당신이 할 수 있습니다 :
public class MyContext : DbContext {
protected override void OnModelCreating(DbModelBuilder builder) {
// Primary keys
builder.Entity<User>().HasKey(q => q.UserID);
builder.Entity<Email>().HasKey(q => q.EmailID);
builder.Entity<UserEmail>().HasKey(q =>
new {
q.UserID, q.EmailID
});
// Relationships
builder.Entity<UserEmail>()
.HasRequired(t => t.Email)
.WithMany(t => t.UserEmails)
.HasForeignKey(t => t.EmailID)
builder.Entity<UserEmail>()
.HasRequired(t => t.User)
.WithMany(t => t.UserEmails)
.HasForeignKey(t => t.UserID)
}
}
그것은없는 다른 접근 방식과 허용 대답과 같은 효과가 더 나은도 악화.
편집 :
CreatedDate를 bool에서 DateTime으로 변경했습니다.
편집 2 : 시간 부족으로 인해 응용 프로그램에서 예제를 작성 하여이 작업을 수행하고 있는지 확인했습니다.
답변
@ Esteban, 제공 한 코드가 맞습니다. 감사하지만 불완전한 것으로 테스트했습니다. “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<UserTestEmailTest> 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<UserTestEmailTest> 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<yournamespace.UserTest> UserTest { get; set; }
//public System.Data.Entity.DbSet<yournamespace.EmailTest> EmailTest { get; set; }
internal static void RelateFluent(System.Data.Entity.DbModelBuilder builder)
{
// Primary keys
builder.Entity<UserTest>().HasKey(q => q.UserTestID);
builder.Entity<EmailTest>().HasKey(q => q.EmailTestID);
builder.Entity<UserTestEmailTest>().HasKey(q =>
new
{
q.UserTestID,
q.EmailTestID
});
// Relationships
builder.Entity<UserTestEmailTest>()
.HasRequired(t => t.EmailTest)
.WithMany(t => t.UserTestEmailTests)
.HasForeignKey(t => t.EmailTestID);
builder.Entity<UserTestEmailTest>()
.HasRequired(t => t.UserTest)
.WithMany(t => t.UserTestEmailTests)
.HasForeignKey(t => t.UserTestID);
}
}
#endregion
답변
다 대다 구성의 두 가지 맛을 모두 얻을 수있는 솔루션을 제안하고 싶습니다.
“캐치”는 조인 테이블을 대상으로하는 뷰를 만들어야합니다. EF는 스키마 테이블이 최대 한 번만 매핑 될 수 있는지 확인하기 때문입니다 EntitySet
.
이 답변은 이전 답변에서 이미 말한 내용에 추가되며 이러한 접근법 중 어느 것도 무시하지 않으며 그에 기반합니다.
모델:
public class Member
{
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ICollection<MemberCommentView> MemberComments { get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public string Message { get; set; }
public virtual ICollection<Member> Members { get; set; }
public virtual ICollection<MemberCommentView> 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<Member>
{
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<Comment>
{
public CommentConfiguration()
{
HasKey(x => x.CommentID);
Property(x => x.CommentID).HasColumnType("int").IsRequired();
Property(x => x.Message).HasColumnType("varchar(max)");
}
}
public class MemberCommentViewConfiguration : EntityTypeConfiguration<MemberCommentView>
{
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<Member> Members { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<MemberCommentView> 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);
}
}
Saluma ‘s (@Saluma) 답변에서
예를 들어 LastName = “Smith”인 멤버의 모든 주석을 찾으려면 다음과 같은 쿼리를 작성할 수 있습니다.
이것은 여전히 작동합니다 …
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();
또는 “Smith”라는 이름을 가진 멤버 목록을 만들려면 (둘 이상 있다고 가정) 투영을 사용할 수 있습니다.
이것은 여전히 작동합니다 …
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);
이것은 모두 구문 설탕처럼 느껴지지만 추가 구성을 기꺼이 수행하면 약간의 특권을 얻습니다. 어느 쪽이든 당신은 두 가지 접근법을 모두 최대한 활용할 수있는 것 같습니다.
답변
TLDR; (EF6 / VS2012U5의 EF 편집기 버그와 반 관련) DB에서 모델을 생성하고 해당 m : m 테이블을 볼 수없는 경우 : 두 관련 테이블 삭제-> .edmx 저장-> 데이터베이스에서 생성 / 추가- > 저장.
여기에 온 사람들은 EF .edmx 파일에 표시 할 속성 열과 다 대다 관계를 얻는 방법을 궁금해하는 사람들을 위해 (현재 탐색 속성 집합으로 표시되지 않고 처리하지 않기 때문에) 이러한 클래스를 생성했습니다. 귀하의 데이터베이스 테이블 (또는 MS lingo의 데이터베이스 우선)에서 믿습니다.
.edmx에서 문제의 테이블 2 개를 삭제하고 (OP 예제, 멤버 및 설명을 가져 오려면) ‘데이터베이스에서 모델 생성’을 통해 다시 추가하십시오. (즉, Visual Studio에서 업데이트하도록하지 마십시오. 삭제, 저장, 추가, 저장)
그런 다음 여기에 제안 된 내용에 따라 세 번째 테이블을 만듭니다.
이것은 순수한 다 대 다 관계가 처음에 추가되고 속성이 나중에 DB에서 설계되는 경우에 관련됩니다.
이 스레드 / 인터넷 검색에서 즉시 명확하지 않았습니다. Google에서 링크 # 1이므로 문제를 찾고 있지만 DB 측에서 먼저 오는 것처럼 링크를 넣으십시오.
답변
이 오류를 해결하는 한 가지 방법 ForeignKey
은 외래 키로 원하는 속성 위에 속성 을 배치 하고 탐색 속성을 추가하는 것입니다.
참고 : ForeignKey
속성에서 괄호와 큰 따옴표 사이에 이러한 방식으로 참조되는 클래스 이름을 배치하십시오.