EF Core - slog

EF Core를 사용하면서 공유할 만한 내용을 계속 뎃글로 달겠습니다.

2개의 좋아요

record를 이용한 일반적인 엔티티 형태

    public record Slogger(string Id, string Name, string NickName, string Email, bool ConfirmEmail)
    {
        [Key]
        public string Id { get; init; } = Id;
        [MaxLength(32)]
        public string Name { get; init; } = Name;
        [MaxLength(32)]
        public string NickName { get; init; } = NickName;
        [MaxLength(64)]
        public string Email { get; init; } = Email;
        public bool ConfirmEmail { get; init; } = ConfirmEmail;
    }
2개의 좋아요

소스코드에서 데이터베이스 마이그레이션을 하려면

dbContext.Database.Migration()

컴럼이 추가/삭제 되거나, FK가 변경되거나 데이터형이 변경되는 등 이럴때 데이터도 자동으로 마이그레이션 해주면 좋겠지만 데이터는 수동으로 마이그레이션 해줘야 하는 상황이 종종 생깁니다. 이것은 비단 코드 방식 뿐만 아니라 커맨드 방식도 마찬가지입니다.
(Add-Migration 후 데이터를 수동으로 마이그레이션 하지 않으면 Update-Database 가 실패하는 경우가 종종 생김)

그래서 실제 현업에서는 저 Migration()를 쓸일이 그닥 없게 됩니다. Microsoft문서에서도 저 방법 보다는 SQL 스크립트를 생성해서 마이그레이션 하는것을 권장합니다.

  • 프로덕션 환경에서이 방법을 사용 하기 전에 신중 하 게 고려해 야 합니다. 환경에서이 배포 전략의 단순성은 생성 되는 문제에 의해 얻게 됩니다. 대신 마이그레이션에서 SQL 스크립트를 생성 하는 것이 좋습니다.
2개의 좋아요

정보를 저장하거나 변경할 때 상태에 따른 다양한 동작이 필요하는데요,
EF Core는 변경 추적 기능이 있어서 SaveChanged를 호출할 때 자연스럽게 변경내용이 적용됩니다.

하지만 대부분의 경우, API 서비스를 구성하게 되면서 질의 지점과 변경 지점의 계층이 분리되기 때문에 DbContext로 계속 변경지점을 추척할 수 없습니다.

그래서 데이터를 변경하였을 때 감지하여 적절하게 Add 또는 Update를 해주면 편리할 텐데요,

record와 결합하여 다음과 같이 사용할 수 있습니다.

public abstract record BaseRecord : ISettableBaseRecord
    {
        [NotMapped]
        public bool IsStored { get; private set; }
        [NotMapped]
        public bool IsChanged { get; private set; }


        public BaseRecord(BaseRecord record)
        {
            IsStored = record.IsStored;
            IsChanged = true;
        }

        void ISettableBaseRecord.SetSaved()
        {
            IsStored = true;
            IsChanged = false;
        }
    }

    public interface ISettableBaseRecord
    {
        void SetSaved();
    }
    public record Slogger(string Id, string Name, string NickName, string Email, bool ConfirmEmail) : BaseRecord
    {
        [Key]
        public string Id { get; init; } = Id;
        [MaxLength(32)]
        public string Name { get; init; } = Name;
        [MaxLength(32)]
        public string NickName { get; init; } = NickName;
        [MaxLength(64)]
        public string Email { get; init; } = Email;
        public bool ConfirmEmail { get; init; } = ConfirmEmail;
    }

이후 BaseRecord를 사용받아 엔티티들을 구현하면, 엔티티 인스턴스마다 상태관리가 가능해집니다.

var slogger = new Slogger("dimohy", "정세일", "디모이", "dimohy@email.com", false);

// 아직 DB에 저장하지 않은 상태
Console.WriteLine(slogger);

// 저장 후
(slogger as ISettableBaseRecord).SetSaved();

// 저장 후 상태
Console.WriteLine(slogger);

// 수정 발생
var slogger2 = slogger with { Name = "정세팔" };
Console.WriteLine(slogger2);

출력결과

Slogger { IsStored = False, IsChanged = False, Id = dimohy, Name = 정세일, NickName = 디모이, Email = dimohy@email.com, ConfirmEmail = False }
Slogger { IsStored = True, IsChanged = False, Id = dimohy, Name = 정세일, NickName = 디모이, Email = dimohy@email.com, ConfirmEmail = False }
Slogger { IsStored = True, IsChanged = True, Id = dimohy, Name = 정세팔, NickName = 디모이, Email = dimohy@email.com, ConfirmEmail = False }

2개의 좋아요

EF Core에 C# 9 record를 사용하려는 사람이 아직은 많지는 않을 텐데요
현재는 인자에 KeyAttribute등의 attribute를 부여할 수 없기 때문에 record recordName([Key] string Id, ...) 형태를 쓸 수 가 없습니다. 인자에 [Key]등의 Attribute를 부여할 수 있게 EF Core가 변화가 필요할 것 같습니다.

거기다가 <Nullable>enable</Nullable>을 할 경우 기존 방식처럼 EF Core를 써왔을 경우 엔티티 속성에 수많은 경고가 뜨는걸 볼 수 있습니다.

2개의 좋아요

디자인 타임에서 DbContext 생성

Add-Migration, Update-Database 명령을 사용하려면, 디자인타임에 ConnectionString을 지정해서 DB에 접속 가능해야 합니다.

다음과 같은 DesignTimeDbContextFactory를 구현하면 디자인 타임에서 DbContext 인스턴스를 생성하여 마이그레이션 등의 처리를 할 수 있습니다.

    public class SlogContextFactory : IDesignTimeDbContextFactory<SlogContext>
    {
        public SlogContext CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<SlogContext>();
            var config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
            var connectionString = config["ConnectionStrings:postgresql"];
            optionsBuilder.UseNpgsql(connectionString);

            return new(optionsBuilder.Options);
        }
    }
2개의 좋아요