Condividi tramite


Modifiche di rilievo in EF Core 5.0

Le seguenti modifiche all'API e al comportamento possono interrompere l'aggiornamento delle applicazioni esistenti a EF Core 5.0.0.

Riepilogo

Modifica di rilievo Impatto
EF Core 5.0 non supporta .NET Framework Medio
IProperty.GetColumnName() è ora obsoleto Medio
La precisione e la scala sono necessarie per i decimali Medio
La navigazione obbligatoria o non nullable dall'entità a quella dipendente ha una semantica diversa Medio
La definizione della query viene sostituita con metodi specifici del provider Medio
Gli spostamenti di riferimento non Null non vengono sovrascritti dalle query Medio
ToView() viene trattato in modo diverso dalle migrazioni Medio
ToTable(null) contrassegna il tipo di entità come non mappato a una tabella Medio
Rimozione del metodo HasGeometricDimension dall'estensione SQLite NTS Basso
Azure Cosmos DB: la chiave di partizione viene ora aggiunta alla chiave primaria Basso
Azure Cosmos DB: id proprietà rinominata in __id Basso
Azure Cosmos DB: byte[] è ora archiviato come stringa base64 anziché come matrice numerica Basso
Azure Cosmos DB: GetPropertyName e SetPropertyName sono stati rinominati Basso
I generatori di valori vengono chiamati quando lo stato dell'entità viene modificato da Scollegato a Non modificato, Aggiornato o Eliminato Basso
IMigrationsModelDiffer usa ora IRelationalModel Basso
I discriminatori sono di sola lettura Basso
Entity Framework specifico del provider. I metodi di Funzioni generano un'eccezione per il provider InMemory Basso
IndexBuilder.HasName è ora obsoleto Basso
È ora incluso un pluralizzatore per lo scaffolding di modelli inversi Basso
INavigationBase sostituisce INavigation in alcune API per supportare gli spostamenti ignorati Basso
Alcune query con raccolta correlata che usano Distinct o GroupBy non sono più supportate Basso
L'uso di una raccolta di tipi queryable nella proiezione non è supportato Basso

Modifiche a impatto medio

EF Core 5.0 non supporta .NET Framework

Problema n. 15498

Comportamento precedente

EF Core 3.1 è destinato a .NET Standard 2.0, supportato da .NET Framework.

Nuovo comportamento

EF Core 5.0 è destinato a .NET Standard 2.1, che non è supportato da .NET Framework. Ciò significa che EF Core 5.0 non può essere usato con le applicazioni .NET Framework.

Perché

Questo fa parte del movimento più ampio tra i team .NET volti all'unificazione a un singolo framework di destinazione .NET. Per altre informazioni, vedere il futuro di .NET Standard.

Soluzioni di prevenzione

Le applicazioni .NET Framework possono continuare a usare EF Core 3.1, ovvero una versione LTS (Long-Term Support). In alternativa, le applicazioni possono essere aggiornate per usare .NET Core 3.1 o .NET 5, che supportano entrambi .NET Standard 2.1.

IProperty.GetColumnName() è ora obsoleto

Problema di rilevamento n. 2266

Comportamento precedente

GetColumnName() restituito il nome della colonna a cui è mappata una proprietà.

Nuovo comportamento

GetColumnName() restituisce comunque il nome di una colonna a cui è stato eseguito il mapping di una proprietà, ma questo comportamento è ora ambiguo perché EF Core 5 supporta TPT e il mapping simultaneo a una visualizzazione o a una funzione in cui questi mapping potrebbero usare nomi di colonna diversi per la stessa proprietà.

Perché

Questo metodo è stato contrassegnato come obsoleto per guidare gli utenti a un overload più accurato - GetColumnName(IProperty, StoreObjectIdentifier).

Soluzioni di prevenzione

Se il tipo di entità viene mappato solo a una singola tabella e non a viste, funzioni o più tabelle, è GetColumnBaseName(IReadOnlyProperty) possibile usare in EF Core 5.0 e 6.0 per ottenere il nome della tabella. Ad esempio:

var columnName = property.GetColumnBaseName();

In EF Core 7.0, questo può essere sostituito di nuovo con il nuovo GetColumnName, che si comporta come l'originale ha fatto per i mapping semplici e di sola tabella.

Se è possibile eseguire il mapping del tipo di entità a viste, funzioni o più tabelle, è necessario ottenere un oggetto StoreObjectIdentifier per identificare la tabella, la vista o la funzione. Può quindi essere usato per ottenere il nome della colonna per l'oggetto store. Ad esempio:

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

La precisione e la scala sono necessarie per i decimali

Problema di rilevamento n. 19293

Comportamento precedente

EF Core in genere non ha impostato precisione e scalabilità su SqlParameter oggetti. Ciò significa che la precisione completa e la scala sono state inviate a SQL Server, a questo punto SQL Server si arrotonderebbe in base alla precisione e alla scala della colonna del database.

Nuovo comportamento

EF Core ora imposta la precisione e la scalabilità sui parametri usando i valori configurati per le proprietà nel modello EF Core. Ciò significa che l'arrotondamento avviene ora in SqlClient. Consequenziale, se la precisione e la scala configurate non corrispondono alla precisione e alla scala del database, l'arrotondamento visualizzato può cambiare.

Perché

Le funzionalità più recenti di SQL Server, tra cui Always Encrypted, richiedono che i facet dei parametri siano specificati completamente. SqlClient ha inoltre apportato una modifica all'arrotondamento anziché troncare i valori decimali, in modo da corrispondere al comportamento di SQL Server. Ciò ha consentito a EF Core di impostare questi facet senza modificare il comportamento per i decimali configurati correttamente.

Soluzioni di prevenzione

Eseguire il mapping delle proprietà decimali usando un nome di tipo che include precisione e scala. Ad esempio:

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

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

In alternativa, usare HasPrecision nelle API di compilazione del modello. Ad esempio:

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

La navigazione obbligatoria o non nullable dall'entità a quella dipendente ha una semantica diversa

Problema di rilevamento n. 17286

Comportamento precedente

Solo gli spostamenti all'entità di sicurezza possono essere configurati in base alle esigenze. Pertanto, usando RequiredAttribute nel riquadro di spostamento verso l'entità dipendente (l'entità contenente la chiave esterna) o contrassegnandola come non nullable, crea invece la chiave esterna nel tipo di entità che definisce.

Nuovo comportamento

Con il supporto aggiunto per i dipendenti necessari, è ora possibile contrassegnare qualsiasi navigazione di riferimento come richiesto, ovvero nel caso indicato sopra la chiave esterna verrà definita sull'altro lato della relazione e le proprietà non verranno contrassegnate come necessarie.

La chiamata IsRequired prima di specificare la fine dipendente è ora ambigua:

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

Perché

Il nuovo comportamento è necessario per abilitare il supporto per i dipendenti necessari (vedere #12100).

Soluzioni di prevenzione

Rimuovere RequiredAttribute dal riquadro di spostamento all'oggetto dipendente e posizionarlo invece nel riquadro di spostamento all'entità o configurare la relazione in OnModelCreating:

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

La definizione della query viene sostituita con metodi specifici del provider

Problema di rilevamento n. 18903

Comportamento precedente

I tipi di entità sono stati mappati alla definizione delle query a livello di core. Ogni volta che il tipo di entità è stato usato nella radice della query del tipo di entità è stato sostituito dalla query di definizione per qualsiasi provider.

Nuovo comportamento

Le API per la definizione della query sono deprecate. Sono state introdotte nuove API specifiche del provider.

Perché

Durante la definizione delle query è stata implementata come query sostitutiva ogni volta che viene usata la radice di query nella query, si sono verificati alcuni problemi:

  • Se la definizione della query sta proiettando il tipo di entità usando new { ... } nel Select metodo , identificare che come entità richiede lavoro aggiuntivo e renderlo incoerente con il modo in cui EF Core considera i tipi nominale nella query.
  • Per i provider relazionali FromSql è comunque necessario passare la stringa SQL nel formato dell'espressione LINQ.

Inizialmente è stata introdotta la definizione delle query come viste lato client da usare con il provider in memoria per le entità senza chiave (analogamente alle viste di database nei database relazionali). Tale definizione semplifica il test dell'applicazione su un database in memoria. Successivamente sono diventati ampiamente applicabili, che era utile ma portato incoerente e difficile da comprendere il comportamento. Così abbiamo deciso di semplificare il concetto. È stata creata una definizione basata su LINQ esclusiva del provider in memoria e le si tratta in modo diverso. Per altre informazioni, vedere questo problema.

Soluzioni di prevenzione

Per i provider relazionali, usare il ToSqlQuery metodo in OnModelCreating e passare una stringa SQL da usare per il tipo di entità. Per il provider in memoria, usare ToInMemoryQuery il metodo in OnModelCreating e passare una query LINQ da usare per il tipo di entità.

Gli spostamenti di riferimento non Null non vengono sovrascritti dalle query

Problema di rilevamento n. 2693

Comportamento precedente

In EF Core 3.1, gli spostamenti di riferimento inizializzati con entusiasmo a valori non Null vengono talvolta sovrascritti dalle istanze di entità dal database, indipendentemente dal fatto che i valori di chiave corrispondano o meno. Tuttavia, in altri casi, EF Core 3.1 esegue l'operazione opposta e lascia il valore non Null esistente.

Nuovo comportamento

A partire da EF Core 5.0, gli spostamenti di riferimento non Null non vengono mai sovrascritti dalle istanze restituite da una query.

Si noti che l'inizializzazione eager di una raccolta in una raccolta vuota è ancora supportata.

Perché

L'inizializzazione di una proprietà di navigazione di riferimento a un'istanza di entità "vuota" comporta uno stato ambiguo. Ad esempio:

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

In genere, una query per blog e autori creerà Blog prima istanze e quindi imposta le istanze appropriate Author in base ai dati restituiti dal database. In questo caso, tuttavia, ogni Blog.Author proprietà è già inizializzata in un oggetto vuoto Author. Ad eccezione di EF Core, non è possibile sapere che questa istanza è "vuota". Pertanto, la sovrascrittura di questa istanza potrebbe potenzialmente generare automaticamente un'eccezione valida Author. Di conseguenza, EF Core 5.0 ora non sovrascrive in modo coerente una navigazione già inizializzata.

Questo nuovo comportamento si allinea anche al comportamento di EF6 nella maggior parte dei casi, anche se durante l'analisi sono stati rilevati anche alcuni casi di incoerenza in EF6.

Soluzioni di prevenzione

Se viene rilevata questa interruzione, la correzione consiste nell'interrompere l'inizializzazione delle proprietà di navigazione di riferimento.

ToView() viene trattato in modo diverso dalle migrazioni

Problema di rilevamento n. 2725

Comportamento precedente

La chiamata ToView(string) effettuata dalle migrazioni ignora il tipo di entità oltre a mapparla a una vista.

Nuovo comportamento

Contrassegna ora ToView(string) il tipo di entità come non mappato a una tabella oltre a eseguirne il mapping a una vista. Ciò comporta la prima migrazione dopo l'aggiornamento a EF Core 5 per provare a eliminare la tabella predefinita per questo tipo di entità perché non viene più ignorata.

Perché

EF Core consente ora di eseguire il mapping di un tipo di entità sia a una tabella che a una vista contemporaneamente, quindi ToView non è più un indicatore valido che deve essere ignorato dalle migrazioni.

Soluzioni di prevenzione

Usare il codice seguente per contrassegnare la tabella mappata come esclusa dalle migrazioni:

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

ToTable(null) contrassegna il tipo di entità come non mappato a una tabella

Problema di rilevamento n. 21172

Comportamento precedente

ToTable(null) il nome della tabella viene reimpostato sul valore predefinito.

Nuovo comportamento

ToTable(null) contrassegna ora il tipo di entità come non mappato ad alcuna tabella.

Perché

EF Core consente ora di eseguire il mapping di un tipo di entità a una tabella e a una vista contemporaneamente, quindi ToTable(null) viene usato per indicare che non è mappato ad alcuna tabella.

Soluzioni di prevenzione

Usare il codice seguente per reimpostare il nome della tabella sul valore predefinito se non è mappato a una visualizzazione o a una funzione DbFunction:

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

Modifiche a basso impatto

Rimozione del metodo HasGeometricDimension dall'estensione SQLite NTS

Problema di rilevamento n. 14257

Comportamento precedente

HasGeometricDimension è stato usato per abilitare dimensioni aggiuntive (Z e M) nelle colonne geometry. Tuttavia, ha avuto effetto solo sulla creazione del database. Non è necessario specificarlo per eseguire query sui valori con dimensioni aggiuntive. Non funzionava correttamente anche durante l'inserimento o l'aggiornamento di valori con dimensioni aggiuntive (vedere #14257).

Nuovo comportamento

Per abilitare l'inserimento e l'aggiornamento dei valori geometry con dimensioni aggiuntive (Z e M), la dimensione deve essere specificata come parte del nome del tipo di colonna. Questa API corrisponde più strettamente al comportamento sottostante della funzione AddGeometryColumn di SpatiaLite.

Perché

L'uso di HasGeometricDimension dopo aver specificato la dimensione nel tipo di colonna non è necessario e ridondante, quindi HasGeometricDimension è stato rimosso completamente.

Soluzioni di prevenzione

Utilizzare HasColumnType per specificare la dimensione:

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: la chiave di partizione viene ora aggiunta alla chiave primaria

Problema di rilevamento n. 15289

Comportamento precedente

La proprietà della chiave di partizione è stata aggiunta solo alla chiave alternativa che include id.

Nuovo comportamento

La proprietà della chiave di partizione viene ora aggiunta anche alla chiave primaria per convenzione.

Perché

Questa modifica migliora l'allineamento del modello con la semantica di Azure Cosmos DB e migliora le prestazioni di Find e alcune query.

Soluzioni di prevenzione

Per impedire l'aggiunta della proprietà della chiave di partizione alla chiave primaria, configurarla in OnModelCreating.

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

Azure Cosmos DB: id proprietà rinominata in __id

Problema di rilevamento n. 17751

Comportamento precedente

Anche la proprietà shadow mappata alla id proprietà JSON è stata denominata id.

Nuovo comportamento

La proprietà shadow creata per convenzione è ora denominata __id.

Perché

Questa modifica rende meno probabile che la id proprietà sia in conflitto con una proprietà esistente nel tipo di entità.

Soluzioni di prevenzione

Per tornare al comportamento 3.x, configurare la id proprietà in OnModelCreating.

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

Azure Cosmos DB: byte[] è ora archiviato come stringa base64 anziché come matrice numerica

Problema di rilevamento n. 17306

Comportamento precedente

Le proprietà di tipo byte[] sono state archiviate come matrice numerica.

Nuovo comportamento

Le proprietà di tipo byte[] vengono ora archiviate come stringa base64.

Perché

Questa rappresentazione di byte[] è in linea con le aspettative ed è il comportamento predefinito delle principali librerie di serializzazione JSON.

Soluzioni di prevenzione

I dati esistenti archiviati come matrici di numeri continueranno a essere sottoposti a query correttamente, ma attualmente non esiste un modo supportato per modificare il comportamento di inserimento. Se questa limitazione blocca lo scenario, commentare questo problema

Azure Cosmos DB: GetPropertyName e SetPropertyName sono stati rinominati

Problema di rilevamento n. 17874

Comportamento precedente

In precedenza venivano chiamati GetPropertyName i metodi di estensione e SetPropertyName

Nuovo comportamento

L'API precedente è stata rimossa e sono stati aggiunti nuovi metodi: GetJsonPropertyName, SetJsonPropertyName

Perché

Questa modifica rimuove l'ambiguità rispetto a ciò che questi metodi stanno configurando.

Soluzioni di prevenzione

Usare la nuova API.

I generatori di valori vengono chiamati quando lo stato dell'entità viene modificato da Scollegato a Non modificato, Aggiornato o Eliminato

Problema di rilevamento n. 15289

Comportamento precedente

I generatori di valori sono stati chiamati solo quando lo stato dell'entità è stato modificato in Aggiunta.

Nuovo comportamento

I generatori di valori vengono ora chiamati quando lo stato dell'entità viene modificato da Scollegato a Non modificato, Aggiornato o Eliminato e la proprietà contiene i valori predefiniti.

Perché

Questa modifica è stata necessaria per migliorare l'esperienza con le proprietà che non sono persistenti nell'archivio dati e hanno il relativo valore generato sempre sul client.

Soluzioni di prevenzione

Per impedire che il generatore di valori venga chiamato, assegnare un valore non predefinito alla proprietà prima che lo stato venga modificato.

IMigrationsModelDiffer usa ora IRelationalModel

Problema di rilevamento n. 20305

Comportamento precedente

IMigrationsModelDiffer L'API è stata definita usando IModel.

Nuovo comportamento

IMigrationsModelDiffer L'API usa IRelationalModelora . Tuttavia, lo snapshot del modello contiene ancora solo IModel perché questo codice fa parte dell'applicazione e Entity Framework non può modificarlo senza apportare una modifica più importante.

Perché

IRelationalModel è una rappresentazione appena aggiunta dello schema del database. Usarlo per trovare le differenze è più veloce e più accurato.

Soluzioni di prevenzione

Usare il codice seguente per confrontare il modello da snapshot con il modello da 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());

Si prevede di migliorare questa esperienza nella versione 6.0 (vedere #22031)

I discriminatori sono di sola lettura

Problema di rilevamento n. 21154

Comportamento precedente

È stato possibile modificare il valore discriminatorio prima di chiamare SaveChanges

Nuovo comportamento

Nel caso precedente verrà generata un'eccezione.

Perché

Ef non prevede che il tipo di entità venga modificato mentre è ancora monitorato, quindi la modifica del valore discriminatorio lascia il contesto in uno stato incoerente, che potrebbe comportare un comportamento imprevisto.

Soluzioni di prevenzione

Se è necessario modificare il valore discriminatorio e il contesto verrà eliminato immediatamente dopo la chiamata SaveChanges, il discriminatorio può essere reso modificabile:

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

Entity Framework specifico del provider. I metodi di Funzioni generano un'eccezione per il provider InMemory

Problema di rilevamento n. 20294

Comportamento precedente

Entity Framework specifico del provider. I metodi di funzioni conteneva l'implementazione per l'esecuzione client, che consentiva l'esecuzione nel provider InMemory. Ad esempio, EF.Functions.DateDiffDay è un metodo specifico di Sql Server, che ha lavorato sul provider InMemory.

Nuovo comportamento

I metodi specifici del provider sono stati aggiornati per generare un'eccezione nel corpo del metodo per bloccare la valutazione sul lato client.

Perché

I metodi specifici del provider eseguono il mapping a una funzione di database. Il calcolo eseguito dalla funzione di database mappata non può sempre essere replicato sul lato client in LINQ. Il risultato del server può essere diverso durante l'esecuzione dello stesso metodo nel client. Poiché questi metodi vengono usati in LINQ per eseguire la conversione in funzioni di database specifiche, non è necessario valutarli sul lato client. Poiché il provider InMemory è un database diverso, questi metodi non sono disponibili per questo provider. Il tentativo di eseguirli per il provider InMemory o qualsiasi altro provider che non converte questi metodi genera un'eccezione.

Soluzioni di prevenzione

Poiché non è possibile simulare il comportamento delle funzioni di database in modo accurato, è necessario testare le query contenenti tali query sullo stesso tipo di database di produzione.

IndexBuilder.HasName è ora obsoleto

Problema di rilevamento n. 21089

Comportamento precedente

In precedenza, è possibile definire un solo indice su un determinato set di proprietà. Il nome del database di un indice è stato configurato usando IndexBuilder.HasName.

Nuovo comportamento

Sono ora consentiti più indici per lo stesso set o proprietà. Questi indici sono ora distinti in base a un nome nel modello. Per convenzione, il nome del modello viene usato come nome del database; tuttavia può anche essere configurato in modo indipendente usando HasDatabaseName.

Perché

In futuro, si vogliono abilitare indici crescente e decrescente con regole di confronto diverse sullo stesso set di proprietà. Questa modifica ci sposta in un altro passaggio in quella direzione.

Soluzioni di prevenzione

Qualsiasi codice che in precedenza chiama IndexBuilder.HasName deve essere aggiornato per chiamare invece HasDatabaseName.

Se il progetto include migrazioni generate prima di EF Core versione 2.0.0, è possibile ignorare in modo sicuro l'avviso in tali file e eliminarlo aggiungendo #pragma warning disable 612, 618.

È ora incluso un pluralizzatore per lo scaffolding di modelli inversi

Problema di rilevamento n. 11160

Comportamento precedente

In precedenza era necessario installare un pacchetto pluralizzatore separato per pluralizzare i nomi di navigazione dbSet e raccolta e singolarizzare i nomi delle tabelle quando si esegue lo scaffolding di un dbContext e dei tipi di entità mediante la reverse engineering di uno schema di database.

Nuovo comportamento

EF Core include ora un pluralizzatore che usa la libreria Humanizer . Si tratta della stessa libreria usata da Visual Studio per consigliare nomi di variabili.

Perché

L'uso di forme plurali di parole per le proprietà della raccolta e le forme singolari per i tipi e le proprietà di riferimento è idiomatico in .NET.

Soluzioni di prevenzione

Per disabilitare il pluralizzatore, usare l'opzione --no-pluralize su dotnet ef dbcontext scaffold o l'opzione -NoPluralize su Scaffold-DbContext.

INavigationBase sostituisce INavigation in alcune API per supportare gli spostamenti ignorati

Problema di rilevamento n. 2568

Comportamento precedente

EF Core prima della versione 5.0 supportava una sola forma di proprietà di navigazione, rappresentata dall'interfaccia INavigation .

Nuovo comportamento

EF Core 5.0 introduce relazioni molti-a-molti che usano "ignora spostamenti". Sono rappresentati dall'interfaccia ISkipNavigation e la maggior parte delle funzionalità di INavigation è stata inserita in un'interfaccia di base comune: INavigationBase.

Perché

La maggior parte delle funzionalità tra gli spostamenti normali e ignorati è la stessa. Tuttavia, ignorare gli spostamenti ha una relazione diversa con le chiavi esterne rispetto agli spostamenti normali, poiché gli FK coinvolti non sono direttamente in una delle due estremità della relazione, ma piuttosto nell'entità di join.

Soluzioni di prevenzione

In molti casi le applicazioni possono passare all'uso della nuova interfaccia di base senza altre modifiche. Tuttavia, nei casi in cui lo spostamento viene usato per accedere alle proprietà della chiave esterna, il codice dell'applicazione deve essere vincolato solo agli spostamenti normali o aggiornato per eseguire le operazioni appropriate sia per gli spostamenti normali che per ignorare gli spostamenti.

Alcune query con raccolta correlata che usano Distinct o GroupBy non sono più supportate

Problema di rilevamento n. 15873

Comportamento precedente

In precedenza, le query che includono raccolte correlate seguite da GroupBy, nonché alcune query che usano Distinct è consentito eseguire.

Esempio di GroupBy:

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

Distinct esempio: query in Distinct cui la proiezione della raccolta interna non contiene la chiave primaria:

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

Queste query potrebbero restituire risultati non corretti se la raccolta interna conteneva duplicati, ma funzionava correttamente se tutti gli elementi nella raccolta interna erano univoci.

Nuovo comportamento

Queste query non sono più supportate. Viene generata un'eccezione che indica che non sono disponibili informazioni sufficienti per compilare correttamente i risultati.

Perché

Per gli scenari di raccolta correlati, è necessario conoscere la chiave primaria dell'entità per assegnare entità di raccolta all'elemento padre corretto. Quando la raccolta interna non usa GroupBy o Distinct, la chiave primaria mancante può essere semplicemente aggiunta alla proiezione. Tuttavia, nel caso di GroupBy e Distinct non può essere eseguita perché cambierebbe il risultato o GroupByDistinct l'operazione.

Procedure di mitigazione

Riscrivere la query in modo da non usare GroupBy o Distinct operazioni nella raccolta interna ed eseguire invece queste operazioni sul client.

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

L'uso di una raccolta di tipi queryable nella proiezione non è supportato

Problema di rilevamento n. 16314

Comportamento precedente

In precedenza, era possibile usare la raccolta di un tipo Queryable all'interno della proiezione in alcuni casi, ad esempio come argomento di un List<T> costruttore:

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

Nuovo comportamento

Queste query non sono più supportate. Viene generata un'eccezione che indica che non è possibile creare un oggetto di tipo Queryable e suggerire come potrebbe essere corretto.

Perché

Non è possibile materializzare un oggetto di un tipo queryable, quindi verranno creati automaticamente usando List<T> il tipo. Questo causa spesso un'eccezione a causa di una mancata corrispondenza del tipo che non era molto chiara e potrebbe essere sorprendente per alcuni utenti. Abbiamo deciso di riconoscere il modello e di generare un'eccezione più significativa.

Procedure di mitigazione

Aggiungere ToList() una chiamata dopo l'oggetto Queryable nella proiezione:

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