콘텐츠로 건너뛰기

Many to many relationship on EntityFrameworkCore 6

EntityFrameworkCore 6 에서 다대다 관계 구성이 향상되었다고 해서 관련 내용을 확인했습니다.

데이터 입출력시 중간 테이블을 거치지 않고 구현이 가능합니다.

GitHub: bbonkr/sample.ef.mtom 저장소에서 코드를 확인할 수 있습니다.

예제 테이블

Student

Name Nullable Constaint
Id NN PK
Name NN

Cource

Name Nullable Constaint
Id NN PK
Title NN

Enrollment

Name Nullable Constaint
StudentId NN PK
CourseId NN PK

엔티티 타입 구성

.net core 3.1

features/net3 브랜치에서 관련 코드를 확인할 수 있습니다.

public class EnrollmentEntityTypeConfiguration : IEntityTypeConfiguration<Enrollment>
{
    public void Configure(EntityTypeBuilder<Enrollment> builder)
    {
        builder.HasKey(x => new { x.StudentId, x.CourseId });

        builder.Property(x => x.StudentId)
            .IsRequired();

        builder.Property(x => x.CourseId)
            .IsRequired();

        builder.HasOne(x => x.Student)
            .WithMany(x => x.Enrollments)
            .HasForeignKey(x => x.StudentId);

        builder.HasOne(x => x.Course)
            .WithMany(x => x.Enrollments)
            .HasForeignKey(x => x.CourseId);                
    }
}

.NET 5

features/net5 브랜치에서 관련 코드를 확인할 수 있습니다.

.net core 3.1 구성에서 변경사항이 없습니다.

.NET 6

EnrollmentEntityTypeConfiguration 클래스를 제거하고, CourseEntityTypeConfiguration 클래스에서 다대다 관계를 설정합니다.

features/net6 브랜치 혹은 main 브랜치에서 관련 코드를 확인할 수 있습니다.

public class CourseEntityTypeConfiguration : IEntityTypeConfiguration<Course>
{
    public void Configure(EntityTypeBuilder<Course> builder)
    {
        builder.HasKey(x => x.Id);

        builder.Property(x => x.Id)
            .IsRequired()
            .ValueGeneratedOnAdd();

        builder.Property(x => x.Title)
            .IsRequired();

        builder.HasMany(x => x.Students)
            .WithMany(x => x.Courses)
            .UsingEntity<Enrollment>(
                j => j.HasOne(x => x.Student).WithMany(x => x.Enrollments).HasForeignKey(x => x.StudentId),
                j => j.HasOne(x => x.Course).WithMany(x => x.Enrollments).HasForeignKey(x => x.CourseId),
                j =>
                {
                    j.HasKey(x => new { x.StudentId, x.CourseId });
                });
    }
}

엔티티 타입 구성을 변경한 후 마이그레이션 코드를 작성해도 변경사항이 없습니다.

엔티티 타입 구성을 변경한 후 작성된 마이그레이션 코드: 20211218051811_Change mtom

사용

.net core 3.1

학생의 수업정보를 입력합니다.

var enrollments = Context.Courses
    .ToList()
    .Where((_, index) => index < value)
    .Select(x => new Enrollment
    {
        CourseId = x.Id,
    });

student.Enrollments
    .AddRange(enrollments);

학생 정보와 학생의 수업을 쿼리합니다.

var students = await Context.Students
    .Include(x => x.Enrollments)
    .Select(student => new
    {
        Name = student.Name,
        Courses = x.Enrollments.Select(enrollment => new
        {
            Title = enrollment.Course.Title,
        }),
    });

.NET 5

변경사항이 없습니다.

.NET 6

학생의 수업정보를 입력합니다.

Enrollments 를 사용해서 입력하지 않고, Courses 사용해서 입력할 수 있습니다.

var coursesToEnroll = Context.Courses
    .ToList()
    .Where((_, index) => index < value);

student.Courses.AddRange(coursesToEnroll);

학생 정보와 수업정보를 쿼리합니다.

Enrollments 를 사용하지 않고, Courses 를 쿼리할 수 있어 직관적으로 사용할 수 있다고 생각됩니다.

var students = await Context.Students
    .Include(x => x.Courses)
    .Select(student => new
    {
        Name = student.Name,
        Courses = student.Courses.Select(course => new
        {
            Title = course.Title,
        }),
    })

그 외

dotnet-ef tool

dotnet CLI 를 사용해서 dotnet 도구를 해당 프로젝트 범위에서 관리하려면, 매니페스트 파일을 먼저 작성해야 합니다.

$ dotnet new tool-manifest

참조:

nuget: dotnet-ef versions tab.

.net core 3.1

$ dotnet tool install --local dotnet-ef --version 3.1.22

.net 5

$ dotnet tool update --local dotnet-ef --version 5.0.13

.net 6

$ dotnet tool update --local dotnet-ef --version 6.0.1

마이그레이션 코드 작성

Sample.App 프로젝트를 시작프로젝트로 설정하고, AppDbContext 를 대상으로 Sample.Data.SqlServer 프로젝트에 마이그레이션 코드를 작성합니다.

$ cd src/Sample.Data
$ dotnet ef migrations add "Migrations name" --context AppDbContext --startup-project ../Sample.App --project ../Sample.Data.SqlServer 

마이그레이션 코드를 다른 프로젝트에서 관리할 수 있습니다.

결론

EFCore 6 에서 드디어 다대다 관계의 구현이 정리되어, 문제없이 원하는 모양으로 설정할 수 있었습니다.

그리고, 데이터 입출력도 직관적인 방식으로 구현이 가능해졌습니다.

GitHub

이 사이트는 광고를 포함하고 있습니다.
광고로 발생한 수익금은 서버 유지 관리에 사용되고 있습니다.

This site contains advertisements.
Revenue generated by the ad servers are being used for maintenance.

댓글 남기기