[C#] 연관 테이블에 추가 필드를 사용하여 코드를 먼저 작성하십시오.

이 시나리오가 있습니다.

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간의 일대 다 관계도 있기 때문에 MemberMemberComments사이 Comment및이 MemberComments규칙에 의해 삭제 계단식으로 설정합니다. 그리고 MemberIdCommentIdin 및 및 탐색 MemberComment속성에 대한 외래 키 속성으로 감지되고 FK 속성이 Null을 허용하지 않는 유형이기 때문에 결국 계단식 삭제 삭제를 일으키는 관계가 필요합니다. 이 모델에서는 의미가 있다고 생각합니다.MemberCommentint


답변

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속성에서 괄호와 큰 따옴표 사이에 이러한 방식으로 참조되는 클래스 이름을 배치하십시오.

여기에 이미지 설명을 입력하십시오


답변