다음을 통해 공유


EF Core 8(EF8)의 호환성이 손상되는 변경

이 페이지는 EF Core 7에서 EF Core 8로 업데이트되는 기존 애플리케이션의 호환성이 손상될 수 있는 API 및 동작 변경을 기록합니다. 이전 버전의 EF Core에서 업데이트하는 경우 이전의 호환성이 손상되는 변경을 검토합니다.

대상 프레임워크

EF Core 8은 .NET 8을 대상으로 합니다. 이전 .NET, .NET Core 및 .NET Framework 버전을 대상으로 하는 애플리케이션은 .NET 8을 대상으로 업데이트해야 합니다.

요약

주요 변경 내용 영향
LINQ 쿼리의 Contains은(는) 이전 SQL Server 버전에서 작동을 중지할 수 있음 높음
LINQ 쿼리 Contains에 대한의 가능한 성능 회귀 높음
JSON의 열거형이 기본적으로 문자열 대신 ints로 저장됨 높음
SQL Server datetime이 이제 .NET DateOnlyTimeOnly로 스캐폴드됨 중간
데이터베이스 생성 값이 있는 부울 열이 더 이상 null 허용으로 스캐폴드되지 않음 중간
SQLite Math 메서드는 이제 SQL로 변환됩니다. 낮음
ITypeBase가 일부 API에서 IEntityType을 대체 낮음
ValueGenerator 식은 공용 API를 사용해야 함 낮음
ExcludeFromMigrations가 더 이상 TPC 계층 구조의 다른 테이블을 제외하지 않음 낮음
섀도 없는 정수 키가 Cosmos 문서에 유지됨 낮음
관계형 모델은 컴파일된 모델에서 생성됩니다. 낮음
스캐폴딩은 다른 탐색 이름을 생성할 수 있습니다. 낮음
이제 판별자에 최대 길이가 있습니다. 낮음
SQL Server 키 값은 대/소문자를 구분하지 않고 비교됩니다. 낮음
여러 AddDbContext 호출이 서로 다른 순서로 적용됩니다. 낮음
TypeAttributeConventionBase로 대체된 EntityTypeAttributeConventionBase 낮음

높은 영향을 주는 변경 내용

LINQ 쿼리의 Contains는 이전 SQL Server 버전에서 작동을 중지할 수 있음

추적 이슈 #13617

이전 동작

EF는 매개 변수가 있는 값 목록에 대해 Contains 연산자를 사용하여 LINQ 쿼리를 특수하게 지원했습니다.

var names = new[] { "Blog1", "Blog2" };

var blogs = await context.Blogs
    .Where(b => names.Contains(b.Name))
    .ToArrayAsync();

EF Core 8.0 이전에는 EF에서 매개 변수가 있는 값을 상수로 SQL에 삽입했습니다.

SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] IN (N'Blog1', N'Blog2')

새 동작

EF Core 8.0부터 EF는 이제 많은 경우에 더 효율적이지만 SQL Server 2014 이하에서 지원되지 않는 SQL을 생성합니다.

SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] IN (
    SELECT [n].[value]
    FROM OPENJSON(@__names_0) WITH ([value] nvarchar(max) '$') AS [n]
)

최신 SQL Server 버전은 이전 호환성 수준으로 구성되어 새 SQL과 호환되지 않을 수도 있습니다. 이전 온-프레미스 SQL Server 인스턴스에서 마이그레이션된 Azure SQL 데이터베이스가 이전 호환성 수준을 넘어가면서 이 문제가 발생할 수도 있습니다.

이유

SQL에 상수 값을 삽입하면 많은 성능 문제가 발생하여 쿼리 계획 캐싱을 방해하고 다른 쿼리들이 불필요하게 제거됩니다. 새 EF Core 8.0 변환에서는 SQL Server OPENJSON 함수를 사용하여 값을 JSON 배열로 전송합니다. 그러면 이전 기술에 내재된 성능 문제가 해결됩니다. 그러나 OPENJSON 함수는 SQL Server 2014 이하에서 사용할 수 없습니다.

이 변경에 대한 자세한 내용은 블로그 게시물을 참조하세요.

해결 방법

데이터베이스가 SQL Server 2016(13.x) 이상이거나 Azure SQL을 사용하는 경우 다음 명령을 통해 데이터베이스의 구성된 호환성 수준을 확인합니다.

SELECT name, compatibility_level FROM sys.databases;

호환성 수준이 130 미만인 경우(SQL Server 2016) 최신 값으로 수정하는 것이 좋습니다(설명서).

그렇지 않은 경우 데이터베이스 버전이 실제로 SQL Server 2016보다 오래되었거나 어떤 이유로든 변경할 수 없는 이전 호환성 수준으로 설정된 경우 이전의 8.0 이전 SQL로 되돌리도록 EF를 구성할 수 있습니다. EF 9를 사용하는 경우 새로 도입된 TranslateParameterizedCollectionsToConstants사용할 수 있습니다.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.UseSqlServer("<CONNECTION STRING>", o => o.TranslateParameterizedCollectionsToConstants())

EF 8을 사용하는 경우 EF의 SQL 호환성 수준을 구성하여 SQL Server를 사용할 때 동일한 효과를 얻을 수 있습니다.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseSqlServer(@"<CONNECTION STRING>", o => o.UseCompatibilityLevel(120));

LINQ 쿼리에서 Contains 대한 가능한 쿼리 성능 회귀

추적 문제 #32394

이전 동작

EF는 매개 변수가 있는 값 목록에 대해 Contains 연산자를 사용하여 LINQ 쿼리를 특수하게 지원했습니다.

var names = new[] { "Blog1", "Blog2" };

var blogs = await context.Blogs
    .Where(b => names.Contains(b.Name))
    .ToArrayAsync();

EF Core 8.0 이전에는 EF에서 매개 변수가 있는 값을 상수로 SQL에 삽입했습니다.

SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] IN (N'Blog1', N'Blog2')

새 동작

EF Core 8.0부터 EF는 이제 다음을 생성합니다.

SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] IN (
    SELECT [n].[value]
    FROM OPENJSON(@__names_0) WITH ([value] nvarchar(max) '$') AS [n]
)

그러나 EF 8이 릴리스된 후 대부분의 경우 새 SQL이 더 효율적이지만 소수의 경우에서 훨씬 덜 효율적일 수 있으며 경우에 따라 쿼리 시간 제한이 발생할 수도 있습니다.

EF 8의 변경 내용에 대한 요약, EF 9에서 제공되는 부분 완화, 그리고 EF 10에 대한 향후 계획은 이 주석 을 참조하십시오.

해결 방법

EF 9를 사용하는 경우 새로 도입된 TranslateParameterizedCollectionsToConstants 사용하여 모든 쿼리에 대한 Contains 변환을 8.0 이전 동작으로 되돌릴 수 있습니다.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.UseSqlServer("<CONNECTION STRING>", o => o.TranslateParameterizedCollectionsToConstants())

EF 8을 사용하는 경우 EF의 SQL 호환성 수준을 구성하여 SQL Server를 사용할 때 동일한 효과를 얻을 수 있습니다.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseSqlServer(@"<CONNECTION STRING>", o => o.UseCompatibilityLevel(120));

마지막으로 다음과 같이 EF.Constant 사용하여 쿼리별로 번역을 제어할 수 있습니다.

var blogs = await context.Blogs
    .Where(b => EF.Constant(names).Contains(b.Name))
    .ToArrayAsync();

JSON의 열거형이 기본적으로 문자열 대신 ints로 저장됨

추적 이슈 #13617

이전 동작

EF7에서 JSON에 매핑된 열거형은 기본적으로 JSON 문서에서 문자열 값으로 저장됩니다.

새 동작

EF Core 8.0부터 이제 EF는 기본적으로 열거형을 JSON 문서에서 정수 값에 매핑합니다.

이유

기본적으로 EF는 항상 관계형 데이터베이스의 숫자 열에 열거형을 매핑했습니다. EF는 JSON의 값이 열 및 매개 변수의 값과 상호 작용하는 쿼리를 지원하므로 JSON의 값이 JSON이 아닌 열의 값과 일치해야 합니다.

해결 방법

문자열을 계속 사용하려면 변환을 사용하여 열거형 속성을 구성합니다. 예시:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>().Property(e => e.Status).HasConversion<string>();
}

또는 열거형 형식의 모든 속성에 대해

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Properties<StatusEnum>().HaveConversion<string>();
}

중간 영향을 주는 변경 내용

SQL Server datetime이 이제 .NET DateOnlyTimeOnly로 스캐폴드됨

추적 이슈 #24507

이전 동작

이전에는 date 또는 time 열을 사용하여 SQL Server 데이터베이스를 스캐폴딩할 때 EF는 DateTimeTimeSpan 형식을 사용하여 엔터티 속성을 생성했습니다.

새 동작

EF Core 8.0부터 datetimeDateOnlyTimeOnly로 스캐폴드됩니다.

이유

DateOnlyTimeOnly는 .NET 6.0에서 도입되었으며 데이터베이스 날짜 및 시간 형식을 매핑하는 데 적합합니다. DateTime는 특히 사용되지 않고 date에 매핑할 때 혼동을 일으킬 수 있는 시간 구성 요소를 포함하며, TimeSpan은 이벤트가 발생하는 하루 중 시간이 아닌 일(일 포함)을 나타냅니다. 새 형식을 사용하면 버그와 혼동을 방지하고 의도를 명확하게 파악할 수 있습니다.

해결 방법

이 변경 내용은 정기적으로 데이터베이스를 EF 코드 모델("데이터베이스 우선" 흐름)으로 다시 스캐폴드하는 사용자에게만 영향을 줍니다.

새로 스캐폴드된 DateOnlyTimeOnly 형식을 사용하도록 코드를 수정하여 이 변경에 대응하는 것이 좋습니다. 그러나 가능하지 않은 경우 스캐폴딩 템플릿을 편집하여 이전 매핑으로 되돌릴 수 있습니다. 이렇게 하려면 이 페이지에 설명된 대로 템플릿을 설정합니다. 그런 다음 EntityType.t4 파일을 편집하고 엔터티 속성이 생성되는 위치를 찾고(property.ClrType 검색) 코드를 다음으로 변경합니다.

        var clrType = property.GetColumnType() switch
        {
            "date" when property.ClrType == typeof(DateOnly) => typeof(DateTime),
            "date" when property.ClrType == typeof(DateOnly?) => typeof(DateTime?),
            "time" when property.ClrType == typeof(TimeOnly) => typeof(TimeSpan),
            "time" when property.ClrType == typeof(TimeOnly?) => typeof(TimeSpan?),
            _ => property.ClrType
        };

        usings.AddRange(code.GetRequiredUsings(clrType));

        var needsNullable = Options.UseNullableReferenceTypes && property.IsNullable && !clrType.IsValueType;
        var needsInitializer = Options.UseNullableReferenceTypes && !property.IsNullable && !clrType.IsValueType;
#>
    public <#= code.Reference(clrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
<#

데이터베이스 생성 값이 있는 부울 열이 더 이상 null 허용으로 스캐폴드되지 않습니다.

추적 이슈 #15070

이전 동작

이전에는 데이터베이스 기본 제약 조건이 있는 null을 허용하지 않는 bool 열이 null 허용 bool? 속성으로 스캐폴드되었습니다.

새 동작

EF Core 8.0부터는 null을 허용하지 않는 bool 열은 항상 null을 허용하지 않는 속성으로 스캐폴드됩니다.

이유

bool 속성 값이 CLR 기본값인 false인 경우 해당 값이 데이터베이스로 전송되지 않습니다. 데이터베이스의 열 기본값이 true인 경우 속성 값이더라도 false이더라도 데이터베이스의 값은 true(으)로 끝납니다. 그러나 EF8에서는 속성에 값이 있는지 여부를 확인하는 데 사용하는 sentinel이 변경될 수 있습니다. 이는 데이터베이스에서 생성된 bool 값이 있는 true 속성에 대해 자동으로 수행됩니다. 즉, 더 이상 속성을 null 허용으로 스캐폴드할 필요가 없습니다.

해결 방법

이 변경 내용은 정기적으로 데이터베이스를 EF 코드 모델("데이터베이스 우선" 흐름)으로 다시 스캐폴드하는 사용자에게만 영향을 줍니다.

null을 허용하지 않는 bool 속성을 사용하도록 코드를 수정하여 이 변경에 대응하는 것이 좋습니다. 그러나 가능하지 않은 경우 스캐폴딩 템플릿을 편집하여 이전 매핑으로 되돌릴 수 있습니다. 이렇게 하려면 이 페이지에 설명된 대로 템플릿을 설정합니다. 그런 다음 EntityType.t4 파일을 편집하고 엔터티 속성이 생성되는 위치를 찾고(property.ClrType 검색) 코드를 다음으로 변경합니다.

#>
        var propertyClrType = property.ClrType != typeof(bool)
                              || (property.GetDefaultValueSql() == null && property.GetDefaultValue() != null)
            ? property.ClrType
            : typeof(bool?);
#>
    public <#= code.Reference(propertyClrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
<#
<#

낮은 영향을 주는 변경 내용

SQLite Math 메서드는 이제 SQL로 변환됩니다.

추적 이슈 #18843

이전 동작

이전에는 Math의 Abs, Max, Min 및 Round 메서드만 SQL로 변환되었습니다. 쿼리의 최종 Select 식에 나타난 경우 다른 모든 멤버는 클라이언트에서 평가됩니다.

새 동작

EF Core 8.0에서는 해당 Math가 있는 모든 메서드가 SQL로 변환됩니다.

이러한 수학 함수는 기본적으로 제공하는 네이티브 SQLite 라이브러리에서 사용하도록 설정되었습니다(SQLitePCLRaw.bundle_e_sqlite3 NuGet 패키지에 대한 종속성을 통해). 또한 SQLitePCLRaw.bundle_e_sqlcipher에서 제공된 라이브러리에서도 사용하도록 설정되었습니다. 이러한 라이브러리 중 하나를 사용하는 경우 애플리케이션이 이러한 변경의 영향을 받지 않아야 합니다.

그러나 다른 수단으로 네이티브 SQLite 라이브러리를 포함하는 애플리케이션이 수학 함수를 사용하도록 설정하지 않을 수 있습니다. 이러한 경우 Math 메서드는 SQL로 변환되고 실행 시 이러한 함수 오류가 발생하지 않습니다.

이유

SQLite는 버전 3.35.0에서 기본 제공 수학 함수를 추가했습니다. 기본적으로 사용하지 않도록 설정되어 있더라도 EF Core SQLite 공급자에서 기본 번역을 제공하기로 결정할 정도로 널리 사용되고 있습니다.

또한 SQLitePCLRaw 프로젝트에서 Eric Sink와 협업하여 해당 프로젝트의 일부로 제공되는 모든 네이티브 SQLite 라이브러리에서 수학 함수를 사용하도록 설정했습니다.

해결 방법

중단을 해결하는 가장 간단한 방법은 가능한 경우 SQLITE_ENABLE_MATH_FUNCTIONS 컴파일 시간 옵션을 지정하여 네이티브 SQLite 라이브러리의 수학 함수를 사용하도록 설정하는 것입니다.

네이티브 라이브러리의 컴파일을 제어하지 않는 경우 Microsoft.Data.Sqlite API를 사용하여 런타임에 직접 함수를 만들어 중단을 해결할 수도 있습니다.

sqliteConnection
    .CreateFunction<double, double, double>(
        "pow",
        Math.Pow,
        isDeterministic: true);

또는 Select 식을 AsEnumerable로 구분된 두 부분으로 분할하여 클라이언트 평가를 강제 적용할 수 있습니다.

// Before
var query = dbContext.Cylinders
    .Select(
        c => new
        {
            Id = c.Id
            // May throw "no such function: pow"
            Volume = Math.PI * Math.Pow(c.Radius, 2) * c.Height
        });

// After
var query = dbContext.Cylinders
    // Select the properties you'll need from the database
    .Select(
        c => new
        {
            c.Id,
            c.Radius,
            c.Height
        })
    // Switch to client-eval
    .AsEnumerable()
    // Select the final results
    .Select(
        c => new
        {
            Id = c.Id,
            Volume = Math.PI * Math.Pow(c.Radius, 2) * c.Height
        });

ITypeBase가 일부 API에서 IEntityType을 대체

추적 이슈 #13947

이전 동작

이전에는 매핑된 모든 구조 형식이 엔터티 형식이었습니다.

새 동작

EF8에서 복합 형식이 도입되면서 이전에 IEntityType을(를) 사용했던 일부 API가 이제 엔터티 또는 복합 형식과 함께 사용될 수 있도록 ITypeBase을(를) 사용합니다. 다음 내용이 포함됩니다.

  • IProperty.DeclaringEntityType은(는) 이제 사용되지 않으며 대신 IProperty.DeclaringType을(를) 사용해야 합니다.
  • IEntityTypeIgnoredConvention은(는) 이제 사용되지 않으며 대신 ITypeIgnoredConvention을(를) 사용해야 합니다.
  • IValueGeneratorSelector.Select은(는) 이제 ITypeBase이(가) IEntityType일 수도, 그렇지 않을 수도 있도록 허용합니다.

이유

EF8에서 복합 형식이 도입되면서 이러한 API가 IEntityType 또는 IComplexType과(와) 함께 사용할 수 있게 되었습니다.

해결 방법

이전 API는 사용되지 않지만 EF10까지는 제거되지 않습니다. 코드를 업데이트하여 새 API를 최대한 빨리 사용할 수 있도록 합니다.

ValueConverter 및 ValueComparer 식은 컴파일된 모델에 공용 API를 사용해야 함

추적 이슈 #24896

이전 동작

이전에는 컴파일된 모델에 ValueConverterValueComparer 정의가 포함되지 않았으며, 따라서 임의 코드를 포함할 수 있었습니다.

새 동작

이제 EF는 ValueConverterValueComparer 개체에서 식을 추출하고 이러한 C#을 컴파일된 모델에 포함합니다. 이는 이러한 식이 공용 API만 사용해야 함을 의미합니다.

이유

EF 팀은 향후 AOT와 함께 EF Core 사용을 지원하기 위해 점진적으로 더 많은 구문을 컴파일된 모델로 이동하고 있습니다.

해결 방법

비교자가 사용하는 API를 공용으로 만듭니다. 예를 들어 다음과 같은 간단한 변환기를 생각해 봅니다.

public class MyValueConverter : ValueConverter<string, byte[]>
{
    public MyValueConverter()
        : base(v => ConvertToBytes(v), v => ConvertToString(v))
    {
    }

    private static string ConvertToString(byte[] bytes)
        => ""; // ... TODO: Conversion code

    private static byte[] ConvertToBytes(string chars)
        => Array.Empty<byte>(); // ... TODO: Conversion code
}

EF8에서 이 변환기를 컴파일된 모델에서 사용하려면 ConvertToStringConvertToBytes 메서드가 공개되어야 합니다. 예시:

public class MyValueConverter : ValueConverter<string, byte[]>
{
    public MyValueConverter()
        : base(v => ConvertToBytes(v), v => ConvertToString(v))
    {
    }

    public static string ConvertToString(byte[] bytes)
        => ""; // ... TODO: Conversion code

    public static byte[] ConvertToBytes(string chars)
        => Array.Empty<byte>(); // ... TODO: Conversion code
}

ExcludeFromMigrations가 더 이상 TPC 계층 구조의 다른 테이블을 제외하지 않음

추적 이슈 #30079

이전 동작

이전에는 TPC 계층 구조의 테이블에서 ExcludeFromMigrations을(를) 사용하면 계층 구조의 다른 테이블도 제외되었습니다.

새 동작

EF Core 8.0부터는 ExcludeFromMigrations이(가) 다른 테이블에 영향을 주지 않습니다.

이유

이전 동작은 버그였고, 프로젝트 전체의 계층을 관리하는 데 마이그레이션을 사용할 수 없게 했습니다.

해결 방법

제외해야 하는 다른 테이블에 명시적으로 ExcludeFromMigrations을(를) 사용합니다.

섀도 없는 정수 키가 Cosmos 문서에 유지됨

추적 이슈 #31664

이전 동작

이전에는 합성된 키 속성이 되는 조건과 일치하는 비섀도 정수 속성이 JSON 문서에 유지되지 않고 나가는 도중에 다시 합성되었습니다.

새 동작

EF Core 8.0부터 이제 이러한 속성이 유지됩니다.

이유

이전 동작은 버그였고, 합성된 키 조건과 일치하는 속성이 Cosmos에 유지되지 못하게 했습니다.

해결 방법

값이 유지되지 않게 하려면 모델에서 속성을 제외합니다. 또한 Microsoft.EntityFrameworkCore.Issue31664 AppContext 스위치를 true으(로) 설정하여 이 동작을 완전히 사용하지 않도록 설정할 수 있습니다. 자세한 내용은 라이브러리 소비자에 대한 AppContext를 참조하세요.

AppContext.SetSwitch("Microsoft.EntityFrameworkCore.Issue31664", isEnabled: true);

관계형 모델은 컴파일된 모델에서 생성됩니다.

추적 이슈 #24896

이전 동작

이전에는 컴파일된 모델을 사용하는 경우에도 관계형 모델이 런타임에 계산되었습니다.

새 동작

EF Core 8.0부터 관계형 모델은 생성된 컴파일된 모델의 일부입니다. 그러나 특히 큰 모델의 경우 생성된 파일이 컴파일되지 않을 수 있습니다.

이유

이는 시작 시간을 더욱 개선하기 위해 수행되었습니다.

해결 방법

생성된 *ModelBuilder.cs 파일을 편집하고 AddRuntimeAnnotation("Relational:RelationalModel", CreateRelationalModel()); 줄과 CreateRelationalModel() 메서드를 제거합니다.

스캐폴딩은 다른 탐색 이름을 생성할 수 있습니다.

추적 이슈 #27832

이전 동작

이전에는 기존 데이터베이스에서 DbContext 및 엔터티 형식을 스캐폴딩할 때 관계에 대한 탐색 이름이 여러 외래 키 열 이름의 공통 접두사에서 파생되는 경우가 있었습니다.

새 동작

EF Core 8.0부터 복합 외래 키의 열 이름에 대한 공통 접두사는 더 이상 탐색 이름을 생성하는 데 사용되지 않습니다.

이유

이는 경우에 따라 S, Student_ 또는 심지어 _과 같은 매우 형편없는 이름을 생성하는 모호한 명명 규칙입니다. 이 규칙이 없으면 이상한 이름이 더 이상 생성되지 않으며 탐색을 위한 명명 규칙도 더 간단해지기 때문에 어떤 이름이 생성될지 더 쉽게 이해하고 예측할 수 있습니다.

해결 방법

EF Core Power Tools에는 기존 방식으로 탐색을 계속 생성할 수 있는 옵션이 있습니다. 또는 생성된 코드를 T4 템플릿을 사용하여 완전히 사용자 지정할 수 있습니다. 이는 스캐폴딩 관계의 외래 키 속성을 예로 사용할 수 있으며 코드에 적절한 규칙을 사용하여 필요한 탐색 이름을 생성할 수 있습니다.

이제 판별자에는 최대 길이가 있습니다.

추적 이슈 #10691

이전 동작

이전에는 TPH 상속 매핑을 위해 만들어진 판별자 열이 SQL Server/Azure SQL에서 nvarchar(max)로 구성되었거나 다른 데이터베이스에서 이에 상응하는 제한되지 않은 문자열 형식으로 구성되었습니다.

새 동작

EF Core 8.0부터 판별자 열은 알려진 모든 판별자 값을 포괄하는 최대 길이로 만들어집니다. EF는 이러한 변경을 수행하기 위해 마이그레이션을 생성합니다. 그러나 판별자 열이 어떤 방식(예: 인덱스의 일부)으로 제한되는 경우 마이그레이션에서 만들어진 AlterColumn이 실패할 수 있습니다.

이유

nvarchar(max) 열은 가능한 모든 값의 길이를 알고 있는 경우 비효율적이고 불필요합니다.

해결 방법

열 크기는 명시적으로 무제한으로 설정할 수 있습니다.

modelBuilder.Entity<Foo>()
    .Property<string>("Discriminator")
    .HasMaxLength(-1);

SQL Server 키 값은 대/소문자를 구분하지 않고 비교됩니다.

추적 이슈 #27526

이전 동작

이전에는 SQL Server/Azure SQL Database 공급자를 사용하여 문자열 키가 있는 엔터티를 추적할 때 기본 .NET 대/소문자 구분 순서 비교자를 사용하여 키 값을 비교했습니다.

새 동작

EF Core 8.0부터 SQL Server/Azure SQL 문자열 키 값은 기본 .NET 대/소문자를 구분하지 않는 서수 비교자를 사용하여 비교됩니다.

이유

기본적으로 SQL Server에서는 일치 항목에 대한 외래 키 값을 주요 키 값과 비교할 때 대/소문자를 구분하지 않는 비교를 사용합니다. 이는 EF가 대/소문자 구분 비교를 사용하는 경우 필요한 경우 외래 키를 기본 키에 연결하지 않을 수 있음을 의미합니다.

해결 방법

사용자 지정 ValueComparer를 설정하여 대/소문자 구분 비교를 사용할 수 있습니다. 예시:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var comparer = new ValueComparer<string>(
        (l, r) => string.Equals(l, r, StringComparison.Ordinal),
        v => v.GetHashCode(),
        v => v);

    modelBuilder.Entity<Blog>()
        .Property(e => e.Id)
        .Metadata.SetValueComparer(comparer);

    modelBuilder.Entity<Post>(
        b =>
        {
            b.Property(e => e.Id).Metadata.SetValueComparer(comparer);
            b.Property(e => e.BlogId).Metadata.SetValueComparer(comparer);
        });
}

여러 AddDbContext 호출이 서로 다른 순서로 적용됩니다.

추적 문제 #32518

이전 동작

이전에는 여러 번 호출하거나 AddDbContextAddDbContextPoolAddDbContextFactoryAddPooledDbContextFactor 컨텍스트 유형이 동일하지만 구성이 충돌하는 경우 첫 번째 호출이 이겼습니다.

새 동작

EF Core 8.0부터 마지막 호출 1의 구성이 우선적으로 적용됩니다.

이유

메서드 앞이나 후에 ConfigureDbContext 구성을 추가하는 데 사용할 수 있는 새 메서드 Add* 와 일치하도록 변경되었습니다.

해결 방법

호출 순서 Add* 를 반대로 바릅니다.

TypeAttributeConventionBase로 대체된 EntityTypeAttributeConventionBase

새 동작

EF Core 8.0 EntityTypeAttributeConventionBase 에서 이름이 .로 TypeAttributeConventionBase바뀌었습니다.

이유

TypeAttributeConventionBase 는 이제 복합 형식 및 엔터티 형식에 사용할 수 있으므로 더 나은 기능을 나타냅니다.

해결 방법

사용량을 .로 바꾸기 EntityTypeAttributeConventionBaseTypeAttributeConventionBase