EF Core 5.0의 호환성이 손상되는 변경

EF Core 5.0.0으로 업데이트하면 기존 애플리케이션의 호환성이 손상될 수 있는 API 및 동작 변경 내용은 다음과 같습니다.

요약

주요 변경 내용 영향
EF Core 5.0은 .NET Framework를 지원하지 않음 중간
IProperty.GetColumnName()은 이제 사용되지 않음 중간
10진수에 전체 자릿수 및 소수 자릿수가 필요함 중간
주 엔터티에서 종속 엔터티로 필수 또는 null을 허용하지 않는 탐색은 의미 체계가 서로 다름 중간
정의 쿼리가 공급자별 메서드로 대체됨 중간
null이 아닌 참조 탐색을 쿼리가 덮어쓰지 않음 중간
ToView()가 마이그레이션에서 다르게 처리됨 중간
ToTable(null)이 엔터티 형식을 테이블에 매핑되지 않은 것으로 표시 중간
SQLite NTS 확장에서 HasGeometricDimension 메서드를 제거함 낮음
Azure Cosmos DB: 파티션 키가 이제 기본 키에 추가되었습니다. 낮음
Azure Cosmos DB: id 속성 이름이 __id(으)로 변경됩니다. 낮음
Azure Cosmos DB: byte[]가 이제 숫자 배열이 아닌 base64 문자열로 저장됩니다. 낮음
Azure Cosmos DB: GetPropertyName 및 SetPropertyName의 이름이 바뀌었습니다. 낮음
엔터티 상태가 분리됨에서 변경되지 않음, 업데이트됨 또는 삭제됨으로 변경될 때 값 생성기가 호출됨 낮음
IMigrationsModelDiffer는 이제 IRelationalModel을 사용 낮음
판별자는 읽기 전용 낮음
InMemory 공급자에 대해 공급자별 EF.Functions 메서드 throw 낮음
IndexBuilder.HasName은 이제 사용되지 않음 낮음
이제 리버스 엔지니어링된 모델을 스캐폴딩하기 위해 Pluralizer가 포함됩니다. 낮음
일부 API에서 INavigationBase가 INavigation을 대체하여 건너뛰기 탐색을 지원함 낮음
상관 관계가 지정된 컬렉션이 포함되고 Distinct 또는 GroupBy도 사용하는 일부 쿼리는 더 이상 지원되지 않음 낮음
프로젝션에서 쿼리할 수 있는 형식의 컬렉션을 사용할 수 없음 낮음

중간 영향을 주는 변경 내용

EF Core 5.0은 .NET Framework를 지원하지 않음

추적 문제 #15498

이전 동작

EF Core 3.1은 .NET Framework에서 지원되는 .NET Standard 2.0을 대상으로 합니다.

새 동작

EF Core 5.0은 .NET Framework에서 지원되지 않는 .NET Standard 2.1을 대상으로 합니다. 즉, EF Core 5.0은 .NET Framework 애플리케이션에서 사용할 수 없습니다.

이유

단일 .NET 대상 프레임워크로 통합하기 위한 움직임이 .NET 팀 간에 확산되고 있습니다. 자세한 내용은 .NET Standard의 미래를 참조하세요.

해결 방법

.NET Framework 애플리케이션은 LTS(장기 지원) 릴리스인 EF Core 3.1을 계속 사용할 수 있습니다. 또는 .NET Core 3.1 또는 .NET 5(모두 .NET Standard 2.1을 지원함)를 사용하도록 애플리케이션을 업데이트할 수 있습니다.

IProperty.GetColumnName()은 이제 사용되지 않음

추적 이슈 #2266

이전 동작

GetColumnName()은 속성이 매핑되는 열의 이름을 반환했습니다.

새 동작

GetColumnName()은 속성이 매핑되는 열의 이름을 계속 반환하지만 이제 이러한 동작이 명확하지는 않습니다. EF Core 5에서 TPT와 뷰 또는 함수에 대한 동시 매핑을 지원하여 해당 매핑에서 동일한 속성에 다른 열 이름을 사용할 수 있기 때문입니다.

이유

더욱 정확한 오버로드인 GetColumnName(IProperty, StoreObjectIdentifier)을 사용자에게 안내하기 위해 이 메서드를 사용되지 않는 것으로 표시했습니다.

해결 방법

엔터티 형식이 단일 테이블에만 매핑되고 뷰, 함수 또는 여러 테이블에 매핑되지 않는 경우 GetColumnBaseName(IReadOnlyProperty)을(를) EF Core 5.0 및 6.0에서 사용하여 테이블 이름을 가져올 수 있습니다. 예시:

var columnName = property.GetColumnBaseName();

EF Core 7.0에서는 단순 단일 테이블만 매핑하기 위해 원래처럼 동작하는 새 GetColumnName(으)로 다시 바꿀 수 있습니다.

엔터티 형식을 뷰, 함수 또는 여러 테이블에 매핑할 수 있는 경우 테이블, 뷰 또는 함수를 ID로 지정하려면 StoreObjectIdentifier을(를) 가져와야 합니다. 그런 다음 해당 저장소 개체의 열 이름을 가져오는 데 사용할 수 있습니다. 예시:

var columnName = property.GetColumnName(StoreObjectIdentifier.Table("Users", null)));

10진수에 전체 자릿수 및 소수 자릿수가 필요함

추적 이슈 #19293

이전 동작

EF Core는 일반적으로 SqlParameter 개체에서 전체 자릿수 및 소수 자릿수를 설정하지 않았습니다. 즉, SQL Server가 데이터베이스 열의 전체 자릿수 및 소수 자릿수를 기준으로 반올림될 때 전체 자릿수 및 소수 자릿수가 SQL Server에 전송되었습니다.

새 동작

이제 EF Core는 EF Core 모델의 속성에 대해 구성된 값을 사용하여 매개 변수의 전체 자릿수 및 소수 자릿수를 설정합니다. 즉, 이제 SqlClient에서 반올림됩니다. 결국, 구성된 전체 자릿수 및 소수 자릿수가 데이터베이스 전체 자릿수 및 소수 자릿수와 일치하지 않으면 표시된 반올림이 변경될 수 있습니다.

이유

Always Encrypted를 포함한 최신 SQL Server 기능을 사용하려면 매개 변수 패싯을 완전히 지정해야 합니다. 또한 SqlClient에서는 10진수 값을 자르는 대신 반올림하여 SQL Server 동작과 일치하도록 변경했습니다. 이를 통해 EF Core는 올바르게 구성된 10진수의 동작을 변경하지 않고 해당 패싯을 설정할 수 있었습니다.

해결 방법

전체 자릿수 및 소수 자릿수를 포함하는 형식 이름을 사용하여 10진수 속성을 매핑합니다. 예시:

public class Blog
{
    public int Id { get; set; }

    [Column(TypeName = "decimal(16, 5)")]
    public decimal Score { get; set; }
}

또는 모델 빌드 API에서 HasPrecision을 사용합니다. 예시:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>().Property(e => e.Score).HasPrecision(16, 5);
    }

주 엔터티에서 종속 엔터티로 필수 또는 null을 허용하지 않는 탐색은 의미 체계가 서로 다름

추적 이슈 #17286

이전 동작

주 엔터티에 대한 탐색만 필수로 구성할 수 있습니다. 따라서 종속 엔터티(외래 키를 포함하는 엔터티)에 대한 탐색에 RequiredAttribute를 사용하거나 null을 허용하지 않음으로 표시하면 정의하는 엔터티 형식에 대한 외래 키가 대신 만들어집니다.

새 동작

필수 종속 엔터티가 추가로 지원되어 이제 모든 참조 탐색을 필수로 표시할 수 있습니다. 즉, 위에 나온 사례에서 외래 키는 관계의 다른 쪽에 정의되며 속성은 필수로 표시되지 않습니다.

종속 끝을 지정하기 전에 IsRequired를 호출하는 것은 이제 모호합니다.

modelBuilder.Entity<Blog>()
    .HasOne(b => b.BlogImage)
    .WithOne(i => i.Blog)
    .IsRequired()
    .HasForeignKey<BlogImage>(b => b.BlogForeignKey);

이유

필수 종속 엔터티에 대한 지원을 사용하도록 설정하는 데 새 동작이 필요합니다(#12100 참조).

해결 방법

종속 엔터티에 대한 탐색에서 RequiredAttribute를 제거하고 대신 이를 주 엔터티에 대한 탐색에 배치하거나 OnModelCreating에서 관계를 구성합니다.

modelBuilder.Entity<Blog>()
    .HasOne(b => b.BlogImage)
    .WithOne(i => i.Blog)
    .HasForeignKey<BlogImage>(b => b.BlogForeignKey)
    .IsRequired();

정의 쿼리가 공급자별 메서드로 대체됨

추적 문제 #18903

이전 동작

엔터티 형식이 코어 수준에서 정의 쿼리에 매핑되었습니다. 엔터티 형식이 엔터티 형식의 쿼리 루트에서 사용되면 언제든지 모든 공급자에 대한 정의 쿼리로 대체되었습니다.

새 동작

정의 쿼리에 대한 API가 더 이상 사용되지 않습니다. 새 공급자별 API가 도입되었습니다.

이유

쿼리 루트가 쿼리에서 사용될 때마다 정의 쿼리가 대체 쿼리로 구현되었지만 다음과 같은 몇 가지 문제가 발생했습니다.

  • 정의 쿼리가 Select 메서드에서 new { ... }를 사용하여 엔터티 형식을 프로젝션하는 경우 추가 작업이 필요한 엔터티로 식별하고 EF Core가 쿼리에서 명목 형식을 처리하는 방식과 일치하지 않도록 했습니다.
  • 관계형 공급자의 경우 LINQ 식 형식으로 SQL 문자열을 전달하는 데 FromSql이 여전히 필요합니다.

처음에는 정의 쿼리가 클라이언트 쪽 보기로 도입되어 키 없는 엔터티의 메모리 내 공급자와 함께 사용되었습니다(관계형 데이터베이스의 데이터베이스 뷰와 유사함). 이러한 정의를 통해 메모리 내 데이터베이스에 대해 애플리케이션을 쉽게 테스트할 수 있습니다. 이후로 이 방식이 광범위하게 적용되어 유용했지만 일관되지 않아 동작을 이해하기 어려웠습니다. 따라서 개념을 단순화하기로 했습니다. LINQ 기반 정의 쿼리를 메모리 내 공급자에만 적용하고 다르게 처리합니다. 자세한 내용은 이 문제를 참조하세요.

해결 방법

관계형 공급자의 경우 OnModelCreating에서 ToSqlQuery 메서드를 사용하고 엔터티 형식에 사용할 SQL 문자열을 전달합니다. 메모리 내 공급자의 경우 OnModelCreating에서 ToInMemoryQuery 메서드를 사용하고 엔터티 형식에 사용할 LINQ 쿼리를 전달합니다.

null이 아닌 참조 탐색을 쿼리가 덮어쓰지 않음

추적 문제 #2693

이전 동작

EF Core 3.1에서는 null이 아닌 값으로 즉시 초기화되는 참조 탐색을 경우에 따라 키 값이 일치하는지 여부에 관계없이 데이터베이스의 엔터티 인스턴스가 덮어쓸 수 있습니다. 하지만 EF Core 3.1이 정반대를 수행하고 기존 null이 아닌 값을 그대로 두는 경우도 있습니다.

새 동작

EF Core 5.0부터 null이 아닌 참조 탐색을 쿼리에서 반환된 인스턴스가 덮어쓰지 않습니다.

빈 컬렉션으로의 컬렉션 탐색 즉시 초기화는 계속 지원됩니다.

이유

참조 탐색 속성을 "빈" 엔터티 인스턴스로 초기화하면 모호한 상태가 됩니다. 예시:

public class Blog
{
     public int Id { get; set; }
     public Author Author { get; set; ) = new Author();
}

일반적으로 블로그 및 저자에 대한 쿼리는 먼저 Blog 인스턴스를 만든 다음 데이터베이스에서 반환된 데이터를 기반으로 적절한 Author 인스턴스를 설정합니다. 그러나 이 경우 모든 Blog.Author 속성은 이미 빈 Author로 초기화되었습니다. 단, EF Core는 이 인스턴스가 "비어 있음"을 알 방법이 없습니다. 따라서 이 인스턴스를 덮어쓰면 유효한 Author가 자동으로 삭제될 수 있습니다. 따라서 5.0 EF Core는 이미 초기화된 탐색을 일관되게 덮어쓰지 않습니다.

이 새로운 동작은 대부분의 경우 EF6의 동작과도 부합하지만 조사 결과, EF6에서 일부 불일치를 발견했습니다.

해결 방법

이 중단이 발생하는 경우 해결 방법은 참조 탐색 속성 즉시 초기화를 중지하는 것입니다.

ToView()가 마이그레이션에서 다르게 처리됨

추적 이슈 #2725

이전 동작

ToView(string)를 호출하면 마이그레이션에서 뷰에 매핑하는 것 외에도 엔터티 형식을 무시합니다.

새 동작

이제 ToView(string)는 뷰에 매핑하는 것 외에도 엔터티 형식을 테이블에 매핑되지 않은 것으로 표시합니다. 따라서 EF Core 5로 업그레이드한 후 첫 번째 마이그레이션에서 더 이상 무시되지 않는 이 엔터티 형식의 기본 테이블을 삭제하려고 합니다.

이유

이제 EF Core에서는 엔터티 형식을 테이블과 뷰에 모두 동시에 매핑할 수 있으므로 ToView는 더 이상 유효한 표시기가 아니며 마이그레이션에서 무시해야 합니다.

해결 방법

다음 코드를 사용하여 매핑된 테이블을 마이그레이션에서 제외된 것으로 표시합니다.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>().ToTable("UserView", t => t.ExcludeFromMigrations());
}

ToTable(null)이 엔터티 형식을 테이블에 매핑되지 않은 것으로 표시

추적 이슈 #21172

이전 동작

ToTable(null)은 테이블 이름을 기본값으로 다시 설정합니다.

새 동작

ToTable(null)은 이제 엔터티 형식을 테이블에 매핑되지 않은 것으로 표시합니다.

이유

이제 EF Core에서는 엔터티 형식을 테이블과 뷰에 모두 동시에 매핑할 수 있으므로 ToTable(null)을 사용하여 테이블에 매핑되지 않은 것으로 표시합니다.

해결 방법

뷰나 DbFunction에 매핑되지 않은 경우 다음 코드를 사용하여 테이블 이름을 기본값으로 다시 설정합니다.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>().Metadata.RemoveAnnotation(RelationalAnnotationNames.TableName);
}

낮은 영향을 주는 변경 내용

SQLite NTS 확장에서 HasGeometricDimension 메서드를 제거함

추적 이슈 #14257

이전 동작

HasGeometricDimension은 기하 도형 열에서 추가 차원(Z 및 M)을 사용하도록 설정하는 데 사용되었습니다. 그러나 이 동작은 데이터베이스 만들기에만 영향을 주었습니다. 추가 차원을 사용하여 값을 쿼리하도록 지정할 필요가 없었습니다. 추가 차원을 사용하여 값을 삽입하거나 업데이트할 경우에도 제대로 작동하지 않았습니다(#14257 참조).

새 동작

추가 차원(Z 및 M)을 사용하여 기하 도형 값을 삽입하고 업데이트할 수 있도록 하려면 차원을 열 형식 이름의 일부로 지정해야 합니다. 이 API는 SpatiaLite의 AddGeometryColumn 함수 기본 동작에 더 비슷하게 일치합니다.

이유

열 형식에 차원을 지정한 후에는 HasGeometricDimension을 사용하는 것은 불필요하며 중복되므로 HasGeometricDimension을 완전히 제거했습니다.

해결 방법

HasColumnType을 사용하여 차원을 지정합니다.

modelBuilder.Entity<GeoEntity>(
    x =>
    {
        // Allow any GEOMETRY value with optional Z and M values
        x.Property(e => e.Geometry).HasColumnType("GEOMETRYZM");

        // Allow only POINT values with an optional Z value
        x.Property(e => e.Point).HasColumnType("POINTZ");
    });

Azure Cosmos DB: 파티션 키가 이제 기본 키에 추가됩니다.

추적 이슈 #15289

이전 동작

파티션 키 속성이 id를 포함하는 대체 키에만 추가되었습니다.

새 동작

이제 파티션 키 속성이 규칙에 따라 기본 키에도 추가됩니다.

이유

이 변경으로 인해 모델이 Azure Cosmos DB 의미 체계에 더 잘 부합하고 Find 및 일부 쿼리의 성능이 향상됩니다.

해결 방법

파티션 키 속성이 기본 키에 추가되지 않도록 하려면 OnModelCreating에서 구성합니다.

modelBuilder.Entity<Blog>()
    .HasKey(b => b.Id);

Azure Cosmos DB: id 속성 이름이 __id(으)로 변경됩니다.

추적 문제 #17751

이전 동작

id JSON 속성에 매핑된 섀도 속성 이름도 id로 지정되었습니다.

새 동작

규칙에 따라 만들어진 섀도 속성 이름이 이제 __id로 지정됩니다.

이유

이 변경으로 인해 id 속성이 엔터티 형식의 기존 속성과 충돌할 가능성이 줄어듭니다.

해결 방법

3.x 동작으로 돌아가려면 OnModelCreating에서 id 속성을 구성합니다.

modelBuilder.Entity<Blog>()
    .Property<string>("id")
    .ToJsonProperty("id");

Azure Cosmos DB: byte[]가 이제 숫자 배열이 아닌 base64 문자열로 저장됩니다.

추적 문제 #17306

이전 동작

byte[] 형식의 속성이 숫자 배열로 저장되었습니다.

새 동작

byte[] 형식의 속성이 이제 base64 문자열로 저장됩니다.

이유

byte[]의 이 표현은 기대치에 더 잘 부합하며 주요 JSON serialization 라이브러리의 기본 동작입니다.

해결 방법

숫자 배열로 저장된 기존 데이터는 계속해서 올바르게 쿼리되지만 현재는 삽입 동작을 다시 변경하는 방법이 지원되지 않습니다. 이러한 제한으로 시나리오가 차단되는 경우 이 문제에 주석을 추가합니다.

Azure Cosmos DB: GetPropertyName 및 SetPropertyName의 이름이 바뀌었습니다.

추적 문제 #17874

이전 동작

이전에는 확장 메서드를 GetPropertyNameSetPropertyName이라고 했습니다.

새 동작

이전 API는 제거되었으며 새 메서드 GetJsonPropertyName, SetJsonPropertyName이 추가되었습니다.

이유

이 변경으로 인해 이러한 메서드를 구성하는 작업과 관련된 모호성이 제거됩니다.

해결 방법

새 API를 사용합니다.

엔터티 상태가 분리됨에서 변경되지 않음, 업데이트됨 또는 삭제됨으로 변경될 때 값 생성기가 호출됨

추적 이슈 #15289

이전 동작

엔터티 상태가 추가됨으로 변경될 때만 값 생성기가 호출되었습니다.

새 동작

이제 엔터티 상태가 분리됨에서 변경되지 않음, 업데이트됨 또는 삭제됨으로 변경되고 속성이 기본값을 포함할 때 값 생성기가 호출됩니다.

이유

데이터 저장소에 유지되지 않고 클라이언트에서 항상 값이 생성되는 속성이 사용되는 환경을 개선하기 위해 이 변경이 필요했습니다.

해결 방법

값 생성기가 호출되지 않도록 하려면 상태가 변경되기 전에 기본값이 아닌 값을 속성에 할당합니다.

IMigrationsModelDiffer는 이제 IRelationalModel을 사용

추적 이슈 #20305

이전 동작

IMigrationsModelDiffer API가 IModel을 사용하여 정의되었습니다.

새 동작

IMigrationsModelDiffer API는 이제 IRelationalModel을 사용합니다. 그러나 이 코드는 애플리케이션의 일부이기 때문에 모델 스냅샷에는 여전히 IModel만 포함되어 있으므로, 호환성이 손상되는 변경을 더 크게 수행하지 않으면 Entity Framework에서 이를 변경할 수 없습니다.

이유

IRelationalModel은 데이터베이스 스키마의 새로 추가된 표현입니다. 이를 사용하여 차이를 찾는 것이 더 빠르고 정확합니다.

해결 방법

다음 코드를 사용하여 snapshot의 모델을 context의 모델과 비교할 수 있습니다.

var dependencies = context.GetService<ProviderConventionSetBuilderDependencies>();
var relationalDependencies = context.GetService<RelationalConventionSetBuilderDependencies>();

var typeMappingConvention = new TypeMappingConvention(dependencies);
typeMappingConvention.ProcessModelFinalizing(((IConventionModel)modelSnapshot.Model).Builder, null);

var relationalModelConvention = new RelationalModelConvention(dependencies, relationalDependencies);
var sourceModel = relationalModelConvention.ProcessModelFinalized(snapshot.Model);

var modelDiffer = context.GetService<IMigrationsModelDiffer>();
var hasDifferences = modelDiffer.HasDifferences(
    ((IMutableModel)sourceModel).FinalizeModel().GetRelationalModel(),
    context.Model.GetRelationalModel());

6.0에서 이 환경을 개선할 계획입니다(#22031 참조).

판별자는 읽기 전용

추적 이슈 #21154

이전 동작

SaveChanges를 호출하기 전에 판별자 값을 변경할 수 있었습니다.

새 동작

위의 경우에는 예외가 throw됩니다.

이유

EF는 추적이 계속 진행되는 동안에는 엔터티 형식이 변경될 것으로 예상하지 않으므로, 판별자 값을 변경하면 컨텍스트가 일관되지 않은 상태로 유지되어 예기치 않은 동작이 발생할 수 있습니다.

해결 방법

판별자 값을 변경해야 하고 SaveChanges를 호출한 직후 컨텍스트가 삭제되는 경우 판별자를 변경할 수 있게 만들 수 있습니다.

modelBuilder.Entity<BaseEntity>()
    .Property<string>("Discriminator")
    .Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save);

InMemory 공급자에 대해 공급자별 EF.Functions 메서드 throw

추적 문제 #20294

이전 동작

공급자별 EF.Functions 메서드는 클라이언트 실행을 위한 구현을 포함하여, InMemory 공급자에서 실행될 수 있었습니다. 예를 들어 EF.Functions.DateDiffDay는 InMemory 공급자에서 작동하는 Sql Server 관련 메서드입니다.

새 동작

공급자별 메서드는 메서드 본문에서 예외를 throw하여 클라이언트 쪽에서 평가를 차단하도록 업데이트되었습니다.

이유

공급자별 메서드는 데이터베이스 함수에 매핑됩니다. 매핑된 데이터베이스 함수에서 수행하는 계산을 LINQ에서 항상 클라이언트 쪽에 복제할 수 있는 것은 아닙니다. 이로 인해 클라이언트에서 동일한 메서드를 실행하는 경우 서버 결과가 다를 수 있습니다. 이 메서드는 LINQ에서 특정 데이터베이스 함수를 변환하는 데 사용되므로 클라이언트 쪽에서 평가할 필요가 없습니다. InMemory 공급자는 다른 데이터베이스이므로 이 공급자에 대해 해당 메서드를 사용할 수 없습니다. InMemory 공급자 또는 이러한 메서드를 변환하지 않는 다른 공급자에 대해 이 메서드를 실행하려 하면 예외가 throw됩니다.

해결 방법

데이터베이스 함수의 동작을 정확하게 모방하는 방법은 없으므로 프로덕션에서와 동일한 종류의 데이터베이스에 대해 해당 동작을 포함하는 쿼리를 테스트해야 합니다.

IndexBuilder.HasName은 이제 사용되지 않음

추적 문제 #21089

이전 동작

이전에는 지정된 속성 세트에 대해 하나의 인덱스만 정의할 수 있었습니다. 인덱스의 데이터베이스 이름은 IndexBuilder.HasName을 사용하여 구성되었습니다.

새 동작

이제 동일한 세트 또는 속성에서 여러 인덱스가 허용됩니다. 관련 인덱스는 모델에서 이름으로 구분됩니다. 관례적으로 모델 이름이 데이터베이스 이름으로 사용되지만 HasDatabaseName을 사용하여 독립적으로 구성할 수도 있습니다.

이유

이후에는 오름차순 및 내림차순 인덱스나 동일한 속성 세트에 대한 여러 가지 데이터 정렬을 포함하는 인덱스를 둘 다 사용하려고 합니다. 이번 변경은 이러한 의도에 부합합니다.

해결 방법

이전에는 IndexBuilder.HasName을 호출하는 모든 코드는 HasDatabaseName을 대신 호출하도록 업데이트해야 했습니다.

EF Core 버전 2.0.0 이전에 생성된 마이그레이션이 프로젝트에 포함된 경우 관련 파일에서 경고를 안전하게 무시하고 #pragma warning disable 612, 618을 추가하여 경고를 표시하지 않을 수 있습니다.

이제 리버스 엔지니어링된 모델을 스캐폴딩하기 위해 pluralizer가 포함됨

추적 문제 #11160

이전 동작

이전에는 데이터베이스 스키마를 리버스 엔지니어링하여 DbContext 및 엔터티 형식을 스캐폴딩할 때 DbSet 및 컬렉션 탐색 이름을 복수화하고 테이블 이름을 단수화하기 위해 별도 pluralizer 패키지를 설치해야 했습니다.

새 동작

이제 EF Core에는 Humanizer 라이브러리를 사용하는 pluralizer가 포함됩니다. 이는 Visual Studio가 변수 이름을 추천할 때 사용하는 라이브러리와 동일합니다.

이유

.NET에서는 컬렉션 속성에 복수 형식 단어를 사용하고 형식 및 참조 속성에 단수 형식을 사용하는 것이 자연스럽습니다.

해결 방법

Pluralizer를 사용하지 않으려면 dotnet ef dbcontext scaffold에서 --no-pluralize 옵션을 사용하거나 Scaffold-DbContext에서 -NoPluralize 스위치를 사용합니다.

일부 API에서 INavigationBase가 INavigation을 대체하여 건너뛰기 탐색을 지원함

추적 문제 #2568

이전 동작

5.0 이전 EF Core는 INavigation 인터페이스로 표시되는 한 가지 탐색 속성 형식만 지원했습니다.

새 동작

EF Core 5.0에서는 "건너뛰기 탐색"을 사용하는 다대다 관계가 도입되었습니다. 이 관계는 ISkipNavigation 인터페이스로 표시되고, INavigation의 기능 대부분이 공통 기본 인터페이스인 INavigationBase에 적용되었습니다.

이유

일반 탐색과 건너뛰기 탐색은 기능 대부분이 동일합니다. 그러나 건너뛰기 탐색과 외래 키의 관계는 일반 탐색과 다릅니다. 관련된 FK가 관계의 양쪽 끝에 직접 있지 않고 조인 엔터티에 있기 때문입니다.

해결 방법

대부분의 경우 애플리케이션은 다른 변경 없이 새 기본 인터페이스를 사용하도록 전환할 수 있습니다. 하지만 외래 키에 액세스하는 데 탐색이 사용되는 경우에는 애플리케이션 코드를 일반 탐색으로만 제한하거나 일반 탐색 및 건너뛰기 탐색에 적절한 작업을 수행하도록 업데이트해야 합니다.

상관 관계가 지정된 컬렉션이 포함되고 Distinct 또는 GroupBy도 사용하는 일부 쿼리는 더 이상 지원되지 않음

추적 문제 #15873

이전 동작

이전에는 뒤에 GroupBy가 오는 상관 관계가 지정된 컬렉션이 포함된 쿼리와 Distinct를 사용하는 일부 쿼리를 실행할 수 있었습니다.

GroupBy 예제:

context.Parents
    .Select(p => p.Children
        .GroupBy(c => c.School)
        .Select(g => g.Key))

Distinct 예제 - 특히 내부 컬렉션 프로젝션에 기본 키가 포함되지 않은 Distinct 쿼리:

context.Parents
    .Select(p => p.Children
        .Select(c => c.School)
        .Distinct())

이러한 쿼리는 내부 컬렉션에 중복이 포함된 경우 잘못된 결과를 반환할 수 있지만 내부 컬렉션의 모든 요소가 고유한 경우 올바르게 작동했습니다.

새 동작

이러한 쿼리는 더 이상 지원되지 않습니다. 결과를 올바르게 작성하는 데 충분한 정보가 없다는 것을 나타내는 예외가 throw됩니다.

이유

상관 관계가 지정된 컬렉션 시나리오의 경우 컬렉션 엔터티를 올바른 부모에 할당하기 위해서는 엔터티의 기본 키를 알아야 합니다. 내부 컬렉션이 GroupBy 또는 Distinct를 사용하지 않는 경우 누락된 기본 키를 프로젝션에 간단히 추가할 수 있습니다. 그러나 GroupByDistinct 의 경우에는 GroupBy 또는 Distinct 작업의 결과가 변경되기 때문에 추가할 수 없습니다.

완화 방법

내부 컬렉션에서 GroupBy 또는 Distinct 작업을 사용하지 않도록 쿼리를 다시 작성하고 대신 클라이언트에서 이러한 작업을 수행합니다.

context.Parents
    .Select(p => p.Children.Select(c => c.School))
    .ToList()
    .Select(x => x.GroupBy(c => c).Select(g => g.Key))
context.Parents
    .Select(p => p.Children.Select(c => c.School))
    .ToList()
    .Select(x => x.Distinct())

프로젝션에서 쿼리할 수 있는 형식의 컬렉션을 사용할 수 없음

추적 문제 #16314

이전 동작

이전에는 경우에 따라 프로젝션 내에서 쿼리 가능 형식의 컬렉션을 List<T> 생성자의 인수 등으로 사용할 수 있었습니다.

context.Blogs
    .Select(b => new List<Post>(context.Posts.Where(p => p.BlogId == b.Id)))

새 동작

이러한 쿼리는 더 이상 지원되지 않습니다. 쿼리 가능 형식의 개체를 만들 수 없음을 나타내고 이를 수정하는 방법을 제안하는 예외가 throw됩니다.

이유

쿼리 가능 형식의 개체는 구체화할 수 없으므로 대신 List<T> 형식을 사용하여 자동으로 생성됩니다. 그러면 종종 명확하지 않으면서 일부 사용자에게 놀라울 수 있는 형식 불일치로 인해 예외가 발생합니다. 패턴을 인식하고 보다 의미 있는 예외를 throw하기로 결정했습니다.

완화 방법

프로젝션에서 쿼리 가능 개체 뒤에 ToList() 호출을 추가합니다.

context.Blogs.Select(b => context.Posts.Where(p => p.BlogId == b.Id).ToList())