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
Modifiche a impatto medio
EF Core 5.0 non supporta .NET Framework
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 { ... }
nelSelect
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 IRelationalModel
ora . 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 GroupBy
Distinct
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())