자바를 쓸때는 mybatis나 프로시저를 쓰다가 asp.net으로 넘어오면서 linq라는게 있길래 linq로 작업을 하고 있습니다.
혼자 공부하면서 만든 거라서 제대로 사용하고 있는 게 맞는지 어려움을 겪고 있던 참에 다른 프로젝트를 외주에 맡긴다고 하여 그 소스들을 보고 공부하려고 하는데 모르는 걸 해결할겸 linq로 받을지 아니면 dampper 또는 EF 다른 방식을 사용해볼지 고민입니다.
보통 asp.net은 어떤 방식을 많이 이용하나요?
EF Core를 쓴다면, Linq 로 받는 게 Type safety 가 보장되서, 안전하고 코드 유지보수성도 좋습니다.
다만, IEnumerable 쪽 Linq 인지, IQueryable 쪽 Linq인지 반드시 확인하는 게 좋습니다.
그리고, 아래 영상이 도움이 되실 것 같습니다.
답변감사합니다! 새로운 걸 알아가네요!!
EF 코어가 쿼리로 변환하는 Linq 는 IQueryable 쪽입니다. 잘못 호출하면, 속도가 백배 이상 느려질 수 있습니다.
var count = myDbContext.Customers
.Count(); // IQueryable.Count() => DB 가 Count()를 반환.
count = myDbContext.Customers
.ToList() // Select * from customers => 모든 데이터 메모리 적재
.Count; // 메모리에 있는 데이터 갯수 반환.
얼핏보면 바보같은 코드와 비교하는 듯 보일지라도, 위와 같은 실수가 적지 않다고 하네요.
자료가 몇 만개만 넘어가도, 재앙적 상황이죠. 눈에 잘 띄지 않아 발견하기도 어렵습니다.
성능적인 측면 말고도, 데이터베이스에 불필요한 쿼리를 날릴 여지 - 또는 - 예기치 않은 호출 부작용을 막기 위한 취지로 IEnumerable 타입을 일부러 반환하기도 합니다. 참고로 IQueryable이 IEnumerable보다 더 구체화된 타입입니다. (참고: What to Return from Repositories: IQueryable, IEnumerable or IReadOnlyList? | by Sasha Mathews | Level Up Coding)
그래서 LINQ 확장 메서드를 쓸 때 충분히 살펴야 하는 것이, 호출하려는 함수가 IQueryable로 쿼리 확장의 여지를 두는 타입을 반환하는 것인지, 아니면 쿼리를 “완결짓고” 결과를 반환하는 것인지가 매우 중요합니다.
모든 경우에 해당된다고 볼 수는 없지만, 일반적으로 To~ 로 시작하는 함수들이 보통 쿼리를 "완결짓는다"는 컨벤션을 사용하는 경향이 있습니다. 그리고 @BigSquare 님께서 말씀하신 상황이 터부시되는 상황만은 아닌 것이, 정말 대용량의 데이터를 fetch해야 되는 경우도 틀림없이 있습니다. 이 경우, 블로킹을 막고, 데이터 fetch가 다 끝난 후 통지를 받도록 메커니즘을 설계한 ToListAsync 같은 TPL 계통 메서드를 제공하도록 업데이트되기도 했으니 상황에 따라 적절하게 채택해서 사용해야 한다고 생각합니다.
역시 닷넷데브…
닷넷에 미치지 않고는 모를 내용들이 여기선 이렇게 쉽게 접할 수 있네요. 놀랍습니다ㅎㅎ
Select * From {table} 을 터부시하는 것은 SQL 의 기초 편에서 누구나 배우는 것이고, 저도 그렇게 배웠습니다.
그런데, DB 내부가 아니라, 코드에서 사용할 목적으로 Fetching 할 수도 있다는 점은 꽤 충격적이네요.
ToListAsync 라고 메모리를 잡아 먹지 않은 것은 아니죠. 오히려 메모리만 따진다면, 상태 머신 만큼 더 먹게 되죠.
개발/테스트 단계라면 모를까, 설마 프로덕션 단계까지 그 코드를 남겨 두지는 않겠죠.
혹시 EF코어가 아니라면 어떤 방식을 추천하시나요???
개인적으로 Dapper를 좀 선호하긴 합니다.
레거시 프로젝트에서 ibatis를 사용하다가 신규 프로젝트 진행하면서
dapper를 사용하고 있는데 만족하면서 사용 중입니다.
쿼리로도 사용하고 sp 연결해서도 사용하고 linq로 결과값에 대해 처리하기도 하고 유용합니다.
개발 단계에서는 EF Core의 편리함과 안전함을 이길 수 있는 방법은 없는 것 같습니다.
EF Core를 사용하다 보면, 생쿼리와 코드를 왔다 갔다 하는 것이 얼마나 위험하고, 귀찮은 일인지 체감하게 됩니다.
특히, EF Core의 데이터 베이스 디자인 도구는 데이터 베이스 우선 접근법과 코드 접근 우선법을 모두 지원하기 때문에 신규 프로젝트 뿐만 아니라, WAS를 재설계하는 레거시 프로젝트라면, 여전히 사용할 수 있습니다.
조금 귀찮기는 하지만, 저장 프로시저를 통해 성능과 보안 이득을 노리는 방법이 있습니다.
아래의 방식은 유튜버 Tim Corey 님이 추천한 방식을 제가 약간 보완한 방식입니다.
이걸 하려면, DbContext를 미리 IRepository로 감싸 놔야 합니다.
안티 패턴이네 뭐네 해도 많은 분들이 이미 저장소 패턴을 사용하시기에 크게 달라지는 부분은 없을 것입니다.
그 다음, DbContext를 별도의 프로젝트로 옮깁니다.
만약, Asp.Net Core Identity 를 통해 인허가를 처리하고 있다면, 이 프레임워크가 생성한 ApplicationDbContext 는 그대로 웹 프로젝트에 남겨 두는 게 정신 건강에 이롭습니다. (특히 블레이저 서버라면요.)
어플리케이션 데이터만을 위한 DbContext를 별도의 라이브러리 프로젝트에 옮기고, 웹 프로젝트가 이 프로젝트를 참조합니다. 참고로, 저는 IdentityDbContext, AppDataDbContext 이렇게 작명합니다.
개발할 중에는 EF Core의 데이터 베이스 디자인 도구의 편리성과 Linq의 안전함을 충분히 누리시고…
개발 막바지에, DbContext 프로젝트의 IRepostitory 구현 클래스에 있는 Linq 코드와 동일하도록 저장 프로시저를 DB에 생성합니다.
그 다음, Dapper 프로젝트를 생성하고 여기에 IRepository 구현 클래스를 정의하는데, 모든 메서드는 앞서 정의한 저장 프로시저만을 호출하는 구조로 짭니다.
그 다음, DB에 저장 프로시저만 호출할 수 있는 유저를 생성하고, 이 유저의 로그인 정보를 포함하는 연결 문자열을 Dapper 프로젝트에 제공합니다.
개발/유지보수에는 EF 프로젝트를 사용하고, 퍼블리시 빌드 시에는 Dapper 프로젝트를 사용합니다.
이 방식에서는,
데이터 베이스 전 과정이 Type-safety 를 유지하면서도,
EF 도구를 통해 DB를 편리하게 관리할 수 있으며,
배포된 WAS는 Linq 를 파싱하는데 소모되는 자원을 아낄 수 있고,
운영 DB는 생쿼리를 컴파일하는 자원을 아낄 수 있고,
운영 중 연결 스트링이 노출되어도 생쿼리를 보낼 수 있는 권한이 없기 때문에 DB 보안성을 높일 수 있습니다.
답변 다들 감사합니다 ㅎㅎㅎ 새로운 내용이 너무 많아서 공부를 많이해야겠네요ㅠㅠ!