EF Core 7 (이전 버젼에 추가된 기능) 학습 - slog

변화된 EF Core를 경험하고 다른 분들께도 도움이 될 만한 글을 생성하는 것을 목표로 슬로그를 시작합니다.
(완료하지 못한 슬로그가 누적되어서 조금 신경쓰이긴 하네요 ^^;)

  • 목표 (과정)
    • EF Core 패키지 설치 및 설정 (sqlite이용)
    • Nullable 활성화로 DbContext 구성
    • 마이그레이션 관련 정리
    • 동기/비동기 차이 확인 및 변경추적 동작 확인
    • 로깅 방법 확인
    • 새롭게 지원하는 기능 확인
    • sqlite를 ProgreSQL로 변환 (과정)
좋아요 2

MS에서 제공하는 문서입니다.

추가된 기능도 반영되어 있는지는 살펴봐야 합니다. EF Core 5에서 성능이 많이 향상된 점도 기대가 됩니다.
진행은 sqlite로 하고 그것을 ProgreSql로 변경할 예정입니다. ORM이라 가능한 것이겠지요.

EFCore용 ProgreSQL 패키지는

를 통해 잘 유지되고 있습니다. NuGet 패키지는

로 가장 최근의 미리보기 버젼까지 잘 지원하고 있습니다.

좋아요 2

sqlite로 EF Core를 시작하려면 다음의 패키지를 누겟에서 설치해야 합니다.

이것만 설치하면 EFCore 패키지 및 sqlite3 번들까지 설치가 됩니다.

좋아요 1

데이터베이스 스키마와 모델을 동기화 해야 합니다. 모델을 이용해 데이터베이스 스키마를 동기화 하는 방법은 마이그래이선을 이용하는 것인데

dotnet CLI 도구를 이용할 수 있습니다.

| dotnet ef 설치

dotnet tool install --global dotnet-ef

아니면 패키지 관리자 콘솔을 이용할 수 도 있습니다. 프로젝트에 Microsoft.EntityFrameworkCore.Tools를 추가합니다.

Install-Package Microsoft.EntityFrameworkCore.Tools

저는 Microsoft.EntityFrameworkCore.Tools를 이용할 예정입니다.

좋아요 1

콘솔로 프로젝트를 생성한 후 Microsoft.EntityFrameworkCore.SqliteMicrosoft.EntityFrameworkCore.Tools 패키지를 설치한 후

테이블이 될 모델과

| TodoInfo.cs

[Table(nameof(TodoInfo))]
[Index(nameof(IsComplete))]
[Index(nameof(TargetDate))]
public class TodoInfo
{
    /// <summary>
    /// 아이디 (PK)
    /// </summary>
    [Key]
    public string Id { get; set; } = Guid.NewGuid().ToString();
    /// <summary>
    /// 내용
    /// </summary>
    public string Detail { get; set; } = "";
    /// <summary>
    /// 완료유무
    /// </summary>
    public bool IsComplete { get; set; }
    /// <summary>
    /// 삭제유무
    /// </summary>
    public bool IsDeleted { get; set; }
    /// <summary>
    /// 목표일
    /// </summary>
    public DateOnly? TargetDate { get; set; }

    /// <summary>
    /// 완료일
    /// </summary>
    public DateOnly? CompletedDate { get; set; }
}

DbContext 코드를 생성합니다.

| TodoContext.cs

public class TodoContext : DbContext
{
    public DbSet<TodoInfo> Todos { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("Data Source=data.db");
    }
}

패키지 관리자 콘솔에서 Add-Migration first를 해서 first라는 마이그레이션을 만들고 Update-Database를 하면 data.db가 생깁니다.

Db Browser for SQLite를 이용해 data.db를 살펴보면 다음처럼 TodoInfo라는 테이블이 잘 생성되었음을 확인할 수 있으며,

image

간단하게 다음 코드를 통해

using var db = new TodoContext();

for (var i = 0; i < 10000; i++)
{
    var newTodo = new TodoInfo
    {
        Detail = $"간단한 EFCore 샘플 만들기 : {i + 1}",
        TargetDate = new DateOnly(2022, 5, 13)
    };

    db.Todos.Add(newTodo);
}    
db.SaveChanges();

실행 한 후 데이터를 조회하면 잘 데이터가 인서트되어 있음을 확인할 수 있습니다.

좋아요 1

그런데 신기하네요. 분명히 프로젝트 설정에

 <PropertyGroup>
    <Nullable>enable</Nullable>
  </PropertyGroup>

Nullable이 설정되어 있는데도 불구하고 TodoContextDbSet 속성에 경고가 표시되지 않습니다. 아마도 Microsoft.EntityFrameworkCore.Analyzers 분석기에서 해당 경고를 없애지 않을까 생각이 드네요.

좋아요 2

EF Core 6.0의 새로운 기능

EF Core 6.0의 주요 변경 사항

보호된 키를 사용해보기 위해 ID 형식을 만들고 사용해보았으나 변환기가 없다는 예외로 동작하지 않습니다. 관련된 학습은 좀 더 나중에 해야 할 것 같네요.

...
    [Key]
    public TodoInfoId Id { get; set; }
...

public class TodoInfoId
{
    public int Id { get; }

    public TodoInfoId(int id) => Id = id;
}  
System.InvalidOperationException: The 'TodoInfoId' property 'TodoInfo.Id' could not be mapped because the database provider does not support this type.
좋아요 1
좋아요 1

프로젝트 설정에서 nullable을 활성화 하면 참조형식을 할당하지 않으면 널 경고가 발생합니다.

신기하게 EF Core 7 미리보기 4에서는 DbContext의 DbSet 속성에서 널 관련 경고가 발생하지 않는데, EF Core 6에서는 발생하는 것으로 보아 이번 EF Core 7에서는 기존처럼 DbContext의 DbSet을 { get; set; } 으로 할당 없이 사용해도 경고를 표시하지 않도록 조치가 되는 것 같습니다.

EF Core 6에서 관련 경고를 없애러면 다음 처럼

...
    public DbSet<Entity> Entities => Set<Entity>();
...

으로 읽기 전용으로 만들어 줄 수 있습니다.

좋아요 1

마이그레이션

마이그레이션 도구를 이용해 EF Core 모델을 데이터베이스 스키마와 동기화 할 수 있습니다. EF Core를 사용하면 반드시 해야 하는데요, 데이터베이스에 반영되지 않으면 동작하지 않기 때문입니다.

EF Core는 dotnet CLI 도구 또는 패키지 관리자 콘솔 도구를 이용해 마이그레이션을 할 수 있습니다. 최근엔 dotnet CLI 도구를 사용하므로 도구를 설치하는 방법을 살펴봅시다.

dotnet tool install 명령어를 이용해 전역으로 설치할 수 있습니다.

dotnet tool install --global dotnet-ef

업데이트는 dotnet tool update를 통해 할 수 있습니다.

dotnet tool update --global dotnet-ef

이제는 개발에 필요한 설정이 간단해져서 너무 좋군요.

이후에 dotnet ef migrations add 명령을 통해 마이그레이션을 추가할 수 있는데, 마이그레이션을 추가하는 시점은 엔터티 또는 DB 컨텍스트가 변경되었을 때 수행하며 오류없이 마이그레이션이 추가되면 관련 마이그레이션 코드가 생성되고 dotnet ef database update를 통해 데이터베이스의 스키마에 반영할 수 있습니다.

마이그레이션을 잘못했을 경우 롤백해야 할 수 도 있습니다. dotnet ef migrations remove를 통해 할 수 있습니다.

마이그레이션 관련된 모든 명령어는 아래의 링크를 참조할 수 있습니다.

좋아요 1

비동기 프로그래밍

EF Core는 비동기 API를 제공합니다. 비동기 작업은 async/await 구조 덕분에 이제는 상당히 사용하기 쉬워졌습니다. 데이터베이스에 데이터를 저장하거나 질의하는 수행은 약간의 처리 시간이 필요하므로 비동기 API를 이용하면 좀 더 유리합니다.

EF Core는 .NET 표준에 따라 모든 동기 I/O에 대한 비동기 I/O를 제공합니다. SaveChanges() 메소드의 비동기 버젼은 SaveChangedAsync()~Async가 붙는 식입니다.

LINQ 명령어는 서버에서 평가되거나 클라이언트에서 평가됩니다. 서버에서 평가된다는 의미는 즉 DBMS의 쿼리로 해석된다는 것이고 클라이언트는 .NET의 API로 평가된다는 것인데, EF Core가 계속해서 버젼업 되면서 되도록이면 서버에서 평가되는 쿼리를 생성하는 추세입니다.

서버 및 클라이언트에서 평가되는 명령을 비동기로 처리하려면 다음의 코드처럼 사용해야 합니다.

var groupedHighlyRatedBlogs = await context.Blogs
    .AsQueryable()
    .Where(b => b.Rating > 3) // server-evaluated
    .AsAsyncEnumerable()
    .GroupBy(b => b.Rating) // client-evaluated
    .ToListAsync();

EF Core의 비동기 API는 DBMS 제공자에 따라 여러 제한이 있다고 합니다. 가령 여러 병렬 작업을 하나의 DB 컨텍스틍데서 처리할 수 없습니다. 또한 경험상 DBMS 제공자에 따라 상이하게 동작하기도 했습니다. 이 경험은 EF Core 6 이전의 경험이라 지금은 아닐 수도 있겠네요. 다양한 테스트를 해보고 그 결과도 공유해 보겠습니다.

좋아요 1

변경 추적

EF Core의 DB 컨텍스트의 DBSet에 엔터티를 추가하거나 삭제하고, 읽은 엔터티르 값을 변경하는 작업은 SaveChanges()가 호출될 때 모두 잘 반영이 됩니다. 이는 EF Core가 변경된 내용을 추적하기 때문인데요,

가령 조건에 따라 하나의 엔터티 정보를 데이터베이스에서 가져왔고 엔터티의 특정 속성만 변경한 후 SaveChanges()를 호출했을 때 별도의 다른 작업 없이 변경된 그 속성만 잘 변경이 됩니다.

내부적으로 SQL 쿼리가 생성이 되는데 효율적으로 변경되 그 속성만 UPDATE되는 쿼리문을 생성하는데요, 일종의 사용자 입장에서는 일종의 마법과 같은 동작입니다.

좋아요 1

로깅, 이벤트 및 진단

단순 로딩

단순 로딩은 OnConfiguring() 메소드에서 LogTo() 메소드로 설정하 룻 있습니다.

``csharp
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.LogTo(Console.WriteLine);


이후 관련된 모든 로그가 콘솔로 출력되게 됩니다.

### Microsoft.Extensions.Logging
EF Core는 `Microsoft.Extensions.Logging`의 로깅과 통합되기 때문에 ASP.NET Core의 로깅과 함께 사용할 수 있습니다.
https://docs.microsoft.com/en-us/ef/core/logging-events-diagnostics/extensions-logging?tabs=v3

### 이벤트
EF Core 5부터 사용할 수 있으며 이벤트 방식으로 동작을 관찰 할 수 있습니다. 자세한 것은 아래 링크를 통해 확인할 수 있습니다.

https://docs.microsoft.com/en-us/ef/core/logging-events-diagnostics/events

### 차단
EF Core 5부터 사용할 수 있으며 작업을 가로채서 수정 또는 억제할 수 있는 방법을 제공합니다.

자세한 내용은 아래 링크를 참조하세요.
https://docs.microsoft.com/en-us/ef/core/logging-events-diagnostics/interceptors

### 진단 수신기

.NET은 진단 수신기(DiagnosticListener)를 이용해 여러 동작을 청취할 수 있고 EF Core의 동작또한 청취할 수 있습니다.

아래처럼 유사하게 Observer를 구현한 후,
```csharp
public class DiagnosticObserver : IObserver<DiagnosticListener>
{
    public void OnCompleted()
        => throw new NotImplementedException();

    public void OnError(Exception error)
        => throw new NotImplementedException();

    public void OnNext(DiagnosticListener value)
    {
        if (value.Name == DbLoggerCategory.Name) // "Microsoft.EntityFrameworkCore"
        {
            value.Subscribe(new KeyValueObserver());
        }
    }
}

아래처럼 진단 수신기를 구독할 수 있습니다.

DiagnosticListener.AllListeners.Subscribe(new DiagnosticObserver());

자세한 내용은 아래 링크를 참조하세요.

좋아요 1