데이터 시딩은 초기 데이터 세트로 데이터베이스를 채우는 프로세스입니다.
EF Core에서 이 작업을 수행할 수 있는 방법에는 여러 가지가 있습니다.
구성 옵션 UseSeeding 및 UseAsyncSeeding 메서드
EF 9는 UseSeeding 및 UseAsyncSeeding 메서드를 도입하여 초기 데이터로 데이터베이스를 시드하는 편리한 방법을 제공합니다. 이러한 메서드는 사용자 지정 초기화 논리를 사용하는 환경을 개선하는 것을 목표로 합니다(아래 설명). 모든 데이터 시드 코드를 배치할 수 있는 하나의 명확한 위치를 제공합니다. 또한 내부 코드 UseSeeding 와 UseAsyncSeeding 메서드는 동시성 문제를 방지하기 위해 마이그레이션 잠금 메커니즘에 의해 보호됩니다.
새 시드 메서드는 모델 변경 내용이 없고 마이그레이션이 적용되지 않은 경우에도 작업 EnsureCreated 및 Migrate 명령의 dotnet ef database update 일부로 호출됩니다.
팁
EF Core로 작업할 때 초기 데이터를 데이터베이스에 시드하는 권장 방법은 UseSeeding 및 UseAsyncSeeding을 사용하는 것입니다.
이러한 메서드는 옵션 구성 단계에서 설정할 수 있습니다. 예를 들어 다음과 같습니다.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFDataSeeding;Trusted_Connection=True;ConnectRetryCount=0")
.UseSeeding((context, _) =>
{
var testBlog = context.Set<Blog>().FirstOrDefault(b => b.Url == "http://test.com");
if (testBlog == null)
{
context.Set<Blog>().Add(new Blog { Url = "http://test.com" });
context.SaveChanges();
}
})
.UseAsyncSeeding(async (context, _, cancellationToken) =>
{
var testBlog = await context.Set<Blog>().FirstOrDefaultAsync(b => b.Url == "http://test.com", cancellationToken);
if (testBlog == null)
{
context.Set<Blog>().Add(new Blog { Url = "http://test.com" });
await context.SaveChangesAsync(cancellationToken);
}
});
참고
UseSeeding
/
UseAsyncSeeding 은 마이그레이션이 적용되는 동안 EnsureCreated/EnsureCreatedAsync 및 적용된 후 호출됩니다(예: Migrate/MigrateAsync마이그레이션 dotnet ef database update번들).
EF Core 도구 및 번들은 현재 동기 대리자를 사용하므로 애플리케이션에서 일반적으로 비동기 API를 사용하는 경우에도 항상 구현 UseSeeding 합니다.
배포 동작
UseSeeding EF UseAsyncSeeding Core가 데이터베이스 초기화 또는 마이그레이션 작업을 수행하는 경우에만 실행됩니다. 그에 따라 배포 메커니즘을 선택합니다.
| Operation | 호출된 시드 대리자 |
|---|---|
EnsureCreated 또는 Migrate |
UseSeeding |
EnsureCreatedAsync 또는 MigrateAsync |
UseAsyncSeeding |
dotnet ef database update 또는 Update-Database |
UseSeeding |
| 마이그레이션 번들 | UseSeeding |
| 외부 SQL 도구에서 실행되는 SQL 스크립트 | None |
실행 UseSeeding해야 하는 자동화된 배포의 경우 마이그레이션 번들 또는 전용 초기화 프로세스를 사용합니다. EF Core 도구 및 번들은 동기 대리자를 호출하므로 애플리케이션에서 일반적으로 비동기 API를 사용하는 경우에도 항상 구현 UseSeeding 합니다. 검토 또는 DBA 실행이 필요하고 시드 데이터가 마이그레이션 작업으로 표현되는 경우 SQL 스크립트를 사용합니다. 장단 분에 대한 마이그레이션 적용 을 참조하세요.
시드는 마이그레이션 다운그레이드 후에도 실행됩니다. 애플리케이션이 시드 코드에서 사용하는 모든 테이블을 포함하지 않는 마이그레이션으로 다운그레이드를 지원하는 경우 쿼리하기 전에 필요한 스키마가 있는지 확인합니다. 이는 대상을 지정하여 모든 마이그레이션을 되돌릴 때 특히 중요합니다 0.
Aspire 애플리케이션은 Aspire EF Core 마이그레이션 통합을 사용하여 로컬 마이그레이션 실행을 조정하고 마이그레이션 번들 또는 스크립트를 게시할 수 있습니다.
사용자 지정 초기화 논리
데이터 시드를 수행하는 간단하고 강력한 방법은 기본 애플리케이션 논리가 실행을 시작하기 전에 SaveChangesAsync를 사용하는 것입니다. UseSeeding 및 UseAsyncSeeding를 그 목적을 위해 사용하는 것이 좋지만, 경우에 따라 이러한 방법이 좋은 해결책이 아닐 수 있습니다. 예제 시나리오는 시드 시 한 트랜잭션에서 서로 다른 두 컨텍스트를 사용해야 하는 경우입니다. 다음은 애플리케이션에서 직접 사용자 지정 초기화를 수행하는 코드 샘플입니다.
using (var context = new DataSeedingContext())
{
await context.Database.EnsureCreatedAsync();
var testBlog = await context.Blogs.FirstOrDefaultAsync(b => b.Url == "http://test.com");
if (testBlog == null)
{
context.Blogs.Add(new Blog { Url = "http://test.com" });
await context.SaveChangesAsync();
}
}
경고
여러 인스턴스가 실행 중일 때 동시성 문제가 발생할 수 있으며 앱에 데이터베이스 스키마를 수정할 수 있는 권한이 있어야 하므로 시드 코드는 정상적인 앱 실행의 일부가 되어서는 안 됩니다.
배포의 제약 조건에 따라 초기화 코드를 다음과 같은 다양한 방법으로 실행할 수 있습니다.
- 로컬로 초기화 앱 실행
- 기본 앱을 사용하여 초기화 앱을 배포하고, 초기화 루틴을 호출하며, 초기화 앱을 사용하지 않도록 설정하거나 제거합니다.
일반적으로 게시 프로필을 사용하여 자동화할 수 있습니다.
데이터 관리 모델
데이터도 모델 구성의 일부로 엔터티 형식과 연결할 수 있습니다. 그런 다음 EF Core 마이그레이션은 데이터베이스를 새 버전의 모델로 업그레이드할 때 적용해야 하는 삽입, 업데이트 또는 삭제 작업을 자동으로 계산할 수 있습니다.
경고
마이그레이션은 관리되는 데이터를 원하는 상태로 가져오기 위해 수행해야 하는 작업을 결정할 때만 모델 변경을 고려합니다. 따라서 마이그레이션 외부에서 수행되는 데이터를 변경하면 손실되거나 오류가 발생할 수 있습니다.
예를 들어, 이는 Country에서 OnModelCreating용으로 관리되는 데이터를 구성합니다.
modelBuilder.Entity<Country>(b =>
{
b.Property(x => x.Name).IsRequired();
b.HasData(
new Country { CountryId = 1, Name = "USA" },
new Country { CountryId = 2, Name = "Canada" },
new Country { CountryId = 3, Name = "Mexico" });
});
관계가 있는 엔터티를 추가하려면 외래 키 값을 지정해야 합니다.
modelBuilder.Entity<City>().HasData(
new City { Id = 1, Name = "Seattle", LocatedInId = 1 },
new City { Id = 2, Name = "Vancouver", LocatedInId = 2 },
new City { Id = 3, Name = "Mexico City", LocatedInId = 3 },
new City { Id = 4, Name = "Puebla", LocatedInId = 3 });
다대다 탐색에 대한 데이터를 관리하는 경우 조인 엔터티를 명시적으로 구성해야 합니다. 엔터티 형식에 섀도 상태의 속성(예: LanguageCountry 아래 조인 엔터티)이 있는 경우 익명 클래스를 사용하여 값을 제공할 수 있습니다.
modelBuilder.Entity<Language>(b =>
{
b.HasData(
new Language { Id = 1, Name = "English" },
new Language { Id = 2, Name = "French" },
new Language { Id = 3, Name = "Spanish" });
b.HasMany(x => x.UsedIn)
.WithMany(x => x.OfficialLanguages)
.UsingEntity(
"LanguageCountry",
r => r.HasOne(typeof(Country)).WithMany().HasForeignKey("CountryId").HasPrincipalKey(nameof(Country.CountryId)),
l => l.HasOne(typeof(Language)).WithMany().HasForeignKey("LanguageId").HasPrincipalKey(nameof(Language.Id)),
je =>
{
je.HasKey("LanguageId", "CountryId");
je.HasData(
new { LanguageId = 1, CountryId = 2 },
new { LanguageId = 2, CountryId = 2 },
new { LanguageId = 3, CountryId = 3 });
});
});
소유 엔터티 형식은 비슷한 방식으로 구성할 수 있습니다.
modelBuilder.Entity<Language>().OwnsOne(p => p.Details).HasData(
new { LanguageId = 1, Phonetic = false, Tonal = false, PhonemesCount = 44 },
new { LanguageId = 2, Phonetic = false, Tonal = false, PhonemesCount = 36 },
new { LanguageId = 3, Phonetic = true, Tonal = false, PhonemesCount = 24 });
자세한 컨텍스트는 전체 샘플 프로젝트를 참조하세요.
데이터가 모델에 추가되면 마이그레이션을 통해 변경 내용을 적용해야 합니다.
HasData마이그레이션이 InsertDataUpdateData스캐폴드될 때 변경 내용이 변환되고 DeleteData 작업이 수행됩니다. 호출 Migrate 은 현재 HasData 구성을 독립적으로 검사하지 않습니다. 모델 관리형 데이터를 변경한 후 새 마이그레이션을 추가하고 배포합니다.
팁
자동화된 배포의 경우 마이그레이션 번들을 사용합니다. 실행하기 전에 미리 보거나 변경해야 하는 경우 SQL 스크립트 를 사용합니다.
또는 테스트 데이터베이스 또는 메모리 내 공급자 또는 비관계형 데이터베이스를 사용하는 경우와 같이 관리되는 데이터가 포함된 새 데이터베이스를 만드는 데 사용할 EnsureCreatedAsync 수 있습니다. 데이터베이스가 이미 있는 EnsureCreatedAsync 경우 데이터베이스의 스키마나 관리되는 데이터를 업데이트하지 않습니다. 관계형 데이터베이스의 경우 마이그레이션을 사용하려면 EnsureCreatedAsync를 호출해서는 안 됩니다.
참고
"데이터 시드"라고 하는 데 사용되는 메서드를 사용하여 HasData 데이터베이스를 채웁니다. 이 명명은 기능이 여러 제한 사항을 가지며 특정 유형의 데이터에만 적합하기 때문에 잘못된 기대치를 설정합니다. 그래서 이름을 "모델 관리 데이터"로 바꾸기로 결정했습니다. UseSeeding 및 UseAsyncSeeding 메서드는 범용 데이터 시드에 사용해야 합니다.
모델 관리 데이터의 제한 사항
이 유형의 데이터는 마이그레이션을 통해 관리되며 데이터베이스에 이미 있는 데이터를 업데이트하는 스크립트는 데이터베이스에 연결하지 않고 생성해야 합니다. 그러면 다음과 같은 몇 가지 제한 사항이 적용됩니다.
- 기본 키 값은 일반적으로 데이터베이스에서 생성되더라도 지정해야 합니다. 이는 마이그레이션 간의 데이터 변경 내용을 검색하는 데 사용됩니다.
- 기본 키가 어떤 방식으로든 변경되면 이전에 삽입된 데이터가 제거됩니다.
따라서 이 기능은 마이그레이션 외부에서 변경될 것으로 예상되지 않고 데이터베이스의 다른 항목(예: 우편 번호)에 의존하지 않는 정적 데이터에 가장 유용합니다.
시나리오에 다음이 포함된 경우 첫 번째 섹션에 설명된 UseSeeding 및 UseAsyncSeeding 방법을 사용하는 것이 좋습니다.
- 테스트를 위한 임시 데이터
- 데이터베이스 상태에 따라 달라지는 데이터
- 대용량 데이터(시드 데이터는 마이그레이션 스냅샷에서 캡처되고 대용량 데이터는 빠르게 거대한 파일 및 성능 저하로 이어질 수 있음)
- 대체 키를 ID로 사용하는 엔터티를 포함하여 데이터베이스에서 생성해야 하는 키 값이 필요한 데이터
- 일부 암호 해시와 같이 사용자 지정 변환이 필요한 데이터(값 변환에 의해 처리되지 않음)
- ASP.NET Core ID 역할 및 사용자 만들기와 같은 외부 API에 대한 호출이 필요한 데이터
- 고정적이거나 결정적이지 않은 데이터(예:
DateTime.Now에 시드).
수동 마이그레이션 사용자 지정
마이그레이션이 추가되면 HasData에 지정된 데이터에 대한 변경 내용이 InsertData(), UpdateData(), DeleteData()에 대한 호출로 변환됩니다.
HasData의 몇 가지 제한 사항을 해결하는 한 가지 방법은 이러한 호출 또는 사용자 지정 작업을 마이그레이션에 수동으로 추가하는 것입니다.
migrationBuilder.InsertData(
table: "Countries",
columns: new[] { "CountryId", "Name" },
values: new object[,]
{
{ 1, "USA" },
{ 2, "Canada" },
{ 3, "Mexico" }
});
migrationBuilder.InsertData(
table: "Languages",
columns: new[] { "Id", "Name", "Details_PhonemesCount", "Details_Phonetic", "Details_Tonal" },
values: new object[,]
{
{ 1, "English", 44, false, false },
{ 2, "French", 36, false, false },
{ 3, "Spanish", 24, true, false }
});
migrationBuilder.InsertData(
table: "Cites",
columns: new[] { "Id", "LocatedInId", "Name" },
values: new object[,]
{
{ 1, 1, "Seattle" },
{ 2, 2, "Vancouver" },
{ 3, 3, "Mexico City" },
{ 4, 3, "Puebla" }
});
migrationBuilder.InsertData(
table: "LanguageCountry",
columns: new[] { "CountryId", "LanguageId" },
values: new object[,]
{
{ 2, 1 },
{ 2, 2 },
{ 3, 3 }
});
이러한 작업은 마이그레이션을 작성할 때 값과 키가 고정되는 경우에 적합합니다. 현재 데이터베이스 상태를 쿼리하지 않습니다.
마이그레이션의 데이터 작업에서 , UpdateData및 DeleteData공급자별 SQL 변환의 InsertData예를 참조하세요.
.NET