먼저 둘다 Entity Framework Core에서 데이터 모델을 구성하는데에 사용하는 방법이다.
우선순위를 먼저 보자면 Fluent Api가 Data Annotation보다 우선순위가 높다. 둘 모두를 사용했을 경우에 Fluent api의 설정이 적용되나, 둘 다 사용했는데 두개의 내용이 서로 다르다면 그것부터가 문제다. 애초에 충돌할 코드를 짜지말아야 한다.
둘다 써야하는 것이 의무는 아니나, 둘 다 쓰는 것이 권장된다. 데이터 어노테이션은 모델을 내가 직접 보면서 수정을 해야하는데, 보기에 직관적이고, 테이블 간 관계를 설정하기에는 Fluent Api가 적절하다.
일단 뭐가 있는지 알아보아야 겠다.
Data Annotation
[Table(“테이블 이름”)] – 데이터베이스 테이블의 이름을 지정한다.
[Column(“열 이름”)] – 프로퍼티에 대응되는 컬럼의 이름을 지정한다.
[Key] – 프로퍼티를 주 키로 지정한다.
[DatabaseGenerated(DatabaseGeneratedOption.Identity)] – 기본 키의 값을 데이터베이스에서 자동으로 생성하도록 지정한다. 다른 옵션으로는 None과 Computed가 있다.
[Foreign Key(“참조하는 프로퍼티의 이름”)] – 외래키 관계를 설정한다.
[Required] – 필드가 필수임을 나타낸다. Not Null과 같다.
[MaxLength(Length)], [MinLength(Lenth)] – 문자열 프로퍼티의 최대 및 최소 길이를 지정한다.
[ConcurrencyCheck] – 동시성 충돌 처리에 사용될 프로퍼티를 지정한다.
체크용 어노테이션
[Range(Min, Max)] : 숫자가 지정된 범위 안에 있어야 한다.
[Compare(“다른 프로퍼티 이름”)] – 두 프로퍼티 값이 같아야 함을 지정하고, 주로 비밀번호 확인 필드에서 사용된다.
[EmailAddress] – 문자열이 유효한 이메일 주소 형식인지 검사한다.
[URL] – 문자열이 유효한 url 형식인지 검사한다.
Data Annotation – 테이블 관계 설정
[Foreign Key(“참조하는 프로퍼티의 이름”)] – 외래키 관계를 설정한다.
public class Author
{
public int AuthorId { get; set; }
public string Name { get; set; }
// 내비게이션 프로퍼티: 한 명의 Author는 여러 권의 Book을 가질 수 있음
public ICollection<Book> Books { get; set; }
}
이거를 갖다가 보면 위에 네비 프로퍼티를 쓸때 형을 인터페이스 안쓰고 구현체를 써도 되지만...!! 인터페이스를 쓰는 것이 권장된다고 한다.
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public int AuthorId { get; set; } // 외래 키
[ForeignKey("AuthorId")] // 이 외래 키는 Author 엔티티의 AuthorId와 연결됨
public Author Author { get; set; } // 내비게이션 프로퍼티
}
네비게이션 프로퍼티에 대해서도 자세히 알아야 한다.
네비게이션 프로퍼티
네비게이션 프로퍼티는 Entity Framework 에서 엔티티 간의 관계를 나타내는 프로퍼티이다. 이를 통해서 연관된 엔티티들 간에 탐색할 수 있으며, 코드 내에서 마치 객체 간의 관계를 다루듯 데이터베이스 테이블 간의 관계를 다룰 수 있다.
역할
var blogWithPostsAndComments = context.Blogs
.Where(b => b.BlogId == 1)
.Include(b => b.Posts)
.ThenInclude(p => p.Comments)
.FirstOrDefault();
이고 여기서 Blog와 Post는 1:n Post와 Comment도 1:n 관계이다.
따라서 include를 이용한 쿼리 작성 시에는 위에서부터 순차적으로 넘어온다.
결과로서 나오는 것은 Blog 엔티티의 인스턴스이다. 그래서 우리는 네비게이션 프로퍼티를 추가했던 것이었고
이제 그 안에서 LINQ를 쓰면 알맞게 딱 떨어진다.
Include를 쓰는 것보다는 사용자 정의타입을 혼용하는게 나을 수도 있다.
예를 들어서
var blogWithPostsAndComments = context.Blogs
.Where(b => b.BlogId == 1)
.Include(b => b.Posts)
.ThenInclude(p => p.Comments)
.FirstOrDefault();
위에서 보았던 이런 코드가 있다고 쳐보자.
근데 이런 경우에 만약에 Post안의 요소 중에 모든 것이 Null 이 아니라고 한다면은 괜찮겠지만, 순회를 해야하는 입장에서 순회 전에는 안에 무엇이 들었는지 모르지 않는가. 그래서 컴파일러는 빌드 전에 이것을 미리 경고한다. 물론 빌드는 되긴하지만 런타임에러가 솔직히 더 무섭다. 따라서 이렇게 사용할 수 있다.
var blogWithPostAndComments = context.Blogs
.Where( b=>b.BlogId == 1)
.Select( x=> new {
// 사용자 정의 타입을 만들어서 귀찮더라도 그렇게 주는 것이 마음도 편하고 좋다.
// 그리고 NULL 방지 로직은 본인이 만들어야 한다.
})
. ........~