Bagikan melalui


Perubahan mendasar di EF Core 6.0

PERUBAHAN API dan perilaku berikut berpotensi memutus pembaruan aplikasi yang ada ke EF Core 6.0.

Kerangka Target

EF Core 6.0 menargetkan .NET 6. Aplikasi yang menargetkan versi .NET, .NET Core, dan .NET Framework yang lebih lama perlu menargetkan .NET 6 untuk menggunakan EF Core 6.0.

Ringkasan

Perubahan mencolok Dampak
Dependen opsional berlapis berbagi tabel dan tanpa properti yang diperlukan tidak dapat disimpan Sangat Penting
Mengubah pemilik entitas yang dimiliki sekarang melemparkan pengecualian Medium
Azure Cosmos DB: Jenis entitas terkait ditemukan sebagai milik Medium
SQLite: Koneksi dikumpulkan Medium
Hubungan banyak ke banyak tanpa entitas gabungan yang dipetakan sekarang dibuat perancah Medium
Membersihkan pemetaan antara nilai DeleteBehavior dan ON DELETE Kurang Penting
Database dalam memori memvalidasi properti yang diperlukan tidak berisi null Kurang Penting
Menghapus ORDER BY terakhir saat bergabung untuk koleksi Kurang Penting
DbSet tidak lagi mengimplementasikan IAsyncEnumerable Kurang Penting
Jenis entitas pengembalian TVF juga dipetakan ke tabel secara default Kurang Penting
Periksa keunikan nama batasan sekarang divalidasi Kurang Penting
Menambahkan antarmuka Metadata IReadOnly dan metode ekstensi yang dihapus Kurang Penting
IExecutionStrategy sekarang menjadi layanan singleton Kurang Penting
SQL Server: Lebih banyak kesalahan dianggap sementara Kurang Penting
Azure Cosmos DB: Lebih banyak karakter lolos dalam nilai 'id' Kurang Penting
Beberapa layanan Singleton sekarang Terlingkup Rendah*
API penembolokan baru untuk ekstensi yang menambahkan atau mengganti layanan Rendah*
Prosedur inisialisasi model rekam jepret dan waktu desain baru Kurang Penting
OwnedNavigationBuilder.HasIndex mengembalikan jenis yang berbeda sekarang Kurang Penting
DbFunctionBuilder.HasSchema(null) Mengabaikan [DbFunction(Schema = "schema")] Kurang Penting
Navigasi yang telah diinisialisasi sebelumnya ditimpa oleh nilai dari kueri database Kurang Penting
Nilai string enum yang tidak diketahui dalam database tidak dikonversi ke default enum saat dikueri Kurang Penting
DbFunctionBuilder.HasTranslation sekarang menyediakan argumen fungsi sebagai IReadOnlyList daripada IReadOnlyCollection Kurang Penting
Pemetaan tabel default tidak dihapus saat entitas dipetakan ke fungsi bernilai tabel Kurang Penting
dotnet-ef menargetkan .NET 6 Kurang Penting
IModelCacheKeyFactory implementasi mungkin perlu diperbarui untuk menangani penembolokan waktu desain Kurang Penting
NavigationBaseIncludeIgnored sekarang merupakan kesalahan secara default Kurang Penting

* Perubahan ini sangat menarik bagi penulis penyedia database dan ekstensi.

Perubahan berdampak tinggi

Dependen opsional berlapis berbagi tabel dan tanpa properti yang diperlukan tidak diizinkan

Masalah Pelacakan #24558

Perilaku yang lama

Model dengan dependen opsional berlapis yang berbagi tabel dan tanpa properti yang diperlukan diizinkan, tetapi dapat mengakibatkan kehilangan data saat mengkueri data lalu menyimpan lagi. Misalnya, pertimbangkan model berikut:

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ContactInfo ContactInfo { get; set; }
}

[Owned]
public class ContactInfo
{
    public string Phone { get; set; }
    public Address Address { get; set; }
}

[Owned]
public class Address
{
    public string House { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string Postcode { get; set; }
}

Tidak ada properti dalam ContactInfo atau Address yang diperlukan, dan semua jenis entitas ini dipetakan ke tabel yang sama. Aturan untuk dependen opsional (dibandingkan dengan dependen yang diperlukan) mengatakan bahwa jika semua kolom untuk ContactInfo null, maka tidak ada instans ContactInfo yang akan dibuat saat mengkueri pemilik Customer. Namun, ini juga berarti bahwa tidak ada instans Address yang akan dibuat, bahkan jika Address kolom non-null.

Perilaku yang baru

Mencoba menggunakan model ini sekarang akan melemparkan pengecualian berikut:

System.InvalidOperationException: Jenis entitas 'ContactInfo' adalah dependen opsional menggunakan berbagi tabel dan berisi dependen lain tanpa properti non bersama yang diperlukan untuk mengidentifikasi apakah entitas ada. Jika semua properti nullable berisi nilai null dalam database, maka instans objek tidak akan dibuat dalam kueri yang menyebabkan nilai dependen berlapis hilang. Tambahkan properti yang diperlukan untuk membuat instans dengan nilai null untuk properti lain atau tandai navigasi masuk sebagaimana diperlukan untuk selalu membuat instans.

Ini mencegah kehilangan data saat mengkueri dan menyimpan data.

Mengapa

Menggunakan model dengan dependen opsional berlapis yang berbagi tabel dan tanpa properti yang diperlukan sering mengakibatkan kehilangan data senyap.

Mitigasi

Hindari menggunakan dependen opsional yang berbagi tabel dan tanpa properti yang diperlukan. Ada tiga cara mudah untuk melakukan ini:

  1. Buat dependen diperlukan. Ini berarti bahwa entitas dependen akan selalu memiliki nilai setelah dikueri, bahkan jika semua propertinya null. Contohnya:

    public class Customer
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        [Required]
        public Address Address { get; set; }
    }
    

    Atau:

    modelBuilder.Entity<Customer>(
        b =>
            {
                b.OwnsOne(e => e.Address);
                b.Navigation(e => e.Address).IsRequired();
            });
    
  2. Pastikan dependen berisi setidaknya satu properti yang diperlukan.

  3. Petakan dependen opsional ke tabel mereka sendiri, alih-alih berbagi tabel dengan prinsipal. Contohnya:

    modelBuilder.Entity<Customer>(
        b =>
            {
                b.ToTable("Customers");
                b.OwnsOne(e => e.Address, b => b.ToTable("CustomerAddresses"));
            });
    

Masalah dengan dependen opsional dan contoh mitigasi ini disertakan dalam dokumentasi untuk Apa yang baru dalam EF Core 6.0.

Perubahan dampak sedang

Mengubah pemilik entitas yang dimiliki sekarang melemparkan pengecualian

Masalah Pelacakan #4073

Perilaku yang lama

Dimungkinkan untuk menetapkan ulang entitas yang dimiliki ke entitas pemilik yang berbeda.

Perilaku yang baru

Tindakan ini sekarang akan melemparkan pengecualian:

Properti '{entityType}. {property}' adalah bagian dari kunci sehingga tidak dapat dimodifikasi atau ditandai sebagai dimodifikasi. Untuk mengubah prinsipal entitas yang ada dengan kunci asing yang mengidentifikasi, pertama-tama hapus dependen dan panggil 'SaveChanges', lalu kaitkan dependen dengan prinsipal baru.

Mengapa

Meskipun kita tidak memerlukan properti kunci untuk ada pada jenis yang dimiliki, EF masih akan membuat properti bayangan untuk digunakan sebagai kunci utama dan kunci asing yang menunjuk ke pemilik. Ketika entitas pemilik diubah, hal ini menyebabkan nilai kunci asing pada entitas yang dimiliki berubah, dan karena entitas tersebut juga digunakan sebagai kunci utama, hal ini mengakibatkan identitas entitas berubah. Ini belum sepenuhnya didukung di EF Core dan hanya diizinkan secara kondisional untuk entitas yang dimiliki, terkadang mengakibatkan status internal menjadi tidak konsisten.

Mitigasi

Alih-alih menetapkan instans yang dimiliki yang sama ke pemilik baru, Anda dapat menetapkan salinan dan menghapus yang lama.

Masalah Pelacakan #24803Apa yang baru: Default ke kepemilikan implisit

Perilaku yang lama

Seperti di penyedia lain, jenis entitas terkait ditemukan sebagai jenis normal (tidak dimiliki).

Perilaku yang baru

Jenis entitas terkait sekarang akan dimiliki oleh jenis entitas tempat mereka ditemukan. Hanya jenis entitas yang sesuai dengan properti yang DbSet<TEntity> akan ditemukan sebagai tidak dimiliki.

Mengapa

Perilaku ini mengikuti pola umum pemodelan data di Azure Cosmos DB menyematkan data terkait ke dalam satu dokumen. Azure Cosmos DB tidak secara asli mendukung bergabung dengan dokumen yang berbeda, sehingga pemodelan entitas terkait karena yang tidak dimiliki memiliki kegunaan terbatas.

Mitigasi

Untuk mengonfigurasi jenis entitas agar menjadi panggilan yang tidak dimiliki modelBuilder.Entity<MyEntity>();

SQLite: Koneksi dikumpulkan

Masalah Pelacakan #13837Apa yang baru: Default ke kepemilikan implisit

Perilaku yang lama

Sebelumnya, koneksi di Microsoft.Data.Sqlite tidak dikumpulkan.

Perilaku yang baru

Mulai dari 6.0, koneksi sekarang dikumpulkan secara default. Ini menghasilkan file database tetap terbuka oleh proses bahkan setelah objek koneksi ADO.NET ditutup.

Mengapa

Mengumpulkan koneksi yang mendasar sangat meningkatkan performa membuka dan menutup objek koneksi ADO.NET. Ini sangat terlihat untuk skenario di mana membuka koneksi yang mendasarinya mahal seperti dalam kasus enkripsi, atau dalam skenario di mana ada banyak koneksi berumur pendek ke database.

Mitigasi

Pengumpulan koneksi dapat dinonaktifkan dengan menambahkan Pooling=False ke string koneksi.

Beberapa skenario (seperti menghapus file database) sekarang mungkin mengalami kesalahan yang menyatakan bahwa file masih digunakan. Anda dapat menghapus kumpulan koneksi secara manual sebelum melakukan operasi file menggunakan SqliteConnection.ClearPool().

SqliteConnection.ClearPool(connection);
File.Delete(databaseFile);

Hubungan banyak ke banyak tanpa entitas gabungan yang dipetakan sekarang dibuat perancah

Masalah Pelacakan #22475

Perilaku yang lama

Perancah (rekayasa terbalik) DbContext jenis entitas dan dari database yang ada selalu dipetakan secara eksplisit menggabungkan jenis entitas untuk hubungan banyak-ke-banyak.

Perilaku yang baru

Tabel gabungan sederhana yang hanya berisi dua properti kunci asing ke tabel lain tidak lagi dipetakan ke jenis entitas eksplisit, tetapi sebaliknya dipetakan sebagai hubungan banyak-ke-banyak antara dua tabel yang bergabung.

Mengapa

Hubungan banyak ke banyak tanpa jenis gabungan eksplisit diperkenalkan dalam EF Core 5.0 dan merupakan cara yang lebih bersih dan lebih alami untuk mewakili tabel gabungan sederhana.

Mitigasi

Ada dua mitigasi. Pendekatan yang disukai adalah memperbarui kode untuk menggunakan hubungan banyak ke banyak secara langsung. Sangat jarang bahwa jenis entitas gabungan perlu digunakan secara langsung ketika hanya berisi dua kunci asing untuk hubungan banyak-ke-banyak.

Secara bergantian, entitas gabungan eksplisit dapat ditambahkan kembali ke model EF. Misalnya, dengan asumsi hubungan banyak ke banyak antara Post dan Tag, tambahkan kembali jenis gabungan dan navigasi menggunakan kelas parsial:

public partial class PostTag
{
    public int PostsId { get; set; }
    public int TagsId { get; set; }

    public virtual Post Posts { get; set; }
    public virtual Tag Tags { get; set; }
}

public partial class Post
{
    public virtual ICollection<PostTag> PostTags { get; set; }
}

public partial class Tag
{
    public virtual ICollection<PostTag> PostTags { get; set; }
}

Kemudian tambahkan konfigurasi untuk jenis gabungan dan navigasi ke kelas parsial untuk DbContext:

public partial class DailyContext
{
    partial void OnModelCreatingPartial(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Post>(entity =>
        {
            entity.HasMany(d => d.Tags)
                .WithMany(p => p.Posts)
                .UsingEntity<PostTag>(
                    l => l.HasOne<Tag>(e => e.Tags).WithMany(e => e.PostTags).HasForeignKey(e => e.TagsId),
                    r => r.HasOne<Post>(e => e.Posts).WithMany(e => e.PostTags).HasForeignKey(e => e.PostsId),
                    j =>
                    {
                        j.HasKey("PostsId", "TagsId");
                        j.ToTable("PostTag");
                    });
        });
    }
}

Terakhir, hapus konfigurasi yang dihasilkan untuk hubungan banyak ke banyak dari konteks perancah. Ini diperlukan karena jenis entitas gabungan perancah harus dihapus dari model sebelum jenis eksplisit dapat digunakan. Kode ini perlu dihapus setiap kali konteks di-scaffold, tetapi karena kode di atas berada di kelas parsial, konteks akan bertahan.

Perhatikan bahwa dengan konfigurasi ini, entitas gabungan dapat digunakan secara eksplisit, seperti pada versi EF Core sebelumnya. Namun, hubungan ini juga dapat digunakan sebagai hubungan banyak ke banyak. Ini berarti bahwa memperbarui kode seperti ini dapat menjadi solusi sementara sementara sisa kode diperbarui untuk menggunakan hubungan sebagai banyak-ke-banyak dengan cara alami.

Perubahan berdampak rendah

Membersihkan pemetaan antara nilai DeleteBehavior dan ON DELETE

Masalah Pelacakan #21252

Perilaku yang lama

Beberapa pemetaan antara perilaku hubungan OnDelete() dan perilaku kunci ON DELETE asing dalam database tidak konsisten dalam Migrasi dan Perancah.

Perilaku yang baru

Tabel berikut mengilustrasikan perubahan untuk Migrasi.

OnDelete() SAAT DIHAPUS
NoAction TIDAK ADA TINDAKAN
ClientNoAction TIDAK ADA TINDAKAN
Membatasi BATASI
Cascade CASCADE
ClientCascade BATASI TIDAK ADA TINDAKAN
SetNull SET NULL
ClientSetNull BATASI TIDAK ADA TINDAKAN

Perubahan untuk Perancah adalah sebagai berikut.

SAAT DIHAPUS OnDelete()
TIDAK ADA TINDAKAN ClientSetNull
BATASI ClientSetNull Restrict
CASCADE Cascade
SET NULL SetNull

Mengapa

Pemetaan baru lebih konsisten. Perilaku database default NO ACTION sekarang lebih disukai daripada perilaku RESTRICT yang lebih ketat dan kurang berkinerja.

Mitigasi

Perilaku OnDelete() default dari hubungan opsional adalah ClientSetNull. Pemetaannya telah berubah dari RESTRICT ke NO ACTION. Ini dapat menyebabkan banyak operasi dihasilkan dalam migrasi pertama Anda ditambahkan setelah meningkatkan ke EF Core 6.0.

Anda dapat memilih untuk menerapkan operasi ini atau menghapusnya secara manual dari migrasi karena tidak memiliki dampak fungsional pada EF Core.

SQL Server tidak mendukung RESTRICT, sehingga kunci asing ini sudah dibuat menggunakan NO ACTION. Operasi migrasi tidak akan berpengaruh pada SQL Server dan aman untuk dihapus.

Database dalam memori memvalidasi properti yang diperlukan tidak berisi null

Masalah Pelacakan #10613

Perilaku yang lama

Database dalam memori diizinkan menyimpan nilai null bahkan ketika properti dikonfigurasi sesuai kebutuhan.

Perilaku yang baru

Database dalam memori melempar Microsoft.EntityFrameworkCore.DbUpdateException kapan SaveChanges atau SaveChangesAsync dipanggil dan properti yang diperlukan diatur ke null.

Mengapa

Perilaku database dalam memori sekarang cocok dengan perilaku database lain.

Mitigasi

Perilaku sebelumnya (yaitu tidak memeriksa nilai null) dapat dipulihkan saat mengonfigurasi penyedia dalam memori. Contohnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseInMemoryDatabase("MyDatabase", b => b.EnableNullChecks(false));
}

Menghapus ORDER BY terakhir saat bergabung untuk koleksi

Masalah Pelacakan #19828

Perilaku yang lama

Saat melakukan SQL JOIN pada koleksi (hubungan satu-ke-banyak), EF Core digunakan untuk menambahkan ORDER BY untuk setiap kolom kunci tabel yang digabungkan. Misalnya, memuat semua Blog dengan Posting terkait mereka dilakukan melalui SQL berikut:

SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId], [p].[PostId]

Pesanan ini diperlukan untuk materialisasi entitas yang tepat.

Perilaku yang baru

ORDER BY terakhir untuk gabungan koleksi sekarang dihilangkan:

SELECT [b].[BlogId], [b].[Name], [p].[PostId], [p].[BlogId], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]

ORDER BY untuk kolom ID Postingan tidak lagi dihasilkan.

Mengapa

Setiap ORDER BY memberlakukan pekerjaan tambahan di sisi database, dan pengurutan terakhir tidak diperlukan untuk kebutuhan materialisasi EF Core. Data menunjukkan bahwa menghapus urutan terakhir ini dapat menghasilkan peningkatan performa yang signifikan dalam beberapa skenario.

Mitigasi

Jika aplikasi Anda mengharapkan entitas yang bergabung dikembalikan dalam urutan tertentu, buat itu eksplisit dengan menambahkan operator LINQ OrderBy ke kueri Anda.

DbSet tidak lagi mengimplementasikan IAsyncEnumerable

Masalah Pelacakan #24041

Perilaku yang lama

DbSet<TEntity>, yang digunakan untuk menjalankan kueri pada DbContext, digunakan untuk mengimplementasikan IAsyncEnumerable<T>.

Perilaku yang baru

DbSet<TEntity> tidak lagi secara langsung mengimplementasikan IAsyncEnumerable<T>.

Mengapa

DbSet<TEntity> awalnya dibuat untuk mengimplementasikan IAsyncEnumerable<T> terutama untuk memungkinkan enumerasi langsung di atasnya melalui foreach konstruksi. Sayangnya, ketika proyek juga mereferensikan System.Linq.Async untuk menyusun sisi klien operator LINQ asinkron, ini mengakibatkan kesalahan pemanggilan ambigu antara operator yang ditentukan dan IQueryable<T> yang ditentukan atas IAsyncEnumerable<T>. C# 9 menambahkan dukungan ekstensi GetEnumerator untuk foreach perulangan, menghapus alasan utama asli untuk mereferensikan IAsyncEnumerable.

Sebagian besar DbSet penggunaan akan terus berfungsi apa adanya, karena mereka menyusun operator LINQ di atas DbSet, menghitungnya, dll. Satu-satunya penggunaan yang rusak adalah yang mencoba untuk melemparkan DbSet langsung ke IAsyncEnumerable.

Mitigasi

Jika Anda perlu merujuk sebagai DbSet<TEntity> IAsyncEnumerable<T>, panggil DbSet<TEntity>.AsAsyncEnumerable untuk secara eksplisit melemparkannya.

Jenis entitas pengembalian TVF juga dipetakan ke tabel secara default

Masalah Pelacakan #23408

Perilaku yang lama

Jenis entitas tidak dipetakan ke tabel secara default saat digunakan sebagai jenis pengembalian TVF yang dikonfigurasi dengan HasDbFunction.

Perilaku yang baru

Jenis entitas yang digunakan sebagai jenis pengembalian TVF mempertahankan pemetaan tabel default.

Mengapa

Tidak intuitif bahwa mengonfigurasi TVF menghapus pemetaan tabel default untuk jenis entitas pengembalian.

Mitigasi

Untuk menghapus pemetaan tabel default, panggil ToTable(EntityTypeBuilder, String):

modelBuilder.Entity<MyEntity>().ToTable((string?)null));

Periksa keunikan nama batasan sekarang divalidasi

Masalah Pelacakan #25061

Perilaku yang lama

Periksa batasan dengan nama yang sama diizinkan untuk dideklarasikan dan digunakan pada tabel yang sama.

Perilaku yang baru

Mengonfigurasi dua batasan pemeriksaan secara eksplisit dengan nama yang sama pada tabel yang sama sekarang akan menghasilkan pengecualian. Periksa batasan yang dibuat oleh konvensi akan diberi nama unik.

Mengapa

Sebagian besar database tidak mengizinkan dua batasan pemeriksaan dengan nama yang sama dibuat pada tabel yang sama, dan beberapa mengharuskannya unik bahkan di seluruh tabel. Ini akan mengakibatkan pengecualian dilemparkan saat menerapkan migrasi.

Mitigasi

Dalam beberapa kasus, nama batasan pemeriksaan yang valid mungkin berbeda karena perubahan ini. Untuk menentukan nama yang diinginkan secara eksplisit, panggil HasName:

modelBuilder.Entity<MyEntity>().HasCheckConstraint("CK_Id", "Id > 0", c => c.HasName("CK_MyEntity_Id"));

Menambahkan antarmuka Metadata IReadOnly dan metode ekstensi yang dihapus

Masalah Pelacakan #19213

Perilaku yang lama

Ada tiga set antarmuka metadata: IModel, IMutableModel dan IConventionModel serta metode ekstensi.

Perilaku yang baru

Sekumpulan IReadOnly antarmuka baru telah ditambahkan, misalnya IReadOnlyModel. Metode ekstensi yang sebelumnya ditentukan untuk antarmuka metadata telah dikonversi ke metode antarmuka default.

Mengapa

Metode antarmuka default memungkinkan implementasi ditimpa, ini dimanfaatkan oleh implementasi model run-time baru untuk menawarkan performa yang lebih baik.

Mitigasi

Perubahan ini seharusnya tidak memengaruhi sebagian besar kode. Namun, jika Anda menggunakan metode ekstensi melalui sintaks pemanggilan statis, itu perlu dikonversi ke sintaks pemanggilan instans.

IExecutionStrategy sekarang menjadi layanan singleton

Masalah Pelacakan #21350

Perilaku yang baru

IExecutionStrategy sekarang menjadi layanan singleton. Ini berarti bahwa setiap status tambahan dalam implementasi kustom akan tetap berada di antara eksekusi dan delegasi yang diteruskan ke ExecutionStrategy hanya akan dijalankan sekali.

Mengapa

Pengurangan alokasi ini pada dua jalur panas di EF.

Mitigasi

Implementasi yang berasal dari ExecutionStrategy harus menghapus status apa pun di OnFirstExecution().

Logika kondisional dalam delegasi yang diteruskan ke ExecutionStrategy harus dipindahkan ke implementasi IExecutionStrategykustom .

SQL Server: Lebih banyak kesalahan dianggap sementara

Masalah Pelacakan #25050

Perilaku yang baru

Kesalahan yang tercantum dalam masalah di atas sekarang dianggap sementara. Saat menggunakan strategi eksekusi default (non-coba lagi), kesalahan ini sekarang akan dibungkus dalam instans pengecualian tambahan.

Mengapa

Kami terus mengumpulkan umpan balik dari pengguna dan tim SQL Server di mana kesalahan harus dianggap sementara.

Mitigasi

Untuk mengubah serangkaian kesalahan yang dianggap sementara, gunakan strategi eksekusi kustom yang dapat berasal dari SqlServerRetryingExecutionStrategy - Ketahanan Koneksi - EF Core.

Azure Cosmos DB: Lebih banyak karakter lolos dalam nilai 'id'

Masalah Pelacakan #25100

Perilaku yang lama

Di EF Core 5, hanya '|' lolos dalam id nilai.

Perilaku yang baru

Di EF Core 6, '/', '\', '?' dan '#' juga lolos dalam id nilai.

Mengapa

Karakter ini tidak valid, seperti yang didokumenkan dalam Resource.Id. Menggunakannya di id akan menyebabkan kueri gagal.

Mitigasi

Anda dapat mengambil alih nilai yang dihasilkan dengan mengaturnya sebelum entitas ditandai sebagai Added:

var entry = context.Attach(entity);
entry.Property("__id").CurrentValue = "MyEntity|/\\?#";
entry.State = EntityState.Added;

Beberapa layanan Singleton sekarang Terlingkup

Masalah Pelacakan #25084

Perilaku yang baru

Banyak layanan kueri dan beberapa layanan waktu desain yang terdaftar seperti Singleton yang sekarang terdaftar sebagai Scoped.

Mengapa

Masa pakai harus diubah untuk memungkinkan fitur baru - untuk DefaultTypeMapping memengaruhi kueri.

Masa pakai layanan waktu desain telah disesuaikan agar sesuai dengan masa pakai layanan run-time untuk menghindari kesalahan saat menggunakan keduanya.

Mitigasi

Gunakan TryAdd untuk mendaftarkan layanan EF Core menggunakan masa pakai default. Hanya gunakan TryAddProviderSpecificServices untuk layanan yang tidak ditambahkan oleh EF.

API penembolokan baru untuk ekstensi yang menambahkan atau mengganti layanan

Masalah Pelacakan #19152

Perilaku yang lama

Di EF Core 5, GetServiceProviderHashCode dikembalikan long dan digunakan langsung sebagai bagian dari kunci cache untuk penyedia layanan.

Perilaku yang baru

GetServiceProviderHashCode sekarang mengembalikan int dan hanya digunakan untuk menghitung kode hash kunci cache untuk penyedia layanan.

Selain itu, ShouldUseSameServiceProvider perlu diimplementasikan untuk menunjukkan apakah objek saat ini mewakili konfigurasi layanan yang sama dan dengan demikian dapat menggunakan penyedia layanan yang sama.

Mengapa

Hanya menggunakan kode hash sebagai bagian dari kunci cache mengakibatkan tabrakan sesekali yang sulit didiagnosis dan diperbaiki. Metode tambahan memastikan bahwa penyedia layanan yang sama hanya digunakan jika sesuai.

Mitigasi

Banyak ekstensi tidak mengekspos opsi apa pun yang memengaruhi layanan terdaftar dan dapat menggunakan implementasi berikut dari ShouldUseSameServiceProvider:

private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
{
    public ExtensionInfo(IDbContextOptionsExtension extension)
        : base(extension)
    {
    }

    ...

    public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
        => other is ExtensionInfo;
}

Jika tidak, predikat tambahan harus ditambahkan untuk membandingkan semua opsi yang relevan.

Prosedur inisialisasi model rekam jepret dan waktu desain baru

Masalah Pelacakan #22031

Perilaku yang lama

Dalam EF Core 5, konvensi tertentu perlu dipanggil sebelum model rekam jepret siap digunakan.

Perilaku yang baru

IModelRuntimeInitializer diperkenalkan untuk menyembunyikan beberapa langkah yang diperlukan, dan model run-time diperkenalkan yang tidak memiliki semua metadata migrasi, sehingga model waktu desain harus digunakan untuk perbedaan model.

Mengapa

IModelRuntimeInitializer mengabstraksi langkah-langkah finalisasi model, sehingga ini sekarang dapat diubah tanpa melanggar perubahan lebih lanjut bagi pengguna.

Model run-time yang dioptimalkan diperkenalkan untuk meningkatkan performa run-time. Ini memiliki beberapa pengoptimalan, salah satunya adalah menghapus metadata yang tidak digunakan pada run-time.

Mitigasi

Cuplikan berikut menggambarkan cara memeriksa apakah model saat ini berbeda dari model rekam jepret:

var snapshotModel = migrationsAssembly.ModelSnapshot?.Model;

if (snapshotModel is IMutableModel mutableModel)
{
    snapshotModel = mutableModel.FinalizeModel();
}

if (snapshotModel != null)
{
    snapshotModel = context.GetService<IModelRuntimeInitializer>().Initialize(snapshotModel);
}

var hasDifferences = context.GetService<IMigrationsModelDiffer>().HasDifferences(
    snapshotModel?.GetRelationalModel(),
    context.GetService<IDesignTimeModel>().Model.GetRelationalModel());

Cuplikan ini menunjukkan cara menerapkan IDesignTimeDbContextFactory<TContext> dengan membuat model secara eksternal dan memanggil UseModel:

internal class MyDesignContext : IDesignTimeDbContextFactory<MyContext>
{
    public TestContext CreateDbContext(string[] args)
    {
        var optionsBuilder = new DbContextOptionsBuilder();
        optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DB"));

        var modelBuilder = SqlServerConventionSetBuilder.CreateModelBuilder();
        CustomizeModel(modelBuilder);
        var model = modelBuilder.Model.FinalizeModel();

        var serviceContext = new MyContext(optionsBuilder.Options);
        model = serviceContext.GetService<IModelRuntimeInitializer>().Initialize(model);
        return new MyContext(optionsBuilder.Options);
    }
}

OwnedNavigationBuilder.HasIndex mengembalikan jenis yang berbeda sekarang

Masalah Pelacakan #24005

Perilaku yang lama

Di EF Core 5, HasIndex dikembalikan IndexBuilder<TEntity> di mana TEntity adalah jenis pemilik.

Perilaku yang baru

HasIndex sekarang mengembalikan IndexBuilder<TDependentEntity>, di mana TDependentEntity adalah jenis yang dimiliki.

Mengapa

Objek penyusun yang dikembalikan tidak di ketik dengan benar.

Mitigasi

Kompilasi ulang rakitan Anda terhadap versi terbaru EF Core akan cukup untuk memperbaiki masalah apa pun yang disebabkan oleh perubahan ini.

DbFunctionBuilder.HasSchema(null) Mengabaikan [DbFunction(Schema = "schema")]

Masalah Pelacakan #24228

Perilaku yang lama

Di EF Core 5, panggilan HasSchema dengan null nilai tidak menyimpan sumber konfigurasi, sehingga DbFunctionAttribute dapat mengambil alihnya.

Perilaku yang baru

Memanggil HasSchema dengan null nilai sekarang menyimpan sumber konfigurasi dan mencegah atribut menimpanya.

Mengapa

Konfigurasi yang ditentukan dengan ModelBuilder API tidak boleh diambil alih oleh anotasi data.

Mitigasi

HasSchema Hapus panggilan untuk membiarkan atribut mengonfigurasi skema.

Navigasi yang telah diinisialisasi sebelumnya ditimpa oleh nilai dari kueri database

Masalah Pelacakan #23851

Perilaku yang lama

Properti navigasi yang diatur ke objek kosong dibiarkan tidak berubah untuk kueri pelacakan, tetapi ditimpa untuk kueri non-pelacakan. Misalnya, pertimbangkan jenis entitas berikut:

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

    public Bar Bar { get; set; } = new(); // Don't do this.
}

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

Kueri tanpa pelacakan untuk Foo menyertakan Bar diatur Foo.Bar ke entitas yang dikueri dari database. Misalnya, kode ini:

var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");

Dicetak Foo.Bar.Id = 1.

Namun, kueri yang sama yang dijalankan untuk pelacakan tidak menimpa Foo.Bar entitas yang dikueri dari database. Misalnya, kode ini:

var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");

Dicetak Foo.Bar.Id = 0.

Perilaku yang baru

Di EF Core 6.0, perilaku kueri pelacakan sekarang cocok dengan kueri tanpa pelacakan. Ini berarti bahwa kedua kode ini:

var foo = context.Foos.AsNoTracking().Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");

Dan kode ini:

var foo = context.Foos.Include(e => e.Bar).Single();
Console.WriteLine($"Foo.Bar.Id = {foo.Bar.Id}");

Cetak Foo.Bar.Id = 1.

Mengapa

Ada dua alasan untuk membuat perubahan ini:

  1. Untuk memastikan bahwa kueri pelacakan dan tanpa pelacakan memiliki perilaku yang konsisten.
  2. Ketika database dikueri, masuk akal untuk mengasumsikan bahwa kode aplikasi ingin mendapatkan kembali nilai yang disimpan dalam database.

Mitigasi

Ada dua mitigasi:

  1. Jangan mengkueri objek dari database yang seharusnya tidak disertakan dalam hasil. Misalnya, dalam cuplikan kode di atas, jangan Include Foo.Bar jika Bar instans tidak boleh dikembalikan dari database dan disertakan dalam hasilnya.
  2. Atur nilai navigasi setelah mengkueri dari database. Misalnya, dalam cuplikan kode di atas, panggil foo.Bar = new() setelah menjalankan kueri.

Selain itu, pertimbangkan untuk tidak menginisialisasi instans entitas terkait ke objek default. Ini menyiratkan bahwa instans terkait adalah entitas baru, tidak disimpan ke database, tanpa set nilai kunci. Jika sebagai gantinya entitas terkait memang ada dalam database, maka data dalam kode pada dasarnya berselisih dengan data yang disimpan dalam database.

Nilai string enum yang tidak diketahui dalam database tidak dikonversi ke default enum saat dikueri

Masalah Pelacakan #24084

Perilaku yang lama

Properti Enum dapat dipetakan ke kolom string dalam database menggunakan HasConversion<string>() atau EnumToStringConverter. Ini menghasilkan EF Core yang mengonversi nilai string di kolom ke anggota yang cocok dari jenis enum .NET. Namun, jika nilai string tidak cocok dan anggota enum, maka properti diatur ke nilai default untuk enum.

Perilaku yang baru

EF Core 6.0 sekarang melempar InvalidOperationException dengan pesan "Tidak dapat mengonversi nilai string '{value}' dari database ke nilai apa pun dalam enum '{enumType}' yang dipetakan."

Mengapa

Mengonversi ke nilai default dapat mengakibatkan kerusakan database jika entitas nantinya disimpan kembali ke database.

Mitigasi

Idealnya, pastikan bahwa kolom database hanya berisi nilai yang valid. Secara bergantian, terapkan ValueConverter dengan perilaku lama.

DbFunctionBuilder.HasTranslation sekarang menyediakan argumen fungsi sebagai IReadOnlyList daripada IReadOnlyCollection

Masalah Pelacakan #23565

Perilaku yang lama

Saat mengonfigurasi terjemahan untuk fungsi yang ditentukan pengguna menggunakan HasTranslation metode, argumen untuk fungsi disediakan sebagai IReadOnlyCollection<SqlExpression>.

Perilaku yang baru

Di EF Core 6.0, argumen sekarang disediakan sebagai IReadOnlyList<SqlExpression>.

Mengapa

IReadOnlyList memungkinkan untuk menggunakan pengindeks, sehingga argumen sekarang lebih mudah diakses.

Mitigasi

Tidak ada. IReadOnlyListIReadOnlyCollection mengimplementasikan antarmuka, sehingga transisi harus mudah.

Pemetaan tabel default tidak dihapus saat entitas dipetakan ke fungsi bernilai tabel

Masalah Pelacakan #23408

Perilaku yang lama

Ketika entitas dipetakan ke fungsi bernilai tabel, pemetaan defaultnya ke tabel dihapus.

Perilaku yang baru

Di EF Core 6.0, entitas masih dipetakan ke tabel menggunakan pemetaan default, bahkan jika juga dipetakan ke fungsi bernilai tabel.

Mengapa

Fungsi bernilai tabel yang mengembalikan entitas sering digunakan baik sebagai pembantu atau untuk merangkum operasi yang mengembalikan kumpulan entitas, bukan sebagai pengganti ketat dari seluruh tabel. Perubahan ini bertujuan agar lebih sejalan dengan kemungkinan niat pengguna.

Mitigasi

Pemetaan ke tabel dapat dinonaktifkan secara eksplisit dalam konfigurasi model:

modelBuilder.Entity<MyEntity>().ToTable((string)null);

dotnet-ef menargetkan .NET 6

Masalah Pelacakan #27787

Perilaku yang lama

Perintah dotnet-ef telah menargetkan .NET Core 3.1 untuk sementara waktu sekarang. Ini memungkinkan Anda menggunakan versi alat yang lebih baru tanpa menginstal versi runtime .NET yang lebih baru.

Perilaku yang baru

Di EF Core 6.0.6, alat dotnet-ef sekarang menargetkan .NET 6. Anda masih dapat menggunakan alat ini pada proyek yang menargetkan versi .NET dan .NET Core yang lebih lama, tetapi Anda harus menginstal runtime .NET 6 untuk menjalankan alat.

Mengapa

.NET 6.0.200 SDK memperbarui perilaku dotnet tool install pada osx-arm64 untuk membuat shim osx-x64 untuk alat yang menargetkan .NET Core 3.1. Untuk mempertahankan pengalaman default kerja untuk dotnet-ef, kami harus memperbaruinya ke target .NET 6.

Mitigasi

Untuk menjalankan dotnet-ef tanpa menginstal runtime .NET 6, Anda dapat menginstal versi alat yang lebih lama:

dotnet tool install dotnet-ef --version 3.1.*

IModelCacheKeyFactory implementasi mungkin perlu diperbarui untuk menangani penembolokan waktu desain

Masalah Pelacakan #25154

Perilaku yang lama

IModelCacheKeyFactory tidak memiliki opsi untuk menyimpan model waktu desain secara terpisah dari model runtime.

Perilaku yang baru

IModelCacheKeyFactory memiliki kelebihan beban baru yang memungkinkan model waktu desain di-cache secara terpisah dari model runtime. Tidak menerapkan metode ini dapat mengakibatkan pengecualian yang mirip dengan:

System.InvalidOperationException: 'Konfigurasi yang diminta tidak disimpan dalam model yang dioptimalkan baca, silakan gunakan 'DbContext.GetService<IDesignTimeModel>(). Model'.'

Mengapa

Implementasi model yang dikompilasi memerlukan pemisahan waktu desain (digunakan saat membangun model) dan runtime (digunakan saat mengeksekusi kueri, dll.) model. Jika kode runtime memerlukan akses ke informasi waktu desain, model waktu desain harus di-cache.

Mitigasi

Terapkan kelebihan beban baru. Contohnya:

public object Create(DbContext context, bool designTime)
    => context is DynamicContext dynamicContext
        ? (context.GetType(), dynamicContext.UseIntProperty, designTime)
        : (object)context.GetType();

Navigasi '{navigation}' diabaikan dari 'Sertakan' dalam kueri karena perbaikan akan secara otomatis mengisinya. Jika ada navigasi lebih lanjut yang ditentukan dalam 'Sertakan' setelahnya, navigasi tersebut akan diabaikan. Berjalan kembali di pohon include tidak diperbolehkan.

Masalah Pelacakan #4315

Perilaku yang lama

Peristiwa CoreEventId.NavigationBaseIncludeIgnored dicatat sebagai peringatan secara default.

Perilaku yang baru

Peristiwa CoreEventId.NavigationBaseIncludeIgnored dicatat sebagai kesalahan secara default dan menyebabkan pengecualian dilemparkan.

Mengapa

Pola kueri ini tidak diizinkan, jadi EF Core sekarang melempar untuk menunjukkan bahwa kueri harus diperbarui.

Mitigasi

Perilaku lama dapat dipulihkan dengan mengonfigurasi peristiwa sebagai peringatan. Contohnya:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.ConfigureWarnings(b => b.Warn(CoreEventId.NavigationBaseIncludeIgnored));