Dela via


Icke-bakåtkompatibla ändringar i EF Core 8 (EF8)

På den här sidan dokumenteras API-ändringar och beteendeändringar som kan bryta befintliga program som uppdateras från EF Core 7 till EF Core 8. Se till att gå igenom tidigare större ändringar om du uppdaterar från en tidigare version av EF Core.

Målramverk

EF Core 8 riktar in sig på .NET 8. Program som riktar sig till äldre .NET-, .NET Core- och .NET Framework-versioner måste uppdateras till målet .NET 8.

Sammanfattning

Brytande ändring Påverkan
Contains i LINQ-frågor kan sluta fungera med äldre SQL Server-versioner Hög
Möjliga frågeprestandaregressioner runt Contains i LINQ-frågor Hög
Uppräkningar i JSON lagras som heltal i stället för strängarna som standard Hög
SQL Server date och time genereras nu till .NET DateOnly och TimeOnly Medium
Booleanska kolumner med ett databasgenererat värde genereras inte längre som nullable Medium
SQLite Math metoder översätts nu till SQL Låg
ITypeBase ersätter IEntityType i vissa API:er Låg
ValueGenerator-uttryck måste använda offentliga API:er Låg
ExcludeFromMigrations exkluderar inte längre andra tabeller i en TPC-hierarki Låg
Icke-speglande heltalsnycklar sparas i Cosmos-dokument Låg
Relationsmodell genereras i den kompilerade modellen Låg
Ramverk kan skapa olika navigeringsnamn Låg
Discriminatorer har nu en maxlängd Låg
SQL Server-nyckelvärden jämförs utan hänsyn till skiftläge Låg
Flera AddDbContext-anrop tillämpas i olika ordning Låg
EntityTypeAttributeConventionBase ersatt med TypeAttributeConventionBase Låg

Ändringar med hög påverkan

Contains i LINQ-frågor kan sluta fungera i äldre SQL Server-versioner

spårningsproblem #13617

Gammalt beteende

EF hade specialiserat stöd för LINQ-frågor med Contains operatorn över en parameteriserad värdelista:

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

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

Före EF Core 8.0 infogade EF de parameteriserade värdena som konstanter i SQL:

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

Nytt beteende

Från och med EF Core 8.0 genererar EF nu SQL som är mer effektivt i många fall, men som inte stöds på SQL Server 2014 och nedan:

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]
)

Observera att nyare SQL Server-versioner kan konfigureras med en äldre kompatibilitetsnivå, vilket också gör dem inkompatibla med den nya SQL-koden. Detta kan också inträffa med en Azure SQL-databas som migrerades från en tidigare lokal SQL Server-instans och som överförde den gamla kompatibilitetsnivån.

Varför

Införandet av konstanta värden i SQL skapar många prestandaproblem, vilket motverkar cachelagring av frågeplaner och orsakar onödiga borttagningar av andra frågor. Den nya EF Core 8.0-översättningen använder funktionen SQL Server OPENJSON för att i stället överföra värdena som en JSON-matris. Detta löser prestandaproblemen i den tidigare tekniken. Funktionen OPENJSON är dock inte tillgänglig i SQL Server 2014 eller senare.

Mer information om den här ändringen finns i det här blogginlägget.

Åtgärder

Om databasen är SQL Server 2016 (13.x) eller senare, eller om du använder Azure SQL, kontrollerar du databasens konfigurerade kompatibilitetsnivå via följande kommando:

SELECT name, compatibility_level FROM sys.databases;

Om kompatibilitetsnivån är under 130 (SQL Server 2016) bör du överväga att ändra den till ett nyare värde (dokumentation).

Om databasversionen inte är äldre än SQL Server 2016, eller om den är inställd på en gammal kompatibilitetsnivå som du inte kan ändra av någon anledning, kan du konfigurera EF för att återgå till den äldre, före 8.0 SQL. Om du använder EF 9 kan du använda den nyligen introducerade TranslateParameterizedCollectionsToConstants:

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

Om du använder EF 8 kan du uppnå samma effekt när du använder SQL Server genom att konfigurera EF:s SQL-kompatibilitetsnivå:

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

Möjliga frågeprestandaregressioner runt Contains i LINQ-frågor

spårningsproblem #32394

Gammalt beteende

EF hade specialiserat stöd för LINQ-frågor med Contains operatorn över en parameteriserad värdelista:

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

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

Före EF Core 8.0 infogade EF de parameteriserade värdena som konstanter i SQL:

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

Nytt beteende

Från och med EF Core 8.0 genererar EF nu följande:

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]
)

Men efter lanseringen av EF 8 visade det sig att även om den nya SQL:n är effektivare i de flesta fall kan den vara dramatiskt mindre effektiv i en minoritet av fallen, även om den orsakar tidsgränser för frågor i vissa fall.

Se den här kommentaren för en sammanfattning av ändringen i EF 8, de partiella minskningarna som anges i EF 9 och planen för EF 10.

Åtgärder

Om du använder EF 9 kan du använda den nyligen introducerade TranslateParameterizedCollectionsToConstants för att återställa Contains översättning för alla frågor tillbaka till beteendet före 8.0:

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

Om du använder EF 8 kan du uppnå samma effekt när du använder SQL Server genom att konfigurera EF:s SQL-kompatibilitetsnivå:

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

Slutligen kan du styra översättningen efter fråga med hjälp av EF.Constant på följande sätt:

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

Uppräkningar i JSON lagras som heltal i stället för strängar som standardinställning.

spårningsproblem #13617

Gammalt beteende

I EF7 lagras enumerationer som mappas till JSON som standard som strängvärden i JSON-dokumentet.

Nytt beteende

Från och med EF Core 8.0 mappar EF nu som standard uppräkningar till heltalsvärden i JSON-dokumentet.

Varför

EF har alltid som standard mappat uppräkningar till en numerisk kolumn i relationsdatabaser. Eftersom EF stöder frågor där värden från JSON interagerar med värden från kolumner och parametrar är det viktigt att värdena i JSON matchar värdena i kolumnen som inte är JSON.

Åtgärder

Om du vill fortsätta använda strängar konfigurerar du uppräkningsegenskapen med en konvertering. Till exempel:

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

Eller för alla egenskaper av uppräkningstypen::

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

Ändringar med medelpåverkan

Nu skapar SQL Server date och time mallar till .NET DateOnly och TimeOnly

spårningsproblem #24507

Gammalt beteende

Tidigare, när du skapar en SQL Server-databas med date eller time kolumner, skulle EF generera entitetsegenskaper med typer DateTime och TimeSpan.

Nytt beteende

Från och med EF Core 8.0 genereras date och time som DateOnly och TimeOnly.

Varför

DateOnly och TimeOnly introducerades i .NET 6.0 och är en perfekt matchning för att mappa databasens datum- och tidstyper. DateTime innehåller särskilt en tidskomponent som inte används och kan orsaka förvirring när den mappas till date, och TimeSpan representerar ett tidsintervall – eventuellt inklusive dagar – i stället för en tid på dagen då en händelse inträffar. Att använda de nya typerna förhindrar buggar och förvirring och ger klarhet i avsikten.

Åtgärder

Den här ändringen påverkar endast användare som regelbundet omskapar sin databas till en EF-kodmodell ("databas-först"-flöde).

Vi rekommenderar att du reagerar på den här ändringen genom att ändra koden så att den använder de nyligen kodade DateOnly- och TimeOnly typerna. Om det däremot inte är möjligt kan du redigera mallarna för strukturen för att gå tillbaka till den tidigare mappningen. Det gör du genom att konfigurera mallarna enligt beskrivningen på den här sidan. Redigera sedan filen EntityType.t4, leta reda på var entitetsegenskaperna genereras (sök efter property.ClrType) och ändra koden till följande:

        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!;" : "" #>
<#

Booleska kolumner med ett databasgenererat värde genereras inte längre som nullable.

spårningsproblem #15070

Gammalt beteende

Tidigare har icke-nullbara bool kolumner med en databasstandardbegränsning kodats som nullbara bool? egenskaper.

Nytt beteende

Från och med EF Core 8.0 genereras icke-nullbara bool-kolumnerna alltid som icke-nullbara egenskaperna.

Varför

En bool-egenskap får inte sitt värde skickat till databasen om värdet är false, vilket är CLR-standardvärdet. Om databasen har standardvärdet true för kolumnen, hamnar värdet i databasen som falseäven om värdet för egenskapen är true. Dock kan sentineln, som i EF8 används för att avgöra om en egenskap har ett värde, ändras. Detta görs automatiskt för bool-egenskaper med ett databasgenererat värde på true, vilket innebär att det inte längre är nödvändigt att generera egenskaperna som nullbara.

Åtgärder

Den här ändringen påverkar endast användare som regelbundet omskapar sin databas till en EF-kodmodell ("databas-först"-flöde).

Vi rekommenderar att du reagerar på den här ändringen genom att ändra koden så att den använder egenskapen bool som inte kan nulleras. Om det däremot inte är möjligt kan du redigera mallarna för strukturen för att gå tillbaka till den tidigare mappningen. Det gör du genom att konfigurera mallarna enligt beskrivningen på den här sidan. Redigera sedan filen EntityType.t4, leta reda på var entitetsegenskaperna genereras (sök efter property.ClrType) och ändra koden till följande:

#>
        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!;" : "" #>
<#
<#

Ändringar med låg påverkan

SQLite-Math metoder översätts nu till SQL

spårningsproblem #18843

Gammalt beteende

Tidigare översattes endast metoderna Abs, Max, Min och Round på Math till SQL. Alla andra medlemmar skulle utvärderas på klientsidan om de visas i det slutliga Select-uttrycket för en sökfråga.

Nytt beteende

I EF Core 8.0 översätts alla Math metoder med motsvarande SQLite-matematiska funktioner till SQL.

Dessa matematiska funktioner har aktiverats i det interna SQLite-biblioteket som vi tillhandahåller som standard (genom vårt beroende av SQLitePCLRaw.bundle_e_sqlite3 NuGet-paketet). De har också aktiverats i biblioteket som tillhandahålls av SQLitePCLRaw.bundle_e_sqlcipher. Om du använder något av dessa bibliotek bör ditt program inte påverkas av den här ändringen.

Det finns dock en chans att program inklusive det interna SQLite-biblioteket på andra sätt kanske inte aktiverar matematiska funktioner. I dessa fall översätts Math-metoderna till SQL och påträffar ingen sådan funktion fel när de körs.

Varför

SQLite har lagt till inbyggda matematiska funktioner i version 3.35.0. Även om de är inaktiverade som standard har de blivit tillräckligt genomgripande för att vi ska kunna tillhandahålla standardöversättningar för dem i vår EF Core SQLite-provider.

Vi samarbetade också med Eric Sink i projektet SQLitePCLRaw för att aktivera matematiska funktioner i alla interna SQLite-bibliotek som ingår i projektet.

Åtgärder

Det enklaste sättet att åtgärda avbrott är, när det är möjligt, att aktivera matematikfunktionerna i det inbyggda SQLite-biblioteket genom att ange alternativet SQLITE_ENABLE_MATH_FUNCTIONS vid kompileringstidspunkt.

Om du inte styr kompilering av det interna biblioteket kan du även åtgärda pauser genom att skapa funktionerna själv vid körning med hjälp av api:erna Microsoft.Data.Sqlite.

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

Du kan också tvinga fram klientutvärdering genom att dela upp Select-uttrycket i två delar avgränsade med 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 ersätter IEntityType i vissa API:er

spårningsproblem #13947

Gammalt beteende

Tidigare var alla mappade strukturella typer entitetstyper.

Nytt beteende

Med introduktionen av komplexa typer i EF8 använder vissa API:er som tidigare använt en IEntityType nu ITypeBase så att API:erna kan användas med antingen entitetstyper eller komplexa typer. Detta inkluderar:

  • IProperty.DeclaringEntityType är nu föråldrad och IProperty.DeclaringType bör användas i stället.
  • IEntityTypeIgnoredConvention är nu föråldrad och ITypeIgnoredConvention bör användas i stället.
  • IValueGeneratorSelector.Select accepterar nu en ITypeBase som kan vara, men som inte behöver vara en IEntityType.

Varför

Med introduktionen av komplexa typer i EF8 kan dessa API:er användas med antingen IEntityType eller IComplexType.

Åtgärder

De gamla API:erna är föråldrade, men tas inte bort förrän EF10. Koden bör uppdateras för att använda de nya API:erna ASAP.

ValueConverter- och ValueComparer-uttryck måste använda offentliga API:er för den kompilerade modellen

spårningsproblem #24896

Gammalt beteende

Tidigare inkluderades inte ValueConverter och ValueComparer definitioner i den kompilerade modellen och kan därför innehålla godtycklig kod.

Nytt beteende

EF extraherar nu uttrycken från ValueConverter- och ValueComparer-objekten och inkluderar dessa C# i den kompilerade modellen. Det innebär att dessa uttryck endast får använda offentligt API.

Varför

EF-teamet flyttar gradvis fler konstruktioner till den kompilerade modellen för att stödja användning av EF Core med AOT i framtiden.

Åtgärder

Gör API:erna som används av jämförelsen offentliga. Tänk dig till exempel den här enkla konverteraren:

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
}

Om du vill använda konverteraren i en kompilerad modell med EF8 måste metoderna ConvertToString och ConvertToBytes offentliggöras. Till exempel:

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 utesluter inte längre andra tabeller i en TPC-hierarki

spårningsproblem #30079

Gammalt beteende

Tidigare skulle användning av ExcludeFromMigrations i en tabell i en TPC-hierarki också utesluta andra tabeller i hierarkin.

Nytt beteende

Från och med EF Core 8.0 påverkar ExcludeFromMigrations inte andra tabeller.

Varför

Det gamla beteendet var en bugg och förhindrade migreringar från att användas för att hantera hierarkier mellan projekt.

Åtgärder

Använd ExcludeFromMigrations explicit i andra tabeller som ska undantas.

Icke-skugg heltalsnycklar sparas i Cosmos-dokument

spårningsproblem #31664

Gammalt beteende

Tidigare skulle icke-skugg heltalsegenskaper som matchar kriterierna för att vara en syntetiserad nyckelegenskap inte bevaras i JSON-dokumentet, utan syntetiserades i stället på nytt på vägen ut.

Nytt beteende

Från och med EF Core 8.0 sparas dessa egenskaper nu.

Varför

Det gamla beteendet var en bugg och förhindrade att egenskaper som matchar de syntetiserade nyckelkriterierna bevarades till Cosmos.

Åtgärder

Undanta egenskapen från modellen om dess värde inte ska sparas. Dessutom kan du inaktivera det här beteendet helt genom att ange Microsoft.EntityFrameworkCore.Issue31664 AppContext växla till true, se AppContext för bibliotekskonsumenter mer information.

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

Relationsmodellen genereras i den kompilerade modellen

spårningsproblem #24896

Gammalt beteende

Tidigare beräknades relationsmodellen vid körning även när en kompilerad modell användes.

Nytt beteende

Från och med EF Core 8.0 är relationsmodellen en del av den genererade kompilerade modellen. För särskilt stora modeller kan dock den genererade filen inte kompileras.

Varför

Detta gjordes för att ytterligare förbättra starttiden.

Åtgärder

Redigera den genererade *ModelBuilder.cs-filen och ta bort raden AddRuntimeAnnotation("Relational:RelationalModel", CreateRelationalModel()); samt metoden CreateRelationalModel().

Ramverk kan generera olika navigeringsnamn

spårningsproblem #27832

Gammalt beteende

Tidigare när man skapade en DbContext och entitetstyper från en befintlig databas kom navigeringsnamnen för relationer ibland från ett gemensamt prefix av flera främmande nyckelkolumnnamn.

Nytt beteende

Från och med EF Core 8.0 används inte längre vanliga prefix för kolumnnamn från en sammansatt sekundärnyckel för att generera navigeringsnamn.

Varför

Det här är en obskyr namngivningsregel som ibland genererar mycket dåliga namn som S, Student_eller till och med bara _. Utan den här regeln genereras inte längre konstiga namn, och namngivningskonventionerna för navigering blir också enklare, vilket gör det lättare att förstå och förutsäga vilka namn som ska genereras.

Åtgärder

EF Core Power Tools har möjlighet att fortsätta att generera navigeringar på det gamla sättet. Du kan också anpassa koden helt med hjälp av T4-mallar. Detta kan användas för att exemplifiera de främmande nyckelegenskaperna i stödjande relationer och tillämpa den regel som är lämplig för din kod för att generera de navigeringsnamn du behöver.

Diskriminatorer har nu en maxlängd

spårningsproblem #10691

Gammalt beteende

Tidigare konfigurerades diskriminatorkolumner skapade för TPH-arvsmappning som nvarchar(max) på SQL Server/Azure SQL, eller som motsvarande obundna strängtyp på andra databaser.

Nytt beteende

Från och med EF Core 8.0 skapas diskriminerande kolumner med en maximal längd som täcker alla kända diskriminerande värden. EF genererar en migrering för att göra denna ändring. Men om diskriminator-kolumnen är begränsad på något sätt – till exempel som en del av ett index – kan AlterColumn, som skapas av Migrations, misslyckas.

Varför

nvarchar(max) kolumner är ineffektiva och onödiga när längden på alla möjliga värden är kända.

Åtgärder

Kolumnstorleken kan göras explicit obunden.

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

SQL Server-nyckelvärden jämförs skiftlägesokänsligt

spårningsproblem #27526

Gammalt beteende

Tidigare, när du spårade entiteter med strängnycklar i SQL Server/Azure SQL-databasprovidrar, jämfördes nyckelvärdena med den skiftlägeskänsliga ordningskomparatorn i .NET som standard.

Nytt beteende

Från och med EF Core 8.0 jämförs SQL Server/Azure SQL-strängnyckelvärden med den skiftlägesokänsliga ordningsjämföraren som är standard i .NET.

Varför

Som standard använder SQL Server skiftlägesokänsliga jämförelser när du jämför sekundärnyckelvärden för matchningar med huvudnyckelvärden. Det betyder att när EF använder skiftlägeskänsliga jämförelser kanske det inte ansluter en främmande nyckel till en huvudnyckel när det borde.

Åtgärder

Skiftlägeskänsliga jämförelser kan användas genom att ställa in en anpassad inställning för ValueComparer. Till exempel:

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);
        });
}

Flera AddDbContext-anrop tillämpas i olika ordning

spårningsproblem #32518

Gammalt beteende

Tidigare, när flera anrop gjordes till AddDbContext, AddDbContextPool, AddDbContextFactory eller AddPooledDbContextFactor med samma kontexttyp men med motstridiga konfigurationer, var det den första som vann.

Nytt beteende

Från och med EF Core 8.0 har konfigurationen från det senaste anropet företräde.

Varför

Detta ändrades så att det överensstämmer med den nya metoden ConfigureDbContext som kan användas för att lägga till konfiguration antingen före eller efter Add*-metoderna.

Åtgärder

Ändra ordningen på Add*-anrop.

EntityTypeAttributeConventionBase ersatt med TypeAttributeConventionBase

Nytt beteende

I EF Core 8.0 bytte EntityTypeAttributeConventionBase namn till TypeAttributeConventionBase.

Varför

TypeAttributeConventionBase representerar funktionen bättre eftersom den nu kan användas för komplexa typer och entitetstyper.

Åtgärder

Ersätt EntityTypeAttributeConventionBase med TypeAttributeConventionBase.