< BACK TO BLOG

Many to many relationship on EntityFrameworkCore 6

πŸ—“οΈ2021-12-18

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 Repository


Profile picture

Pon Cheol Ku (ꡬ본철)

Software developer

Other sites

If does not find interesting topic, you might visit other site on below link.

Β© 2024, Built with Gatsby