ᄀᄃᄀᄃ_ᄏ
September 22, 2024, 6:48am
1
안녕하세요 c# 학습중인 학생입니다.
간단한 커뮤니티 사이트 구축 연습을하는 도중 궁금한 사항이 생겨 질문을 남기게 되었습니다.
첫 번째로, "warning CS8625: Null 리터럴을 null을 허용하지 않는 참조 형식으로 변환할 수 없습니다. "경고가 발생하는데 혹시 현업에서는 해당 경고를 반드시 고치시는지 혹은 무시하시는 지가 궁금합니다.
두 번째로, 해당 경고가 발생하는 원인은 UserEntity와 UserDetailEntity에서 lazyloading 방식의
public required virtual UserDetailEntity와 public required virtual UserEntiy를 선언해서 그렇습니다. 회원가입 시 UserEntity를 먼저 db에 생성 한 후 UserDetail을 트랜잭션 처리하여 생성하는 방식인데, Entity에 대한 설계가 잘못된 것인지 궁금합니다. (required가 아닌 nullable을 주는게 옳게 된 설계인지에 대한 여부)
태그에 ef core 가 있기에, ef core 를 사용한다는 가정 하에 답변을 드립니다.
질문의 경고는 부차적인 것 같습니다.
ef 를 사용할 때, 모델 클래스의 navigation 속성에 required 를 부기하면, 둘은 필수적 관계로 설정됩니다.
필수적 관계는 Insert 할 때 두 객체 모두 not null 이어야 하며, Read 할 때도 항상 include (Join)되기에 주의해야 합니다.
만약, 필수적 관계가 의도된 것이라면, 아래와 같은 처리는 필수적 관계를 무시하는 처리입니다.
대신에 두 객체를 모두 트랙킹 시킨 후에 DB를 업데이트하는 방식을 사용해야 합니다.
var userDetails = ...;
var userEntity = new(...) { UserDetails = userDetails };
context.Users.Add(userEntity);
context.SaveChanges();
만약, 시나리오 상, UserDetails를 가지지 않은 UserEntity 가 (짧은 순간이라도) 존재할 수 있다면 필수적 관계 설정은 잘 못된 것입니다.
public class UserEntity
{
public UserDetails? UserDetails { get; set; }
// ...
개인적인 경험으로는, 단수 네비게이션 속성은 무지성적으로 nullable 로 선언하고, 필수적 관계가 필요하다면, 서브 객체의 configuration을 통해 명시적으로cascade 처리를 하는 것이 가장 무난한 방식인 것 같습니다.
이렇게 설정하면, 관련 경고는 사라질 것입니다.
참고로, 코드를 작성할 때, warning 뿐만 아니라, info 도 뜨지 않도록 작성합니다.
특히 다른 사람과 협업할 때는 더욱 그렇게 해야 합니다.
참조되는 모듈의 경고는 참조하는 모듈 작성자에게도 항상 뜨기 때문입니다.
ᄀᄃᄀᄃ_ᄏ
September 23, 2024, 12:23pm
3
해결하였습니다. 답변 주신것에 대해 다시한번 감사드립니다.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
답변 정말 감사합니다.
만약 nullable 처리를 한다면
.Select(u => new SearchUserForManageResponseDto.UserInfo
{*
Id = u.Id,*
FullName = u.UserDetail != null ? $"{u.UserDetail.FirstName} {u.UserDetail.LastName}" : "N/A",*
Nickname = u.UserDetail != null ? u.UserDetail.Nickname : "N/A",*
}*
이런식으로 모든 query문에 nullable관련 처리를 해야 하는데 이러면 성능과 가독성에 문제가 있지 않을까요?
가독성 차원의 문제가 아닙니다.
EF Core 의 장점 중 하나는 테이블의 관계가 코드로 드러난다, 다시 말하면 컴파일러의 도움을 받을 수 있다는 점입니다.
그 예시가 질문의 경고입니다.
니가 필수적 관계로 설정했잖아?
그런데, null 인 케이스가 내 눈에 보여. 확인해 봐!!
라고 컴파일러가 알려 주는 것입니다.
앞서 말씀드린 대로 쿼리 처리에 실수가 있었던 것이죠.
이 경우 쿼리의 처리를 필수적 관계를 준수하도록 바꾸는 게 해결책이지, 꼭 필요한 필수적 관계를 와해 시키는 처리는 옳다고 할 수 없습니다.
만약 런타임 중에 필수적 속성이 null 이라서 문제가 생겼다면, 이는 DB의 레코드에 문제가 있다는 징후로 해석해야지 필수적 관계를 설정한 게 문제라고 해석하면 안되는 것이죠.
두 엔티티가 필수적 관계가 아닌 경우에도, 속성이 null인 상황은 정상적인 것입니다.
오히려 비정상적인 일은 nullable 객체에 접근하기 전에 null 확인을 안 하는 것입니다. 컴파일러는 이 경우에도 경고를 주는 것입니다. (고맙게도요)
null 확인 코드는 매우 정상이고, 상식적인 것이라, 가독성과 아무런 상관이 없습니다. 가독성과 재사용성을 따진다면, 아래의 verbose 한 코드보다는
FullName = u.UserDetail != null ? $"{u.UserDetail.FirstName} {u.UserDetail.LastName}" : "N/A",
밑 작업을 먼저 해 놓고,
class UserDetails
{
public string Culture { get; set; }
// ...
public string FullName() => Culture switch
{
"ko" or "ch" => $"{LastName} {FirstName}",
_ => $"{FirstName} {LastName}"
}
// ...
이 밑작업으로 인해, 호출하는 코드가 간결해지는 게 가독성이 좋은 코드죠.
FullName = u.UserDetail?.FullName() ?? "N/A",
2 Likes