Memutus perubahan di EF Core 5.0
PERUBAHAN API dan perilaku berikut berpotensi memutus pembaruan aplikasi yang ada ke EF Core 5.0.0.
Ringkasan
Perubahan dampak sedang
EF Core 5.0 tidak mendukung .NET Framework
Perilaku yang lama
EF Core 3.1 menargetkan .NET Standard 2.0, yang didukung oleh .NET Framework.
Perilaku yang baru
EF Core 5.0 menargetkan .NET Standard 2.1, yang tidak didukung oleh .NET Framework. Ini berarti EF Core 5.0 tidak dapat digunakan dengan aplikasi .NET Framework.
Mengapa
Ini adalah bagian dari gerakan yang lebih luas di seluruh tim .NET yang bertujuan untuk menyatukan ke satu kerangka kerja target .NET. Untuk informasi selengkapnya, lihat masa depan .NET Standard.
Mitigasi
Aplikasi .NET Framework dapat terus menggunakan EF Core 3.1, yang merupakan rilis dukungan jangka panjang (LTS). Secara bergantian, aplikasi dapat diperbarui untuk menggunakan .NET Core 3.1 atau .NET 5, yang keduanya mendukung .NET Standard 2.1.
IProperty.GetColumnName() sekarang sudah usang
Perilaku yang lama
GetColumnName()
mengembalikan nama kolom tempat properti dipetakan.
Perilaku yang baru
GetColumnName()
masih mengembalikan nama kolom tempat properti dipetakan, tetapi perilaku ini sekarang ambigu karena EF Core 5 mendukung TPT dan pemetaan simultan ke tampilan atau fungsi di mana pemetaan ini dapat menggunakan nama kolom yang berbeda untuk properti yang sama.
Mengapa
Kami menandai metode ini sebagai usang untuk memandu pengguna ke kelebihan beban yang lebih akurat - GetColumnName(IProperty, StoreObjectIdentifier).
Mitigasi
Jika jenis entitas hanya pernah dipetakan ke satu tabel, dan tidak pernah melihat, fungsi, atau beberapa tabel, GetColumnBaseName(IReadOnlyProperty) dapat digunakan dalam EF Core 5.0 dan 6.0 untuk mendapatkan nama tabel. Contohnya:
var columnName = property.GetColumnBaseName();
Dalam EF Core 7.0, ini dapat kembali diganti dengan yang baru GetColumnName
, yang berulah seperti yang aslinya untuk pemetaan tabel tunggal sederhana saja.
Jika jenis entitas dapat dipetakan ke tampilan, fungsi, atau beberapa tabel, maka StoreObjectIdentifier harus diperoleh untuk mengidentifikasi tabel, tampilan, atau fungsi. Ini kemudian dapat digunakan untuk mendapatkan nama kolom untuk objek penyimpanan tersebut. Contohnya:
var columnName = property.GetColumnName(StoreObjectIdentifier.Table("Users", null)));
Presisi dan skala diperlukan untuk desimal
Perilaku yang lama
EF Core biasanya tidak mengatur presisi dan skala pada SqlParameter objek. Ini berarti presisi dan skala penuh dikirim ke SQL Server, di mana SQL Server akan membulatkan berdasarkan presisi dan skala kolom database.
Perilaku yang baru
EF Core sekarang menetapkan presisi dan skala pada parameter menggunakan nilai yang dikonfigurasi untuk properti dalam model EF Core. Ini berarti pembulatan sekarang terjadi di SqlClient. Akibatnya, jika presisi dan skala yang dikonfigurasi tidak cocok dengan presisi dan skala database, maka pembulatan yang terlihat dapat berubah.
Mengapa
Fitur SQL Server yang lebih baru, termasuk Always Encrypted, mengharuskan aspek parameter ditentukan sepenuhnya. Selain itu, SqlClient membuat perubahan untuk membulatkan alih-alih memotong nilai desimal, sehingga cocok dengan perilaku SQL Server. Ini memungkinkan EF Core untuk mengatur faset ini tanpa mengubah perilaku untuk desimal yang dikonfigurasi dengan benar.
Mitigasi
Petakan properti desimal Anda menggunakan nama jenis yang menyertakan presisi dan skala. Contohnya:
public class Blog
{
public int Id { get; set; }
[Column(TypeName = "decimal(16, 5)")]
public decimal Score { get; set; }
}
Atau gunakan HasPrecision
dalam API pembuatan model. Contohnya:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>().Property(e => e.Score).HasPrecision(16, 5);
}
Navigasi yang diperlukan atau tidak dapat diubah ke dependen memiliki semantik yang berbeda
Perilaku yang lama
Hanya navigasi ke prinsipal yang dapat dikonfigurasi sesuai kebutuhan. Oleh karena itu, menggunakan RequiredAttribute
navigasi ke dependen (entitas yang berisi kunci asing) atau menandainya sebagai non-nullable akan membuat kunci asing pada jenis entitas yang menentukan.
Perilaku yang baru
Dengan dukungan tambahan untuk dependen yang diperlukan, sekarang dimungkinkan untuk menandai navigasi referensi apa pun sesuai kebutuhan, yang berarti bahwa dalam kasus yang ditunjukkan di atas kunci asing akan didefinisikan di sisi lain hubungan dan properti tidak akan ditandai sebagaimana diperlukan.
IsRequired
Memanggil sebelum menentukan akhir dependen sekarang ambigu:
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
.IsRequired()
.HasForeignKey<BlogImage>(b => b.BlogForeignKey);
Mengapa
Perilaku baru diperlukan untuk mengaktifkan dukungan untuk dependen yang diperlukan (lihat #12100).
Mitigasi
Hapus RequiredAttribute
dari navigasi ke dependen dan letakkan pada navigasi ke prinsipal atau konfigurasikan hubungan di OnModelCreating
:
modelBuilder.Entity<Blog>()
.HasOne(b => b.BlogImage)
.WithOne(i => i.Blog)
.HasForeignKey<BlogImage>(b => b.BlogForeignKey)
.IsRequired();
Menentukan kueri diganti dengan metode khusus penyedia
Perilaku yang lama
Jenis entitas dipetakan untuk menentukan kueri di tingkat Inti. Kapan saja jenis entitas digunakan di akar kueri jenis entitas digantikan oleh kueri yang menentukan untuk penyedia apa pun.
Perilaku yang baru
API untuk menentukan kueri tidak digunakan lagi. API khusus penyedia baru diperkenalkan.
Mengapa
Saat menentukan kueri diimplementasikan sebagai kueri pengganti setiap kali akar kueri digunakan dalam kueri, kueri memiliki beberapa masalah:
- Jika menentukan kueri memproyeksikan jenis entitas menggunakan
new { ... }
dalamSelect
metode, maka mengidentifikasi bahwa sebagai entitas memerlukan pekerjaan tambahan dan membuatnya tidak konsisten dengan bagaimana EF Core memperlakukan jenis nominal dalam kueri. - Untuk penyedia
FromSql
relasional masih diperlukan untuk meneruskan string SQL dalam formulir ekspresi LINQ.
Awalnya menentukan kueri diperkenalkan sebagai tampilan sisi klien yang akan digunakan dengan penyedia In-Memory untuk entitas tanpa kunci (mirip dengan tampilan database dalam database relasional). Definisi tersebut memudahkan pengujian aplikasi terhadap database dalam memori. Setelah itu mereka menjadi berlaku secara luas, yang berguna tetapi membawa perilaku yang tidak konsisten dan sulit dipahami. Jadi kami memutuskan untuk menyederhanakan konsep. Kami membuat kueri penentuan berbasis LINQ eksklusif untuk penyedia In-Memory dan memperlakukannya secara berbeda. Untuk informasi selengkapnya, lihat masalah ini.
Mitigasi
Untuk penyedia relasional, gunakan ToSqlQuery
metode masuk OnModelCreating
dan teruskan string SQL untuk digunakan untuk jenis entitas.
Untuk penyedia Dalam Memori, gunakan ToInMemoryQuery
metode di OnModelCreating
dan teruskan kueri LINQ untuk digunakan untuk jenis entitas.
Navigasi referensi non-null tidak ditimpa oleh kueri
Perilaku yang lama
Dalam EF Core 3.1, navigasi referensi yang dengan bersemangat diinisialisasi ke nilai non-null terkadang akan ditimpa oleh instans entitas dari database, terlepas dari apakah nilai kunci cocok atau tidak. Namun, dalam kasus lain, EF Core 3.1 akan melakukan kebalikannya dan meninggalkan nilai non-null yang ada.
Perilaku yang baru
Dimulai dengan EF Core 5.0, navigasi referensi non-null tidak pernah ditimpa oleh instans yang dikembalikan dari kueri.
Perhatikan bahwa inisialisasi bersemangat navigasi koleksi ke koleksi kosong masih didukung.
Mengapa
Inisialisasi properti navigasi referensi ke instans entitas "kosong" menghasilkan status ambigu. Contohnya:
public class Blog
{
public int Id { get; set; }
public Author Author { get; set; ) = new Author();
}
Biasanya kueri untuk Blog dan Penulis akan terlebih dahulu membuat Blog
instans lalu mengatur instans yang sesuai Author
berdasarkan data yang dikembalikan dari database. Namun, dalam hal ini setiap Blog.Author
properti sudah diinisialisasi ke kosong Author
. Kecuali EF Core tidak memiliki cara untuk mengetahui bahwa instans ini "kosong". Jadi menimpa instans ini berpotensi secara diam-diam membuang Author
. Oleh karena itu, EF Core 5.0 sekarang secara konsisten tidak menimpa navigasi yang sudah diinisialisasi.
Perilaku baru ini juga selaras dengan perilaku EF6 dalam banyak kasus, meskipun setelah diselidiki, kami juga menemukan beberapa kasus inkonsistensi di EF6.
Mitigasi
Jika jeda ini ditemui, maka perbaikannya adalah berhenti menginisialisasi properti navigasi referensi dengan bersemangat.
ToView() diperlakukan secara berbeda oleh migrasi
Perilaku yang lama
ToView(string)
Panggilan membuat migrasi mengabaikan jenis entitas selain memetakannya ke tampilan.
Perilaku yang baru
Sekarang ToView(string)
menandai jenis entitas sebagai tidak dipetakan ke tabel selain memetakannya ke tampilan. Ini menghasilkan migrasi pertama setelah meningkatkan ke EF Core 5 untuk mencoba menghilangkan tabel default untuk jenis entitas ini karena tidak lagi diabaikan.
Mengapa
EF Core sekarang memungkinkan jenis entitas untuk dipetakan ke tabel dan tampilan secara bersamaan, jadi ToView
bukan lagi indikator yang valid bahwa itu harus diabaikan oleh migrasi.
Mitigasi
Gunakan kode berikut untuk menandai tabel yang dipetakan sebagai dikecualikan dari migrasi:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().ToTable("UserView", t => t.ExcludeFromMigrations());
}
ToTable(null) menandai jenis entitas sebagai tidak dipetakan ke tabel
Perilaku yang lama
ToTable(null)
akan mengatur ulang nama tabel ke default.
Perilaku yang baru
ToTable(null)
sekarang menandai jenis entitas sebagai tidak dipetakan ke tabel apa pun.
Mengapa
EF Core sekarang memungkinkan jenis entitas untuk dipetakan ke tabel dan tampilan secara bersamaan, jadi ToTable(null)
digunakan untuk menunjukkan bahwa itu tidak dipetakan ke tabel apa pun.
Mitigasi
Gunakan kode berikut untuk mereset nama tabel ke default jika tidak dipetakan ke tampilan atau DbFunction:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().Metadata.RemoveAnnotation(RelationalAnnotationNames.TableName);
}
Perubahan berdampak rendah
Metode HasGeometricDimension yang dihapus dari ekstensi NTS SQLite
Perilaku yang lama
HasGeometricDimension digunakan untuk mengaktifkan dimensi tambahan (Z dan M) pada kolom geometri. Namun, itu hanya pernah mempengaruhi pembuatan database. Tidak perlu menentukannya untuk mengkueri nilai dengan dimensi tambahan. Ini juga tidak berfungsi dengan benar saat menyisipkan atau memperbarui nilai dengan dimensi tambahan (lihat #14257).
Perilaku yang baru
Untuk mengaktifkan menyisipkan dan memperbarui nilai geometri dengan dimensi tambahan (Z dan M), dimensi perlu ditentukan sebagai bagian dari nama jenis kolom. API ini lebih cocok dengan perilaku yang mendasar dari fungsi AddGeometryColumn SpatiaLite.
Mengapa
Menggunakan HasGeometricDimension setelah menentukan dimensi dalam jenis kolom tidak perlu dan berlebihan, jadi kami menghapus HasGeometricDimension sepenuhnya.
Mitigasi
Gunakan HasColumnType
untuk menentukan dimensi:
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: Kunci partisi sekarang ditambahkan ke kunci primer
Perilaku yang lama
Properti kunci partisi hanya ditambahkan ke kunci alternatif yang menyertakan id
.
Perilaku yang baru
Properti kunci partisi sekarang juga ditambahkan ke kunci utama menurut konvensi.
Mengapa
Perubahan ini membuat model lebih selaras dengan semantik Azure Cosmos DB dan meningkatkan performa Find
dan beberapa kueri.
Mitigasi
Untuk mencegah properti kunci partisi ditambahkan ke kunci utama, konfigurasikan di OnModelCreating
.
modelBuilder.Entity<Blog>()
.HasKey(b => b.Id);
Azure Cosmos DB: id
properti diganti namanya menjadi __id
Perilaku yang lama
Properti bayangan yang dipetakan ke id
properti JSON juga diberi nama id
.
Perilaku yang baru
Properti bayangan yang dibuat oleh konvensi sekarang bernama __id
.
Mengapa
Perubahan ini membuatnya lebih kecil kemungkinan properti id
berbenturan dengan properti yang ada pada jenis entitas.
Mitigasi
Untuk kembali ke perilaku 3.x, konfigurasikan id
properti di OnModelCreating
.
modelBuilder.Entity<Blog>()
.Property<string>("id")
.ToJsonProperty("id");
Azure Cosmos DB: byte[] sekarang disimpan sebagai string base64 alih-alih array angka
Perilaku yang lama
Properti tipe byte[] disimpan sebagai array angka.
Perilaku yang baru
Properti jenis byte[] sekarang disimpan sebagai string base64.
Mengapa
Representasi byte[] ini selaras lebih baik dengan ekspektasi dan merupakan perilaku default pustaka serialisasi JSON utama.
Mitigasi
Data yang ada disimpan sebagai array angka masih akan dikueri dengan benar, tetapi saat ini tidak ada cara yang didukung untuk mengubah kembali perilaku penyisipan. Jika batasan ini memblokir skenario Anda, komentari masalah ini
Azure Cosmos DB: GetPropertyName dan SetPropertyName diganti namanya
Perilaku yang lama
Sebelumnya metode ekstensi dipanggil GetPropertyName
dan SetPropertyName
Perilaku yang baru
API lama dihapus dan metode baru ditambahkan: GetJsonPropertyName
, SetJsonPropertyName
Mengapa
Perubahan ini menghapus ambiguitas sekeliling metode ini yang dikonfigurasi.
Mitigasi
Gunakan API baru.
Generator nilai dipanggil ketika status entitas diubah dari Dilepas menjadi Tidak Berubah, Diperbarui, atau Dihapus
Perilaku yang lama
Generator nilai hanya dipanggil ketika status entitas berubah menjadi Ditambahkan.
Perilaku yang baru
Generator nilai sekarang dipanggil ketika status entitas diubah dari Dilepas menjadi Tidak Berubah, Diperbarui, atau Dihapus dan properti berisi nilai default.
Mengapa
Perubahan ini diperlukan untuk meningkatkan pengalaman dengan properti yang tidak bertahan ke penyimpanan data dan nilainya selalu dihasilkan pada klien.
Mitigasi
Untuk mencegah generator nilai dipanggil, tetapkan nilai non-default ke properti sebelum status diubah.
IMigrationsModelDiffer sekarang menggunakan IRelationalModel
Perilaku yang lama
IMigrationsModelDiffer
API didefinisikan menggunakan IModel
.
Perilaku yang baru
IMigrationsModelDiffer
API sekarang menggunakan IRelationalModel
. Namun rekam jepret model masih hanya IModel
berisi karena kode ini adalah bagian dari aplikasi dan Kerangka Kerja Entitas tidak dapat mengubahnya tanpa membuat perubahan yang melanggar yang lebih besar.
Mengapa
IRelationalModel
adalah representasi yang baru ditambahkan dari skema database. Menggunakannya untuk menemukan perbedaan lebih cepat dan lebih akurat.
Mitigasi
Gunakan kode berikut untuk membandingkan model dari snapshot
dengan model dari 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());
Kami berencana untuk meningkatkan pengalaman ini di 6.0 (lihat #22031)
Diskriminator bersifat baca-saja
Perilaku yang lama
Dimungkinkan untuk mengubah nilai diskriminator sebelum memanggil SaveChanges
Perilaku yang baru
Pengecualian akan dilemparkan dalam kasus di atas.
Mengapa
EF tidak mengharapkan jenis entitas berubah saat masih dilacak, jadi mengubah nilai diskriminator meninggalkan konteks dalam keadaan tidak konsisten, yang mungkin mengakibatkan perilaku yang tidak terduga.
Mitigasi
Jika mengubah nilai diskriminator diperlukan dan konteks akan segera dibuang setelah memanggil SaveChanges
, diskriminator dapat dibuat dapat diubah:
modelBuilder.Entity<BaseEntity>()
.Property<string>("Discriminator")
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save);
EF khusus penyedia. Metode fungsi dilemparkan untuk penyedia InMemory
Perilaku yang lama
EF khusus penyedia. Metode Functions berisi implementasi untuk eksekusi klien, yang memungkinkan mereka untuk dijalankan pada penyedia InMemory. Misalnya, EF.Functions.DateDiffDay
adalah metode khusus Sql Server, yang bekerja pada penyedia InMemory.
Perilaku yang baru
Metode khusus penyedia telah diperbarui untuk melemparkan pengecualian dalam isi metode mereka untuk memblokir evaluasi mereka di sisi klien.
Mengapa
Metode khusus penyedia dipetakan ke fungsi database. Komputasi yang dilakukan oleh fungsi database yang dipetakan tidak selalu dapat direplikasi di sisi klien di LINQ. Ini dapat menyebabkan hasil dari server berbeda saat menjalankan metode yang sama pada klien. Karena metode ini digunakan dalam LINQ untuk diterjemahkan ke fungsi database tertentu, metode ini tidak perlu dievaluasi di sisi klien. Karena penyedia InMemory adalah database yang berbeda, metode ini tidak tersedia untuk penyedia ini. Mencoba mengeksekusinya untuk penyedia InMemory, atau penyedia lain yang tidak menerjemahkan metode ini, memberikan pengecualian.
Mitigasi
Karena tidak ada cara untuk menimpulkan perilaku fungsi database secara akurat, Anda harus menguji kueri yang berisinya terhadap jenis database yang sama seperti dalam produksi.
IndexBuilder.HasName sekarang usang
Perilaku yang lama
Sebelumnya, hanya satu indeks yang dapat didefinisikan melalui sekumpulan properti tertentu. Nama database indeks dikonfigurasi menggunakan IndexBuilder.HasName.
Perilaku yang baru
Beberapa indeks sekarang diizinkan pada set atau properti yang sama. Indeks-indeks ini sekarang dibedakan oleh nama dalam model. Menurut konvensi, nama model digunakan sebagai nama database; namun juga dapat dikonfigurasi secara independen menggunakan HasDatabaseName.
Mengapa
Di masa mendatang, kami ingin mengaktifkan indeks atau indeks naik dan turun dengan kolase yang berbeda pada kumpulan properti yang sama. Perubahan ini memindahkan kita langkah lain ke arah itu.
Mitigasi
Kode apa pun yang sebelumnya memanggil IndexBuilder.HasName harus diperbarui untuk memanggil HasDatabaseName sebagai gantinya.
Jika proyek Anda menyertakan migrasi yang dihasilkan sebelum EF Core versi 2.0.0, Anda dapat dengan aman mengabaikan peringatan dalam file-file tersebut dan menekannya dengan menambahkan #pragma warning disable 612, 618
.
Pluralizer sekarang disertakan untuk perancah model rekayasa terbalik
Perilaku yang lama
Sebelumnya, Anda harus menginstal paket pluralizer terpisah untuk menetralisasi DbSet dan mengumpulkan nama navigasi dan menyelaraskan nama tabel saat membuat perancah DbContext dan jenis entitas dengan merekayasa balik skema database.
Perilaku yang baru
EF Core sekarang menyertakan pluralizer yang menggunakan pustaka Humanizer . Ini adalah pustaka yang sama yang digunakan Visual Studio untuk merekomendasikan nama variabel.
Mengapa
Menggunakan bentuk kata jamak untuk properti koleksi dan bentuk tunggal untuk jenis dan properti referensi idiomatik di .NET.
Mitigasi
Untuk menonaktifkan pluralizer, gunakan --no-pluralize
opsi aktif dotnet ef dbcontext scaffold
atau -NoPluralize
tombol aktifkan Scaffold-DbContext
.
INavigationBase menggantikan INavigation di beberapa API untuk mendukung navigasi lewati
Perilaku yang lama
EF Core sebelum 5.0 hanya mendukung satu bentuk properti navigasi, yang INavigation
diwakili oleh antarmuka.
Perilaku yang baru
EF Core 5.0 memperkenalkan hubungan banyak ke banyak yang menggunakan "lewati navigasi". Ini diwakili oleh ISkipNavigation
antarmuka, dan sebagian besar fungsionalitas INavigation
telah didorong ke antarmuka dasar umum: INavigationBase
.
Mengapa
Sebagian besar fungsionalitas antara navigasi normal dan lewati sama. Namun, navigasi lewati memiliki hubungan yang berbeda dengan kunci asing daripada navigasi normal, karena FK yang terlibat tidak langsung di salah satu akhir hubungan, melainkan di entitas gabungan.
Mitigasi
Dalam banyak kasus, aplikasi dapat beralih menggunakan antarmuka dasar baru tanpa perubahan lain. Namun, dalam kasus di mana navigasi digunakan untuk mengakses properti kunci asing, kode aplikasi harus dibatasi hanya untuk navigasi normal, atau diperbarui untuk melakukan hal yang sesuai untuk navigasi normal dan lewati.
Beberapa kueri dengan koleksi berkorelasi yang juga menggunakan Distinct
atau GroupBy
tidak lagi didukung
Perilaku lama
Sebelumnya, kueri yang melibatkan koleksi berkorelasi diikuti oleh GroupBy
, serta beberapa kueri menggunakan Distinct
kami diizinkan untuk mengeksekusi.
Contoh GroupBy:
context.Parents
.Select(p => p.Children
.GroupBy(c => c.School)
.Select(g => g.Key))
Distinct
contoh - khususnya Distinct
kueri di mana proyeksi pengumpulan dalam tidak berisi kunci utama:
context.Parents
.Select(p => p.Children
.Select(c => c.School)
.Distinct())
Kueri ini dapat mengembalikan hasil yang salah jika koleksi dalam berisi duplikat apa pun, tetapi berfungsi dengan benar jika semua elemen dalam koleksi dalam unik.
Perilaku baru
Kueri ini tidak lagi didukung. Pengecualian dilemparkan menunjukkan bahwa kami tidak memiliki informasi yang cukup untuk membangun hasil dengan benar.
Mengapa
Untuk skenario pengumpulan yang berkorelasi, kita perlu mengetahui kunci utama entitas untuk menetapkan entitas pengumpulan ke induk yang benar. Ketika koleksi dalam tidak menggunakan GroupBy
atau Distinct
, kunci primer yang hilang hanya dapat ditambahkan ke proyeksi. Namun dalam kasus GroupBy
dan Distinct
tidak dapat dilakukan karena akan mengubah hasil GroupBy
atau Distinct
operasi.
Mitigasi
Tulis ulang kueri untuk tidak menggunakan GroupBy
atau Distinct
operasi pada koleksi dalam, dan lakukan operasi ini pada klien sebagai gantinya.
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())
Menggunakan kumpulan jenis Yang Dapat Dikueri dalam proyeksi tidak didukung
Perilaku lama
Sebelumnya, dimungkinkan untuk menggunakan kumpulan jenis Yang Dapat Dikueri di dalam proyeksi dalam beberapa kasus, misalnya sebagai argumen ke List<T>
konstruktor:
context.Blogs
.Select(b => new List<Post>(context.Posts.Where(p => p.BlogId == b.Id)))
Perilaku baru
Kueri ini tidak lagi didukung. Pengecualian dilemparkan menunjukkan bahwa kita tidak dapat membuat objek jenis Yang Dapat Dikueri dan menyarankan bagaimana hal ini dapat diperbaiki.
Mengapa
Kami tidak dapat mewujudkan objek dari jenis Yang Dapat Dikueri, sehingga objek tersebut akan dibuat secara otomatis menggunakan List<T>
jenis sebagai gantinya. Ini sering menyebabkan pengecualian karena ketidakcocokan jenis yang tidak terlalu jelas dan bisa mengejutkan beberapa pengguna. Kami memutuskan untuk mengenali pola dan melemparkan pengecualian yang lebih bermakna.
Mitigasi
Tambahkan ToList()
panggilan setelah objek Yang Dapat Dikueri dalam proyeksi:
context.Blogs.Select(b => context.Posts.Where(p => p.BlogId == b.Id).ToList())