8. EF Core Code First Model
Code First Model은 현재 사용하고자 하는 DB가 존재하지 않을 때 C#으로 Model을 정의하여 이를 토대로 DB를 생성하는 방식을 말합니다.
예를 들어 학생과 수업를 관리하는 DB가 필요하다고 할 때 학생은 다수의 수업에 대한 수강신청이 가능하고 하나의 수업에서는 여러 학생이 출석할 수 있습니다. 이때 학생과 수업 테이블 간에는 다대다 관계가 성립됩니다.
위와 같은 시나리오에서 모델을 정의하는 과정을 따라가 보면 우선 다음의 Namespace를 Import하고
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.SqlServer;
Student.cs와 Course.cs라는 2개의 클래스 파일을 추가합니다.
public class Student
{
public int StudentId { get; set; }
public string? StudentName { get; set; }
public ICollection<Course>? Courses { get; set; }
}
public class Course
{
public int CourseId { get; set; }
[Required]
[StringLength(60)]
public string? CourseName { get; set; }
public ICollection<Student>? Students { get; set; }
}
또한 Classes.cs라는 이름의 파일로 전의 예제에서 dotnet-ef를 통해 생성했던 데이터베이스 Context파일과 동일한 Context파일을 아래와 같이 작성합니다.
public class Classes : DbContext
{
public DbSet<Student>? Students { get; set; }
public DbSet<Course>? Courses { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// SQLite일때
// string path = Path.Combine(Environment.CurrentDirectory, "Classes.db");
// optionsBuilder.UseSqlite($"Filename={path}");
optionsBuilder.UseSqlServer("Server=.;user id=sa;password=1234;Database=Classes;MultipleActiveResultSets=True;");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().Property(s => s.StudentName).HasMaxLength(30).IsRequired();
Student student1 = new() { StudentId = 1, StudentName = "홍길동" };
Student student2 = new() { StudentId = 2, StudentName = "홍길순" };
Student student3 = new() { StudentId = 3, StudentName = "홍길석" };
Course csharp1 = new() { CourseId = 1, CourseName = "수학" };
Course csharp2 = new() { CourseId = 2, CourseName = "과학" };
Course csharp3 = new() { CourseId = 3, CourseName = "영어" };
modelBuilder.Entity<Student>().HasData(student1, student2, student3);
modelBuilder.Entity<Course>().HasData(csharp1, csharp2, csharp3);
modelBuilder.Entity<Course>().HasMany(c => c.Students).WithMany(s => s.Courses).UsingEntity(e => e.HasData(
new { CoursesCourseId = 1, StudentsStudentId = 1 },
new { CoursesCourseId = 1, StudentsStudentId = 2 },
new { CoursesCourseId = 1, StudentsStudentId = 3 },
new { CoursesCourseId = 2, StudentsStudentId = 2 },
new { CoursesCourseId = 3, StudentsStudentId = 3 }
));
}
}
예제를 보면 우선 Course클래스에서 CourseName에 적용했던 Annotation Property와 달리 Student의 StudentName에서는 Fluent API를 통해 최대길이 30자와 반드시 값이 필요하다는 제약조건을 설정하고 있습니다. 이미 언급했듯 제약조건을 설정하기 위해 한 가지 방법만을 사용할 필요는 없는 것입니다.
또한 다대다 관계에서 'CourseStudent'와 같은 중계 테이블을 생성하고 익명 타입을 통해 데이터를 채우고 있습니다. 이때 1번 수학에는 1, 2, 3모든 학생이 수강신청을 했으며 과학(2)은 2번 학생만, 영어(3)는 3번 학생만 수강신청을 하였습니다. 여기서 속성 이름(칼럼명)은 NavigationPropertyName규칙을 따릅니다. Courses가 Navigation이름, CourseId가 속성 이름이므로 CoursesCourseId라는 이름의 속성을 사용한 것입니다.
이것으로 DB를 생성하기 위한 기본 작업은 모두 마무리되었으며 위에서 정의한 규칙대로 실제 데이터베이스로 DB 생성 작업을 처리하는 로직을 아래와 같이 구현합니다.
using Microsoft.EntityFrameworkCore;
using (Classes db = new())
{
//기존 DB가 존재할 경우 삭제
bool deleted = await db.Database.EnsureDeletedAsync();
//Model로 부터 DB를 만들고 필요한 SQL Script를 생성
bool created = await db.Database.EnsureCreatedAsync();
//생성된 Script확인
//Console.WriteLine(a.Database.GenerateCreateScript());
//DB생성 후 기본데이터 확인
foreach (Student s in db.Students.Include(c => c.Courses))
{
Console.WriteLine($"{s.StudentName}학생의 신청과목 총{s.Courses.Count}개");
foreach (Course c in s.Courses)
{
Console.WriteLine($" {c.CourseName}");
}
}
}
//홍길동학생의 신청과목 총1개
// 수학
//홍길순학생의 신청과목 총2개
// 수학
// 과학
//홍길석학생의 신청과목 총2개
// 수학
// 영어
성공적으로 프로젝트가 실행되면 다음처럼 Classes라는 DB가 생성될 것입니다.
CourseName와 StudentName은 Required로 정의되었으므로 not null이 되었습니다.
(1) 향후 유지관리
위와 같은 방식으로 최초로 DB를 생성한 이후에는 경우에 따라 기존 DB 스키마를 변경해야 하는 과정이 필요할 수 있습니다. 이런 경우에는 예제에서처럼 Ensure~() 메서드를 사용해서는 안됩니다. 주석으로 명시된 것처럼 기존에 DB를 삭제하고 다시 새로운 DB를 생성하기 때문에 기존에 DB에 들어간 데이터는 보존하지 않게 됩니다.
변경사항이 적용되어야 하는 경우에는 대신 EF Core migrations를 사용할 수 있으며 더 자세한 내용은 아래 글을 참고하시기 바랍니다.
Migrations Overview - EF Core | Microsoft Docs
'.NET > C#' 카테고리의 다른 글
[C#] C#과 .NET6 시작하기 - 2. .NET 이해하기 (0) | 2022.06.24 |
---|---|
[C#] C#과 .NET6 시작하기 - 1. 개발환경설정 (0) | 2022.06.24 |
[C#] Entity Framework Core - 4. 데이터 조작과 트랜잭션 (0) | 2022.06.24 |
[C#] Entity Framework Core - 3. 질의하기및 Pattern 로드 (0) | 2022.06.24 |
[C#] Entity Framework Core - 2. 모델링(Modeling) (0) | 2022.06.24 |