스캐폴딩(리버스 엔지니어링)

리버스 엔지니어링은 데이터베이스 스키마에 따라 엔터티 형식 클래스 및 DbContext 클래스를 스캐폴드하는 프로세스입니다. EF Core PMC(패키지 관리자 콘솔) 도구의 Scaffold-DbContext 명령 또는 .NET CLI(명령줄 인터페이스) 도구의 dotnet ef dbcontext scaffold 명령을 사용하여 수행할 수 있습니다.

참고 항목

여기에 설명된 DbContext 및 엔터티 형식의 스캐폴딩은 여기에 설명되지 않은 Visual Studio를 사용하는 ASP.NET Core 컨트롤러의 스캐폴딩과 다릅니다.

Visual Studio를 사용하는 경우 EF Core 파워 도구 커뮤니티 확장을 사용해 보세요. 이러한 도구는 EF Core 명령줄 도구를 기반으로 빌드되고 추가 워크플로 및 사용자 지정 옵션을 제공하는 그래픽 도구를 제공합니다.

필수 조건

  • 스캐폴딩하기 전에 Visual Studio에서만 작동하는 PMC 도구또는 .NET에서 지원하는 모든 플랫폼에서 .NET CLI 도구설치해야 합니다.
  • 스캐폴드하는 프로젝트의 Microsoft.EntityFrameworkCore.Design에 NuGet 패키지를 설치합니다.
  • 스캐폴드할 데이터베이스 스키마를 대상으로 하는 데이터베이스 공급자에 대한 NuGet 패키지를 설치합니다.

필수형 인수

PMC 명령과 .NET CLI 명령에는 두 가지 필수 인수인 데이터베이스에 대한 연결 문자열과 사용할 EF Core 데이터베이스 공급자가 있습니다.

연결 문자열

명령에 대한 첫 번째 인수는 데이터베이스에 대한 연결 문자열입니다. 도구는 이 연결 문자열을 사용하여 데이터베이스 스키마를 읽습니다.

명령 문자열을 인용하고 이스케이프하는 방법은 명령 실행에 사용하는 셸에 따라 달라집니다. 자세한 내용은 셸 설명서를 참조하세요. 예를 들어 PowerShell에서는 $ 문자를 이스케이프해야 하지만 \ 문자는 이스케이프해선 안됩니다.

다음 예제에서는 컴퓨터의 SQL Server LocalDB 인스턴스에 있는 Chinook 데이터베이스에서 엔터티 형식 및 DbContext를 스캐폴드하여 Microsoft.EntityFrameworkCore.SqlServer 데이터베이스 공급자를 사용합니다.

dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook" Microsoft.EntityFrameworkCore.SqlServer

연결 문자열에 대한 사용자 비밀

호스팅 모델 및 구성 시스템(예: ASP.NET Core 프로젝트)을 사용하는 .NET 애플리케이션이 있다면 Name=<connection-string> 구문을 사용하여 구성에서 연결 문자열을 읽을 수 있습니다.

예를 들어 다음 구성 파일을 사용하여 ASP.NET Core 애플리케이션을 고려해 보세요.

{
  "ConnectionStrings": {
    "Chinook": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=Chinook"
  }
}

구성 파일의 이 연결 문자열은 다음을 사용하여 데이터베이스에서 스캐폴딩하는 데 사용할 수 있습니다.

dotnet ef dbcontext scaffold "Name=ConnectionStrings:Chinook" Microsoft.EntityFrameworkCore.SqlServer

그러나 연결 문자열을 구성 파일에 저장하는 것은 예를 들어 원본 제어로 푸시하여 실수로 노출하기 너무 쉽기 때문에 좋지 않습니다. 그 대신 연결 문자열은 Azure Key Vault를 사용하거나 로컬로 작업할 때는 "사용자 비밀"이라고도 하는 Secret Manager 도구 등을 사용하여 안전한 방식으로 저장해야 합니다.

예를 들어, 사용자 비밀을 사용하려면 먼저 ASP.NET Core 구성 파일에서 연결 문자열을 제거합니다. 다음으로, ASP.NET Core 프로젝트와 동일한 디렉터리에서 다음 명령을 실행하여 사용자 비밀을 초기화합니다.

dotnet user-secrets init

이 명령은 소스 코드와 별도로 컴퓨터에 스토리지를 설정하고 이 스토리지의 키를 프로젝트에 추가합니다.

다음으로, 연결 문자열을 사용자 비밀에 저장합니다. 예시:

dotnet user-secrets set ConnectionStrings:Chinook "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook"

이제 이전에 구성 파일에서 명명된 연결 문자열을 사용한 것과 동일한 명령이 사용자 비밀에 저장된 연결 문자열을 대신 사용합니다. 예시:

dotnet ef dbcontext scaffold "Name=ConnectionStrings:Chinook" Microsoft.EntityFrameworkCore.SqlServer

스캐폴드된 코드의 연결 문자열

기본적으로 스캐폴더는 스캐폴드된 코드에 연결 문자열을 포함하지만 경고가 표시됩니다. 예시:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
    => optionsBuilder.UseSqlServer("Data Source=(LocalDb)\\MSSQLLocalDB;Database=AllTogetherNow");

이렇게 하면 처음 사용할 때 생성된 코드가 충돌하지 않으므로 학습 환경이 매우 나빠집니다. 그러나 경고에 따르면 프로덕션 코드에는 연결 문자열이 없어야 합니다. 연결 문자열을 관리할 수 있는 다양한 방법은 DbContext 수명, 구성 및 초기화를 참조하세요.

-NoOnConfiguring(Visual Studio PMC) 또는 --no-onconfiguring(.NET CLI) 옵션을 전달하여 연결 문자열이 포함된 OnConfiguring 메서드를 만들지 않을 수 있습니다.

공급자 이름

두 번째 인수는 공급자 이름입니다. 공급자 이름은 일반적으로 공급자의 NuGet 패키지 이름과 동일합니다. 예를 들어, SQL Server 또는 Azure SQL의 경우 Microsoft.EntityFrameworkCore.SqlServer를 사용합니다.

명령줄 옵션

스캐폴딩 프로세스는 다양한 명령줄 옵션으로 제어할 수 있습니다.

테이블 및 뷰 지정

기본적으로 데이터베이스 스키마의 모든 테이블과 뷰는 엔터티 형식으로 스캐폴드됩니다. 스키마와 테이블을 지정하여 스캐폴드되는 테이블과 뷰를 제한할 수 있습니다.

-Schemas(Visual Studio PMC) 또는 --schema(.NET CLI) 인수는 엔터티 형식이 생성될 테이블 및 뷰의 스키마를 지정합니다. 이 인수를 생략하면 모든 스키마가 포함됩니다. 이 옵션을 사용하면 -Tables 또는 --table을 사용하여 명시적으로 포함되지 않더라도 스키마의 모든 테이블과 뷰가 모델에 포함됩니다.

-Tables(Visual Studio PMC) 또는 --table(.NET CLI) 인수는 엔터티 형식이 생성될 테이블 및 뷰를 지정했습니다. 특정 스키마의 테이블 또는 뷰는 'schema.table' 또는 'schema.view' 형식을 사용하여 포함할 수 있습니다. 이 옵션을 생략하면 모든 테이블과 뷰가 포함됩니다. |

예를 들어 ArtistsAlbums 테이블만 스캐폴드합니다.

dotnet ef dbcontext scaffold ... --table Artist --table Album

CustomerContractor 스키마에서 모든 테이블과 뷰를 스캐폴드하려면 다음을 수행합니다.

dotnet ef dbcontext scaffold ... --schema Customer --schema Contractor

예를 들어 Customer 스키마에서 Purchases 테이블을 스캐폴드하고 Contractor 스키마에서 AccountsContracts 테이블을 스캐폴드합니다.

dotnet ef dbcontext scaffold ... --table Customer.Purchases --table Contractor.Accounts --table Contractor.Contracts

데이터베이스 이름 유지

테이블 및 열 이름은 기본적으로 형식 및 속성에 대한 .NET 명명 규칙과 더 잘 일치하도록 고정됩니다. -UseDatabaseNames(Visual Studio PMC) 또는 --use-database-names(.NET CLI)를 지정하면 이 동작이 사용하지 않도록 설정되어 원래 데이터베이스 이름을 최대한 유지합니다. 잘못된 .NET 식별자는 여전히 고정되며 탐색 속성 같은 합성된 이름은 여전히 .NET 명명 규칙을 준수합니다.

예를 들어 다음 테이블을 살펴봅니다.

CREATE TABLE [BLOGS] (
    [ID] int NOT NULL IDENTITY,
    [Blog_Name] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Blogs] PRIMARY KEY ([ID]));

CREATE TABLE [posts] (
    [id] int NOT NULL IDENTITY,
    [postTitle] nvarchar(max) NOT NULL,
    [post content] nvarchar(max) NOT NULL,
    [1 PublishedON] datetime2 NOT NULL,
    [2 DeletedON] datetime2 NULL,
    [BlogID] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogID]) REFERENCES [Blogs] ([ID]) ON DELETE CASCADE);

기본적으로 다음 엔터티 형식은 다음 테이블에서 스캐폴드됩니다.

public partial class Blog
{
    public int Id { get; set; }
    public string BlogName { get; set; } = null!;
    public virtual ICollection<Post> Posts { get; set; } = new List<Post>();
}

public partial class Post
{
    public int Id { get; set; }
    public string PostTitle { get; set; } = null!;
    public string PostContent { get; set; } = null!;
    public DateTime _1PublishedOn { get; set; }
    public DateTime? _2DeletedOn { get; set; }
    public int BlogId { get; set; }
    public virtual Blog Blog { get; set; } = null!;
    public virtual ICollection<Tag> Tags { get; set; } = new List<Tag>();
}

그러나 -UseDatabaseNames 또는 --use-database-names 사용하면 다음과 같은 엔터티 형식이 발생합니다.

public partial class BLOG
{
    public int ID { get; set; }
    public string Blog_Name { get; set; } = null!;
    public virtual ICollection<post> posts { get; set; } = new List<post>();
}

public partial class post
{
    public int id { get; set; }
    public string postTitle { get; set; } = null!;
    public string post_content { get; set; } = null!;
    public DateTime _1_PublishedON { get; set; }
    public DateTime? _2_DeletedON { get; set; }
    public int BlogID { get; set; }
    public virtual BLOG Blog { get; set; } = null!;
}

매핑 특성 사용(데이터 주석이라고도 함)

엔터티 형식은 기본적으로 ModelBuilderOnModelCreating API를 사용하여 구성됩니다. 가능한 경우 -DataAnnotations(PMC) 또는 --data-annotations(.NET Core CLI)가 매핑 특성을 대신 사용하도록 지정합니다.

예를 들어 Fluent API를 사용하면 다음을 스캐폴드합니다.

entity.Property(e => e.Title)
    .IsRequired()
    .HasMaxLength(160);

데이터 주석을 사용하는 동안에는 다음을 스캐폴드합니다.

[Required]
[StringLength(160)]
public string Title { get; set; }

모델의 일부 측면은 매핑 특성을 사용하여 구성할 수 없습니다. 그래도 스캐폴더는 모델 빌드 API를 사용하여 이러한 사례를 처리합니다.

DbContext 이름

스캐폴드된 DbContext 클래스 이름은 기본적으로 Context 접미사가 붙는 데이터베이스의 이름이 됩니다. 다른 값을 지정하려면 PMC에서는 -Context를 사용하고 .NET Core CLI에서는 --context를 사용합니다.

대상 디렉터리 및 네임스페이스

엔터티 클래스 및 DbContext 클래스는 프로젝트의 루트 디렉터리로 스캐폴드되고 프로젝트의 기본 네임스페이스를 사용합니다.

--output-dir을 이용해 클래스가 스캐폴드되는 디렉터리를 지정할 수 있으며, --context-dir을 이용해 DbContext 클래스를 엔터티 형식 클래스와 별개의 디렉터리로 스캐폴드할 수 있습니다.

dotnet ef dbcontext scaffold ... --context-dir Data --output-dir Models

기본적으로 네임스페이스는 루트 네임스페이스에 프로젝트의 루트 디렉터리 아래에 있는 모든 하위 디렉터리의 이름을 더한 형태가 됩니다. 그러나 --namespace.를 사용하여 모든 출력 클래스의 네임스페이스를 재정의할 수 있습니다. --context-namespace를 사용하여 DbContext 클래스의 네임스페이스만 재정의할 수도 있습니다.

dotnet ef dbcontext scaffold ... --namespace Your.Namespace --context-namespace Your.DbContext.Namespace

스캐폴드된 코드

기존 데이터베이스에서 스캐폴딩한 결과는 다음과 같습니다.

  • DbContext에서 상속된 클래스가 포함된 파일
  • 각 엔터티 형식에 대한 파일

EF7부터는 생성된 코드를 T4 텍스트 템플릿을 사용하여 사용자 지정할 수도 있습니다. 자세한 내용은 사용자 지정 리버스 엔지니어링 템플릿을 참조하세요.

C# nullable 참조 형식

이제 스캐폴더는 C# NRT(Nullable 참조 형식)를 사용하는 EF 모델 및 엔터티 형식을 만들 수 있습니다. NRT 사용은 코드가 스캐폴드되는 C# 프로젝트에서 NRT 지원이 사용하도록 설정된 경우 자동으로 스캐폴드됩니다.

예를 들어 다음 Tags 테이블에는 null 허용과 null을 허용하지 않는 문자열 열이 모두 포함되어 있습니다.

CREATE TABLE [Tags] (
  [Id] int NOT NULL IDENTITY,
  [Name] nvarchar(max) NOT NULL,
  [Description] nvarchar(max) NULL,
  CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));

따라서 생성된 클래스에는 null 허용과 null을 허용하지 않는 문자열 속성이 포함됩니다.

public partial class Tag
{
    public Tag()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public string? Description { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

마찬가지로 다음 Posts 테이블에는 Blogs 테이블에 대한 필수 관계가 포함되어 있습니다.

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NOT NULL,
    [Contents] nvarchar(max) NOT NULL,
    [PostedOn] datetime2 NOT NULL,
    [UpdatedOn] datetime2 NULL,
    [BlogId] int NOT NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]));

따라서 Blogs 간에 null을 허용하지 않는(필수) 관계가 스캐폴딩됩니다.

public partial class Blog
{
    public Blog()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;

    public virtual ICollection<Post> Posts { get; set; }
}

Posts의 경우는 다음과 같습니다.

public partial class Post
{
    public Post()
    {
        Tags = new HashSet<Tag>();
    }

    public int Id { get; set; }
    public string Title { get; set; } = null!;
    public string Contents { get; set; } = null!;
    public DateTime PostedOn { get; set; }
    public DateTime? UpdatedOn { get; set; }
    public int BlogId { get; set; }

    public virtual Blog Blog { get; set; } = null!;

    public virtual ICollection<Tag> Tags { get; set; }
}

다대다 관계

스캐폴딩 프로세스는 단순 조인 테이블을 검색하고 자동으로 다 대 다 매핑을 생성합니다. 예를 들어 PostsTags 테이블과 이러한 테이블을 연결하는 조인 테이블 PostTag를 살펴보겠습니다.

CREATE TABLE [Tags] (
  [Id] int NOT NULL IDENTITY,
  [Name] nvarchar(max) NOT NULL,
  [Description] nvarchar(max) NULL,
  CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));

CREATE TABLE [Posts] (
    [Id] int NOT NULL IDENTITY,
    [Title] nvarchar(max) NOT NULL,
    [Contents] nvarchar(max) NOT NULL,
    [PostedOn] datetime2 NOT NULL,
    [UpdatedOn] datetime2 NULL,
    CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]));

CREATE TABLE [PostTag] (
    [PostsId] int NOT NULL,
    [TagsId] int NOT NULL,
    CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
    CONSTRAINT [FK_PostTag_Posts_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tags] ([Id]) ON DELETE CASCADE,
    CONSTRAINT [FK_PostTag_Tags_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([Id]) ON DELETE CASCADE);

스캐폴드하면 Post에 대한 클래스가 생성됩니다.

public partial class Post
{
    public Post()
    {
        Tags = new HashSet<Tag>();
    }

    public int Id { get; set; }
    public string Title { get; set; } = null!;
    public string Contents { get; set; } = null!;
    public DateTime PostedOn { get; set; }
    public DateTime? UpdatedOn { get; set; }
    public int BlogId { get; set; }

    public virtual Blog Blog { get; set; } = null!;

    public virtual ICollection<Tag> Tags { get; set; }
}

Tag에 대한 클래스도 생성됩니다.

public partial class Tag
{
    public Tag()
    {
        Posts = new HashSet<Post>();
    }

    public int Id { get; set; }
    public string Name { get; set; } = null!;
    public string? Description { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

그러나 PostTag 테이블에 대한 클래스는 생성되지 않습니다. 대신 다 대 다 관계에 대한 구성이 스캐폴드됩니다.

entity.HasMany(d => d.Tags)
    .WithMany(p => p.Posts)
    .UsingEntity<Dictionary<string, object>>(
        "PostTag",
        l => l.HasOne<Tag>().WithMany().HasForeignKey("PostsId"),
        r => r.HasOne<Post>().WithMany().HasForeignKey("TagsId"),
        j =>
            {
                j.HasKey("PostsId", "TagsId");
                j.ToTable("PostTag");
                j.HasIndex(new[] { "TagsId" }, "IX_PostTag_TagsId");
            });

기타 프로그래밍 언어

Microsoft 스캐폴드 C# 코드에서 게시한 EF Core 패키지입니다. 그러나 기본 스캐폴딩 시스템은 다른 언어로 스캐폴딩하기 위한 플러그 인 모델을 지원합니다. 이 플러그 인 모델은 다양한 커뮤니티 실행 프로젝트에서 사용됩니다. 예를 들면 다음과 같습니다.

코드 사용자 지정

EF7부터 생성된 코드를 사용자 지정하는 가장 좋은 방법 중 하나는 코드를 생성하는 데 사용되는 T4 템플릿을 사용자 지정하는 것입니다.

코드를 생성한 후에 변경할 수도 있지만 이 작업을 수행하는 가장 좋은 방법은 데이터베이스 모델이 변경될 때 스캐폴딩 프로세스를 다시 실행할지 여부에 따라 달라집니다.

한 번만 스캐폴드

이 방법을 사용하면 스캐폴드된 코드는 앞으로 코드 기반 매핑을 위한 시작점을 제공합니다. 생성된 코드를 원하는 대로 변경할 수 있으며 프로젝트의 다른 코드와 마찬가지로 일반 코드가 됩니다.

데이터베이스와 EF 모델을 동기화 상태로 유지하는 작업은 다음 두 가지 방법 중 하나를 통해 할 수 있습니다.

  • EF Core 데이터베이스 마이그레이션을 사용하도록 전환하고, 마이그레이션을 사용하여 스키마를 구동하여 엔터티 형식 및 EF 모델 구성을 데이터 소스로 사용합니다.
  • 데이터베이스가 변경될 때 엔터티 형식 및 EF 구성을 수동으로 업데이트합니다. 예를 들어 테이블에 새 열을 추가한 경우 매핑된 엔터티 형식에 열의 속성을 추가하고 OnModelCreating의 매핑 특성 및/또는 코드를 사용하여 필요한 구성을 추가합니다. 비교적 쉬운 작업이며, 유일하게 어려운 부분은 코드를 담당하는 개발자가 대응할 수 있도록 데이터베이스 변경 내용이 어떤 식으로든 기록되거나 검색되도록 하는 프로세스입니다.

반복되는 스캐폴딩

한 번 스캐폴딩하는 것의 다른 방법은 데이터베이스가 변경될 때마다 다시 스캐폴딩하는 것입니다. 이렇게 하면 이전에 스캐폴드된 코드에 덮어씁니다. 즉, 해당 코드의 엔터티 형식 또는 EF 구성에 대한 변경 내용이 손실됩니다.

[팁] 기본적으로 EF 명령은 실수로 인한 코드 손실로부터 보호하기 위해 기존 코드를 덮어쓰지 않습니다. -Force(Visual Studio PMC) 또는 --force(.NET CLI) 인수를 사용하여 기존 파일을 강제로 덮어쓸 수 있습니다.

스캐폴드된 코드는 덮어쓰여지므로 직접 수정하는 것이 아니라 부분 클래스 및 메서드와 구성을 재정의할 수 있는 EF Core의 메커니즘을 사용하는 것이 가장 좋습니다. 특별한 사항

  • DbContext 클래스와 엔터티 클래스는 모두 부분 클래스로 생성됩니다. 이렇게 하면 스캐폴딩을 실행할 때 재정의되지 않는 별도의 파일에 추가 멤버 및 코드를 도입할 수 있습니다.
  • DbContext 클래스에는 OnModelCreatingPartial이라는 부분 메서드가 포함되어 있습니다. 이 메서드의 구현을 DbContext의 부분 클래스에 추가할 수 있습니다. 그러면 OnModelCreating이 호출된 후에 호출됩니다.
  • ModelBuilder API를 사용하여 만든 모델 구성은 규칙 또는 매핑 특성에 의해 수행된 모든 구성과 모델 작성기에서 수행된 이전 구성을 재정의합니다. 즉, OnModelCreatingPartial 코드를 사용하여 해당 구성을 제거할 필요 없이 스캐폴딩 프로세스에서 생성된 구성을 재정의할 수 있습니다.

마지막으로, EF7부터 코드를 생성하는 데 사용되는 T4 템플릿을 사용자 지정할 수 있습니다. 이는 기본값으로 스캐폴딩한 다음 부분 클래스 및/또는 메서드를 사용하여 수정하는 것보다 더 효과적인 방법입니다.

작동 방법

리버스 엔지니어링은 데이터베이스 스키마를 읽는 것으로 시작합니다. 테이블, 열, 제약 조건 및 인덱스 관련 정보를 읽습니다.

그런 다음 스키마 정보를 사용하여 EF Core 모델을 만듭니다. 테이블은 엔터티 형식을 만드는 데 사용하고, 열은 속성을 만드는 데 사용하며, 외래 키는 관계를 만드는 데 사용합니다.

마지막으로 모델은 코드를 생성하는 데 사용합니다. 해당하는 엔터티 형식 클래스, Fluent API 및 데이터 주석은 스캐폴드되어 앱에서 동일한 모델을 다시 만듭니다.

제한 사항

  • 데이터베이스 스키마를 사용하여 나타낼 수 없는 모델 요소도 있습니다. 예를 들어 상속 계층 구조, 소유 엔터티 형식테이블 분할은 데이터베이스 스키마에 존재하지 않습니다. 바로 이점 때문에 이러한 구문은 절대로 스캐폴드되지 않습니다.
  • 또한 일부 열 형식은 EF Core 공급자에서 지원되지 않습니다. 이러한 열은 모델에 포함되지 않습니다.
  • EF Core 모델에서 동시성 토큰을 정의하여 두 사용자가 동일한 엔터티를 동시에 업데이트하지 못하게 할 수 있습니다. 일부 데이터베이스에는 이 유형의 열을 나타내는 특수 형식이 있으며(예: SQL Server의 rowversion), 이 경우 이 정보를 리버스 엔지니어링할 수 있습니다. 하지만 다른 동시성 토큰은 스캐폴드되지 않습니다.