contract@chaoreumsoft.co.kr |    031-921-0523

연결된 시나리오에서 데이터 저장 및 쿼리

페이지 정보

작성자최고관리자

본문

연결된 시나리오에서 엔터티 데이터를 저장하는 것은 컨텍스트가 수명 동안 엔터티에서 발생한 변경 사항을 자동으로 추적하기 때문에 상당히 쉬운 작업입니다.


Entity Framework에는 엔터티 데이터를 저장하는 두 가지 지속성 시나리오(연결됨 및 연결 끊김)가 있습니다. 


연결된 시나리오에서는 DbContext의 동일한 인스턴스가 엔터티를 검색하고 저장하는 데 사용되지만 연결이 끊긴 시나리오에서는 다릅니다. 


이장에서는 연결된 시나리오에서 데이터를 저장하는 방법을 배웁니다.


다음 그림은 연결된 시나리오에서 CUD(Create, Update, Delete) 작업을 보여줍니다.


0dc9464e270a7c53b10f992ce51c360e_1651217790_582.png
 

위 그림과 같이 Entity Framework는 DbContext.SaveChanges() 메서드가 호출될 때 EntityState가 Added, Modified 또는 Deleted인 엔터티에 대해 INSERT, UPDATE 및 DELETE 문을 빌드하고 실행 합니다. 연결된 시나리오에서 DbContext의 인스턴스는 모든 엔터티를 추적하므로 엔터티가 생성, 수정 또는 삭제될 때마다 각 엔터티에 적절한 EntityState를 자동으로 설정합니다.


데이터 삽입


DbSet.Add 메서드를 사용하여 새 엔터티를 컨텍스트(DbContext의 인스턴스)에 추가합니다. 그러면 SaveChanges() 메서드를 호출할 때 데이터베이스에 새 레코드가 삽입됩니다.

using (var context = new SchoolDBEntities())
{
    var std = new Student()
    {
        FirstName = "Bill",
        LastName = "Gates"
    };
    context.Students.Add(std);

    context.SaveChanges();
}

위의 예에서 context.Students.Add(std)는 새로 생성된 Student 엔터티 인스턴스를 Added EntityState가 있는 컨텍스트에 추가 합니다. context.SaveChanges() 메서드는 데이터베이스에 대해 다음 INSERT 문을 빌드하고 실행 합니다.


exec sp_executesql N'INSERT [dbo].[Students]([FirstName], [LastName])
VALUES (@0, @1)
SELECT [StudentId]
FROM [dbo].[Students]
WHERE @@ROWCOUNT > 0 AND [StudentId] = scope_identity()',N
''@0 nvarchar(max) ,@1 nvarchar(max) ',@0=N'Bill',@1=N'Gates'
go


데이터 업데이트 

연결된 시나리오에서 EF Core API는 컨텍스트를 사용하여 검색된 모든 엔터티를 추적합니다. 따라서 엔터티 데이터를 편집할 때 EF는 자동으로 EntityState를 수정됨으로 표시하므로 SaveChanges() 메서드를 호출할 때 데이터베이스에서 문이 업데이트됩니다.

using (var context = new SchoolContext())
{
    var std = context.Students.First<Student>(); 
    std.FirstName = "Steve";
    context.SaveChanges();
}

위의 예에서는 context.Students.First<student>()를 사용하여 데이터베이스에서 첫 번째 학생을 검색합니다. FirstName을 수정하는 즉시 컨텍스트는 DbContext 인스턴스(컨텍스트)의 범위에서 수행된 수정으로 인해 EntityState를 Modified로 설정합니다. 따라서 SaveChanges() 메서드를 호출하면 데이터베이스에서 다음 Update 문을 빌드하고 실행합니다.

exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Students] SET [FirstName] = @p0
WHERE [StudentId] = @p1;
SELECT @@ROWCOUNT;
',N'@p1 int,@p0 nvarchar(4000)',@p1=1,@p0=N'Steve'
Go

업데이트 문에서 EF Core API는 값이 수정된 속성을 포함하며 나머지는 무시됩니다. 위의 예에서는 FirstName 속성만 편집되었으므로 update 문에는 FirstName 열만 포함됩니다.


데이터 삭제

DbSet.Remove() 또는 DbContext.Remove 메서드를 사용하여 데이터베이스 테이블의 레코드를 삭제합니다.

using (var context = new SchoolContext())
{
    var std = context.Students.First<Student>();
    context.Students.Remove(std);

    // or
    // context.Remove<Student>(std);

    context.SaveChanges();
}

위의 예에서 context.Students.Remove(std) 또는 context.Remove<Students>(std)는 std 엔터티 개체를 삭제됨으로 표시합니다. 따라서 EF Core는 데이터베이스에서 다음 DELETE 문을 빌드하고 실행합니다.

exec sp_executesql N'SET NOCOUNT ON;
DELETE FROM [Students]
WHERE [StudentId] = @p0;
SELECT @@ROWCOUNT;
',N'@p0 int',@p0=1
Go

따라서 연결된 시나리오에서 Entity Framework Core의 데이터를 추가, 업데이트 또는 삭제하는 것은 매우 쉽습니다.



Entity Framework Core에서 쿼리 

Entity Framework Core의 쿼리는 더 최적화된 SQL 쿼리와 C#/VB.NET 함수를 LINQ-to-Entities 쿼리에 포함하는 기능을 통해 EF 6.x와 동일하게 유지됩니다.


쿼리의 C#/VB.NET 함수 

EF Core에는 쿼리에 C# 또는 VB.NET 함수를 포함할 수 있는 LINQ-to-Entities의 새로운 기능이 있습니다. EF 6에서는 불가능했습니다.

private static void Main(string[] args)
{
    var context = new SchoolContext();
    var studentsWithSameName = context.Students
                                      .Where(s => s.FirstName == GetName())
                                      .ToList();
}

public static string GetName() {
    return "Bill";
}

위의 L2E 쿼리에서 Where 절에 GetName() C# 함수를 포함했습니다. 그러면 데이터베이스에서 다음 쿼리가 실행됩니다.

exec sp_executesql N'SELECT [s].[StudentId], [s].[DoB], [s].[FirstName], 
    [s].[GradeId], [s].[LastName], [s].[MiddleName]
FROM [Students] AS [s]
WHERE [s].[FirstName] = @__GetName_0',N'@__GetName_0 nvarchar(4000)',
    @__GetName_0=N'Bill'
Go

Eager Loading 

Entity Framework Core는 Include() 확장 메서드 및 프로젝션 쿼리를 사용하여 EF 6과 마찬가지로 관련 엔터티의 즉시 로드를 지원합니다. 이 외에도 여러 수준의 관련 엔터티를 로드하는 ThenInclude() 확장 메서드도 제공합니다. (EF 6은 ThenInclude() 메서드를 지원하지 않습니다.)


Include 

EF 6과 달리 Include() 메서드의 매개 변수로 람다 식을 지정하여 아래와 같이 탐색 속성을 지정할 수 있습니다.


var context = new SchoolContext();

var studentWithGrade = context.Students
                           .Where(s => s.FirstName == "Bill")
                           .Include(s => s.Grade)
                           .FirstOrDefault();

위의 예에서 .Include(s => s.Grade)는 단일 SQL 쿼리에서 데이터베이스의 Student 엔터티 데이터와 함께 로드할 참조 속성을 지정하기 위해 람다 식 s => s.Grade를 전달합니다. 위 쿼리는 데이터베이스에서 다음 SQL 쿼리를 실행합니다.

SELECT TOP(1) [s].[StudentId], [s].[DoB], [s].[FirstName], [s].[GradeId],[s].[LastName], 
        [s].[MiddleName], [s.Grade].[GradeId], [s.Grade].[GradeName], [s.Grade].[Section]
FROM [Students] AS [s]
LEFT JOIN [Grades] AS [s.Grade] ON [s].[GradeId] = [s.Grade].[GradeId]
WHERE [s].[FirstName] = N'Bill'

EF 6에서와 같이 Include() 메서드에서 속성 이름을 문자열로 지정할 수도 있습니다.

var context = new SchoolContext();

var studentWithGrade = context.Students
                        .Where(s => s.FirstName == "Bill")
                        .Include("Grade")
                        .FirstOrDefault();

속성 이름의 철자가 틀리거나 존재하지 않는 경우 런타임 예외가 발생하므로 위의 예는 권장되지 않습니다. 컴파일 시간 동안 오류를 감지할 수 있도록 항상 람다 식과 함께 Include() 메서드를 사용합니다.


Include() 확장 메서드는 아래와 같이 FromSql() 메서드 뒤에도 사용할 수 있습니다.

var context = new SchoolContext();

var studentWithGrade = context.Students
                        .FromSql("Select * from Students where FirstName ='Bill'")
                        .Include(s => s.Grade)
                        .FirstOrDefault();   

참고: Include() 확장 메서드는 DbSet.Find() 메서드 이후에 사용할 수 없습니다. 예를 들어 EF Core 2.0에서는 context.Students.Find(1).Include()가 불가능합니다. 이는 향후 버전에서 가능할 수 있습니다.


Multiple Include 

Include() 메서드를 여러 번 사용하여 동일한 엔터티의 여러 탐색 속성을 로드합니다. 예를 들어 다음 코드는 학생의 Grade 및 StudentCourses 관련 엔터티를 로드합니다.

var context = new SchoolContext();

var studentWithGrade = context.Students.Where(s => s.FirstName == "Bill")
                        .Include(s => s.Grade)
                        .Include(s => s.StudentCourses)
                        .FirstOrDefault();

위의 쿼리는 단일 데이터베이스 왕복에서 두 개의 SQL 쿼리를 실행합니다.


SELECT TOP(1) [s].[StudentId], [s].[DoB], [s].[FirstName], [s].[GradeId], [s].[LastName], 
        [s].[MiddleName], [s.Grade].[GradeId], [s.Grade].[GradeName], [s.Grade].[Section]
FROM [Students] AS [s]
LEFT JOIN [Grades] AS [s.Grade] ON [s].[GradeId] = [s.Grade].[GradeId]
WHERE [s].[FirstName] = N'Bill'
ORDER BY [s].[StudentId]
Go

SELECT [s.StudentCourses].[StudentId], [s.StudentCourses].[CourseId]
FROM [StudentCourses] AS [s.StudentCourses]
INNER JOIN (
    SELECT DISTINCT [t].*
    FROM (
        SELECT TOP(1) [s0].[StudentId]
        FROM [Students] AS [s0]
        LEFT JOIN [Grades] AS [s.Grade0] ON [s0].[GradeId] = [s.Grade0].[GradeId]
        WHERE [s0].[FirstName] = N'Bill'
        ORDER BY [s0].[StudentId]
    ) AS [t]
) AS [t0] ON [s.StudentCourses].[StudentId] = [t0].[StudentId]
ORDER BY [t0].[StudentId]
Go

ThenInclude 

EF Core는 여러 수준의 관련 엔터티를 로드하는 새로운 ThenInclude() 확장 메서드를 도입했습니다. 다음 예를 고려하십시오.

var context = new SchoolContext();

var student = context.Students.Where(s => s.FirstName == "Bill")
                        .Include(s => s.Grade)
                            .ThenInclude(g => g.Teachers)
                        .FirstOrDefault();

위의 예에서 .Include(s => s.Grade) 는 Student 엔터티의 Grade 참조 탐색 속성을 로드합니다. .ThenInclude(g => g.Teachers)는 Grade 엔터티의 Teacher 컬렉션 속성을 로드합니다. thenInclude 메서드는 Include 메서드 다음에 호출해야 합니다. 위의 내용은 데이터베이스에서 다음 SQL 쿼리를 실행합니다.

SELECT TOP(1) [s].[StudentId], [s].[DoB], [s].[FirstName], [s].[GradeId], [s].[LastName],
         [s].[MiddleName], [s.Grade].[GradeId], [s.Grade].[GradeName], [s.Grade].[Section]
FROM [Students] AS [s]
LEFT JOIN [Grades] AS [s.Grade] ON [s].[GradeId] = [s.Grade].[GradeId]
WHERE [s].[FirstName] = N'Bill'
ORDER BY [s.Grade].[GradeId]
Go

SELECT [s.Grade.Teachers].[TeacherId], [s.Grade.Teachers].[GradeId], [s.Grade.Teachers].[Name]
FROM [Teachers] AS [s.Grade.Teachers]
INNER JOIN (
    SELECT DISTINCT [t].*
    FROM (
        SELECT TOP(1) [s.Grade0].[GradeId]
        FROM [Students] AS [s0]
        LEFT JOIN [Grades] AS [s.Grade0] ON [s0].[GradeId] = [s.Grade0].[GradeId]
        WHERE [s0].[FirstName] = N'Bill'
        ORDER BY [s.Grade0].[GradeId]
    ) AS [t]
) AS [t0] ON [s.Grade.Teachers].[GradeId] = [t0].[GradeId]
ORDER BY [t0].[GradeId]
go

Projection Query 

Include() 또는 ThenInclude() 메서드 대신 프로젝션 쿼리를 사용하여 여러 관련 엔터티를 로드할 수도 있습니다. 다음 예제에서는 Student, Grade 및 Teacher 엔터티를 로드하는 프로젝션 쿼리를 보여 줍니다.

var context = new SchoolContext();

var stud = context.Students.Where(s => s.FirstName == "Bill")
                        .Select(s => new
                        {
                            Student = s,
                            Grade = s.Grade,
                            GradeTeachers = s.Grade.Teachers
                        })
                        .FirstOrDefault();

위의 예에서 .Select 확장 메서드는 결과에 Student, Grade 및 Teacher 엔터티를 포함하는 데 사용됩니다. 위의 thenInclude() 메서드와 동일한 SQL 쿼리를 실행합니다.


Lazy Loading 

Entity Framework Core 2.0에서는 지연 로드가 지원되지 않습니다.


Explicit Loading 

Entity Explicit 로딩은 EF 6에서와 같은 방식으로 작동합니다.


Tag
Entity Framework Core 사용법, EF Core
© Chaoreumsoft Corp. All rights reserved.