LinqToSql 에서 관련된 엔터티 혹은 엔터티들을 필터링 하는 방법이 있나요?

안녕하세요.
지나간 혹은 지워지고 있는 분야인 LinqToSql을 만지작하면서 있는데 질의하는 방법을 모르겠네요…

아래의 질문자 링크와 거의 같다고 보면됩니다.

Entity Framework에서는 Include가 있는 것 같은데요…


다시 돌아와서 제가 원하는 것은 아래와 같습니다.

쿼리로 표현하면

select
	us.UserName,
	dp.DeptName
from
	[User] us
	inner join DeptUser du on us.id = du.userId
	inner join Dept dp on du.deptId = dp.id
where
	us.userId ='jang'
	and du.active = 1

링큐로 표현한다면(물론 제가 원하는 바는 아닙니다. usdp를 join만 했고 select하지 않았기 때문)

from us in Users
where us.UserId == "jang"
join usdp in DeptUsers on us.Id equals usdp.UserId
where usdp.Active == true
select us

대충 결과는 아래와 같습니다.

말로써 표현하면
“jang이라는 아이디를 사용하는 사용자의 현재 부서의 정보를 구하라” 입니다.

다만 LINQ Class가 모두 ER 같이 ORM 모델로 이루어져 User기준으로 구하고 싶네요.
new를 사용하여 익명객채 사용하지 않구요.

좋아요 3

select new { us, usdp }를 해야 할 것 같은데요, 왜 익명 타입을 쓰면 안되나요?

좋아요 3

ORM을 온전히 사용해보고 싶네요.
특정 USER를 선택하고 DEPTUSER 속성에서 현재 부서를 선택하고, 선택된 DEPTUSER에서 DEPT를 속성으로 쭉 타고 구하면 좋을 것 같네요…
그러면 USER 객체를 구할 수 있고 관련된 정보는 모두 구조적으로 데이터를 가질 수 있으니까요. 알려진 타입이라 함수를 지역을 벗어날 수도 있고요.
분명 방법이 있을 것 같은데 거의 new를 해서 사용하다보니 원래 그려러고 했는지 모르겠지만 ORM 기능을 온전히 사용 해 보고 싶네요.

좋아요 3

지연로딩과 탐색 속성을 이용해서 원하시는게 가능합니다.

다만 LINQ 문법으로도 가능한지는 모르겠네요.

지연 로드 문서를 살펴보세요.

탐색 속성으로 원하시는 타고, 타고 들어가는게 가능합니다.

좋아요 3

탐색속성이 무엇을 의미하는지요 ? Include 명령어를 말씀하시는지 모르겠네요.

EF.Core로 Include,ThenInclude를 사용하여 원하는 바를 할 수 있는데, LINQ 는 포기해야 되는 것 같네요.

좋아요 3

탐색 속성은 EF에서 엔터티와 엔터티간의 관계를 형성하게 해주는 속성이며 관계 속성이라고도 할 수 있습니다. 아래 코드를 참조해주세요.

image

image

UserDept로 엔터티를 만든 후, 질문자님이 만드신 구조를 참조해서 다 대 다 탐색 속성을 부여했습니다.

| User

[Table(nameof(User))]
public record User
{
    [Key]
    public string UserId { get; set; } = default!;

    public string UserName { get; set; } = default!;


    public virtual ICollection<Dept> Depts { get; set; } = default!;
}

| Dept

public record Dept
{
    [Key]
    public string DeptId { get; set; } = default!;

    public string DeptName { get; set; } = default!;

    
    public virtual ICollection<User> Users { get; set; } = default!;
}

여기서 User의 Depts 속성과 Dept의 Users의 속성이 탐색 속성 입니다.

| Context

public class Context : DbContext
{
    public DbSet<User> Users => Set<User>();
    public DbSet<Dept> Depts => Set<Dept>();
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        // "database.db" 파일로 SQLite 사용
        optionsBuilder.UseSqlite("Data Source=database.db")
            .UseLazyLoadingProxies();
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
    }
}

SQLite로 간단한 DBContext를 만들고요, 중요한 건 .UseLazyLoadingProxies()지연 로딩을 활성화 해야 합니다.

데이터베이스 업데이트를 하면 다음의 테이블이 생성됩니다.

image

삽입

// 사용자 및 부서 삽입 {{{
var newUser = new User
{
    UserId = "test",
    UserName = "테스트",
    Depts = new List<Dept> { new() { DeptId = "test_grp", DeptName = "테스트 부서" } }
};

c.Users.Add(newUser);
c.SaveChanges();

이렇게 하면 데이터베이스의 테이블에 다음과 같이 값이 삽입 됩니다.

image

image

image

이제 탐색 속성지연 로딩에 의해 다음처럼 질의가 가능하게 됩니다.

var user = c.Users.Find("test"); // test 사용자를 찾아
if (user is null)
    return;

// 사용자명과 사용자의 부서명 출력
Console.WriteLine($"User : {user.UserName}({user.UserId})");
Console.WriteLine($"User Dept : {user.Depts.First().DeptName}");

샘플코드

좋아요 2

위의 코드는 탐색 속성지연 로딩을 이해하기 위해서 참고로 하시고 Active로 필터링은 자동으로 생성되는 DeptUser 대신 직접 DeptUser엔터티를 만드시고 탐색 속성으로 User-DeptUser-Dept 관계를 만드시면 됩니다.

좋아요 2

늦었지만 답변 감사합니다. 소스도 올려주셔서 감사합니다.
다만 결과물은 같을 수 있겠으나 과정이 조금 다른 것 아닌가 합니다.

저의 의도는 아래와 같이 lazy로딩과 관련 없이 relationship 있는 속성을 비즈니스 코드를 적용하여 확증적 으로 조회하여 바로 사용 하는 것입니다.
또한 조회한 후 바로 직렬화 하는 경우입니다.
앞서 언급한 바와 같이 Include,ThenInclude가 저의 의도에는 더 맞는 것 갔습니다.

var use2 = c.Users.Include(u=> u.Depts.Where(d=>d.Active== true));
좋아요 3