Perubahan mencolok disertakan dalam EF Core 3.x
PERUBAHAN API dan perilaku berikut berpotensi memutus aplikasi yang ada saat meningkatkannya menjadi 3.x. Perubahan yang kami harapkan hanya berdampak pada penyedia database didokumenkan di bawah perubahan penyedia.
Ringkasan
Perubahan berdampak tinggi
Kueri LINQ tidak lagi dievaluasi pada klien
Masalah Pelacakan #14935Lihat juga masalah #12795
Perilaku yang lama
Sebelum 3.0, ketika EF Core tidak dapat mengonversi ekspresi yang merupakan bagian dari kueri ke SQL atau parameter, EF Core secara otomatis mengevaluasi ekspresi pada klien. Secara default, evaluasi klien dari ekspresi yang berpotensi mahal hanya memicu peringatan.
Perilaku yang baru
Dimulai dengan 3.0, EF Core hanya memungkinkan ekspresi dalam proyeksi tingkat atas (panggilan terakhir Select()
dalam kueri) untuk dievaluasi pada klien.
Saat ekspresi di bagian lain kueri tidak dapat dikonversi ke SQL atau parameter, pengecualian akan dilemparkan.
Mengapa
Evaluasi kueri klien otomatis memungkinkan banyak kueri dijalankan bahkan jika bagian penting dari kueri tersebut tidak dapat diterjemahkan.
Perilaku ini dapat mengakibatkan perilaku yang tidak terduga dan berpotensi merusak yang mungkin hanya terlihat dalam produksi.
Misalnya, kondisi dalam Where()
panggilan yang tidak dapat diterjemahkan dapat menyebabkan semua baris dari tabel ditransfer dari server database, dan filter yang akan diterapkan pada klien.
Situasi ini dapat dengan mudah tidak terdeteksi jika tabel hanya berisi beberapa baris dalam pengembangan, tetapi terpukul keras ketika aplikasi pindah ke produksi, di mana tabel mungkin berisi jutaan baris.
Peringatan evaluasi klien juga terbukti terlalu mudah diabaikan selama pengembangan.
Selain itu, evaluasi klien otomatis dapat menyebabkan masalah di mana meningkatkan terjemahan kueri untuk ekspresi tertentu menyebabkan perubahan yang tidak diinginkan di antara rilis.
Mitigasi
Jika kueri tidak dapat sepenuhnya diterjemahkan, maka tulis ulang kueri dalam formulir yang dapat diterjemahkan, atau gunakan AsEnumerable()
, ToList()
, atau mirip dengan secara eksplisit membawa data kembali ke klien tempat kueri tersebut kemudian dapat diproses lebih lanjut menggunakan LINQ-to-Objects.
Perubahan dampak sedang
Entity Framework Core tidak lagi menjadi bagian dari kerangka kerja bersama ASP.NET Core
Melacak Pengumuman Masalah#325
Perilaku yang lama
Sebelum ASP.NET Core 3.0, ketika Anda menambahkan referensi paket ke Microsoft.AspNetCore.App
atau Microsoft.AspNetCore.All
, itu akan mencakup EF Core dan beberapa penyedia data EF Core seperti penyedia SQL Server.
Perilaku yang baru
Mulai 3.0, kerangka kerja bersama ASP.NET Core tidak menyertakan EF Core atau penyedia data EF Core apa pun.
Mengapa
Sebelum perubahan ini, mendapatkan EF Core memerlukan langkah-langkah yang berbeda tergantung pada apakah aplikasi yang ditargetkan ASP.NET Core dan SQL Server atau tidak. Selain itu, meningkatkan ASP.NET Core memaksa peningkatan EF Core dan penyedia SQL Server, yang tidak selalu diinginkan.
Dengan perubahan ini, pengalaman mendapatkan EF Core sama di semua penyedia, implementasi .NET yang didukung, dan jenis aplikasi. Pengembang juga sekarang dapat mengontrol dengan tepat kapan penyedia data EF Core dan EF Core ditingkatkan.
Mitigasi
Untuk menggunakan EF Core dalam aplikasi ASP.NET Core 3.0 atau aplikasi lain yang didukung, tambahkan referensi paket secara eksplisit ke penyedia database EF Core yang akan digunakan aplikasi Anda.
Alat baris perintah EF Core, dotnet ef, bukan lagi bagian dari .NET Core SDK
Perilaku yang lama
Sebelum 3.0, dotnet ef
alat ini disertakan dalam .NET Core SDK dan siap digunakan dari baris perintah dari proyek apa pun tanpa memerlukan langkah tambahan.
Perilaku yang baru
Mulai dari 3.0, .NET SDK tidak menyertakan dotnet ef
alat, jadi sebelum Anda dapat menggunakannya, Anda harus menginstalnya secara eksplisit sebagai alat lokal atau global.
Mengapa
Perubahan ini memungkinkan kami untuk mendistribusikan dan memperbarui dotnet ef
sebagai alat .NET CLI biasa di NuGet, konsisten dengan fakta bahwa EF Core 3.0 juga selalu didistribusikan sebagai paket NuGet.
Mitigasi
Untuk dapat mengelola migrasi atau perancah DbContext
, instal dotnet-ef
sebagai alat global:
dotnet tool install --global dotnet-ef
Anda juga dapat memperolehnya alat lokal saat memulihkan dependensi proyek yang menyatakannya sebagai dependensi alat menggunakan file manifes alat.
Perubahan berdampak rendah
FromSql, ExecuteSql, dan ExecuteSqlAsync telah diganti namanya
Penting
ExecuteSqlCommand
dan ExecuteSqlCommandAsync
tidak digunakan lagi. Gunakan metode ini sebagai gantinya.
Perilaku yang lama
Sebelum EF Core 3.0, nama metode ini kelebihan beban untuk bekerja dengan string normal atau string yang harus diinterpolasi ke dalam SQL dan parameter.
Perilaku yang baru
Dimulai dengan EF Core 3.0, gunakan FromSqlRaw
, ExecuteSqlRaw
, dan ExecuteSqlRawAsync
untuk membuat kueri berparameter di mana parameter diteruskan secara terpisah dari string kueri.
Contohnya:
context.Products.FromSqlRaw(
"SELECT * FROM Products WHERE Name = {0}",
product.Name);
Gunakan FromSqlInterpolated
, ExecuteSqlInterpolated
, dan ExecuteSqlInterpolatedAsync
untuk membuat kueri berparameter di mana parameter diteruskan sebagai bagian dari string kueri terinterpolasi.
Contohnya:
context.Products.FromSqlInterpolated(
$"SELECT * FROM Products WHERE Name = {product.Name}");
Perhatikan bahwa kedua kueri di atas akan menghasilkan SQL parameter yang sama dengan parameter SQL yang sama.
Mengapa
Metode kelebihan beban seperti ini membuatnya sangat mudah untuk secara tidak sengaja memanggil metode string mentah ketika niatnya adalah untuk memanggil metode string yang diinterpolasi, dan sebaliknya. Hal ini dapat mengakibatkan kueri tidak diparameterkan ketika seharusnya.
Mitigasi
Beralih untuk menggunakan nama metode baru.
Metode FromSql saat digunakan dengan prosedur tersimpan tidak dapat disusur
Perilaku yang lama
Sebelum EF Core 3.0, metode FromSql mencoba mendeteksi apakah SQL yang diteruskan dapat terdiri. Ini melakukan evaluasi klien ketika SQL tidak dapat dikomposisikan seperti prosedur tersimpan. Kueri berikut bekerja dengan menjalankan prosedur tersimpan di server dan melakukan FirstOrDefault di sisi klien.
context.Products.FromSqlRaw("[dbo].[Ten Most Expensive Products]").FirstOrDefault();
Perilaku yang baru
Dimulai dengan EF Core 3.0, EF Core tidak akan mencoba mengurai SQL. Jadi jika Anda menyusun setelah FromSqlRaw/FromSqlInterpolated, maka EF Core akan menyusun SQL dengan menyebabkan sub kueri. Jadi jika Anda menggunakan prosedur tersimpan dengan komposisi maka Anda akan mendapatkan pengecualian untuk sintaks SQL yang tidak valid.
Mengapa
EF Core 3.0 tidak mendukung evaluasi klien otomatis, karena rawan kesalahan seperti yang dijelaskan di sini.
Mitigasi
Jika Anda menggunakan prosedur tersimpan di FromSqlRaw/FromSqlInterpolated, Anda tahu bahwa prosedur tersebut tidak dapat disusam, sehingga Anda dapat menambahkan AsEnumerable
/AsAsyncEnumerable
tepat setelah panggilan metode FromSql untuk menghindari komposisi apa pun di sisi server.
context.Products.FromSqlRaw("[dbo].[Ten Most Expensive Products]").AsEnumerable().FirstOrDefault();
Metode FromSql hanya dapat ditentukan pada akar kueri
Perilaku yang lama
Sebelum EF Core 3.0, FromSql
metode dapat ditentukan di mana saja dalam kueri.
Perilaku yang baru
Dimulai dengan EF Core 3.0, metode dan baru FromSqlRaw
(yang menggantikan FromSql
) hanya dapat ditentukan pada akar kueri, yaitu langsung pada DbSet<>
.FromSqlInterpolated
Mencoba menentukannya di tempat lain akan mengakibatkan kesalahan kompilasi.
Mengapa
Menentukan FromSql
di mana saja selain pada DbSet
yang tidak memiliki arti tambahan atau nilai tambah, dan dapat menyebabkan ambiguitas dalam skenario tertentu.
Mitigasi
FromSql
pemanggilan harus dipindahkan untuk langsung di mana DbSet
mereka menerapkannya.
Kueri tanpa pelacakan tidak lagi melakukan resolusi identitas
Perilaku yang lama
Sebelum EF Core 3.0, instans entitas yang sama akan digunakan untuk setiap kemunculan entitas dengan jenis dan ID tertentu. Ini cocok dengan perilaku kueri pelacakan. Misalnya, kueri ini:
var results = context.Products.Include(e => e.Category).AsNoTracking().ToList();
akan mengembalikan instans yang sama Category
untuk masing-masing Product
yang terkait dengan kategori yang diberikan.
Perilaku yang baru
Dimulai dengan EF Core 3.0, instans entitas yang berbeda akan dibuat ketika entitas dengan jenis dan ID tertentu ditemui di tempat yang berbeda dalam grafik yang dikembalikan. Misalnya, kueri di atas sekarang akan mengembalikan instans baru Category
untuk masing-masing Product
bahkan ketika dua produk dikaitkan dengan kategori yang sama.
Mengapa
Resolusi identitas (yaitu, menentukan bahwa entitas memiliki jenis dan ID yang sama dengan entitas yang ditemui sebelumnya) menambahkan performa tambahan dan overhead memori. Ini biasanya menjalankan penghitung mengapa kueri tanpa pelacakan digunakan di tempat pertama. Selain itu, meskipun resolusi identitas terkadang dapat berguna, tidak diperlukan jika entitas akan diserialisasikan dan dikirim ke klien, yang umum untuk kueri tanpa pelacakan.
Mitigasi
Gunakan kueri pelacakan jika resolusi identitas diperlukan.
Nilai kunci sementara tidak lagi diatur ke instans entitas
Perilaku yang lama
Sebelum EF Core 3.0, nilai sementara ditetapkan ke semua properti kunci yang nantinya akan memiliki nilai nyata yang dihasilkan oleh database. Biasanya nilai sementara ini adalah angka negatif yang besar.
Perilaku yang baru
Dimulai dengan 3.0, EF Core menyimpan nilai kunci sementara sebagai bagian dari informasi pelacakan entitas, dan membiarkan properti kunci itu sendiri tidak berubah.
Mengapa
Perubahan ini dilakukan untuk mencegah nilai kunci sementara menjadi permanen secara keliru ketika entitas yang sebelumnya telah dilacak oleh beberapa DbContext
instans dipindahkan ke instans yang berbeda DbContext
.
Mitigasi
Aplikasi yang menetapkan nilai kunci utama ke kunci asing untuk membentuk asosiasi antar entitas dapat bergantung pada perilaku lama jika kunci utama dibuat dan milik entitas dalam Added
status.
Hal ini dapat dihindari dengan:
- Tidak menggunakan kunci yang dihasilkan penyimpanan.
- Mengatur properti navigasi ke hubungan formulir alih-alih mengatur nilai kunci asing.
- Dapatkan nilai kunci sementara aktual dari informasi pelacakan entitas.
Misalnya,
context.Entry(blog).Property(e => e.Id).CurrentValue
akan mengembalikan nilai sementara meskipunblog.Id
belum ditetapkan.
DetectChanges menghormati nilai kunci yang dihasilkan penyimpanan
Perilaku yang lama
Sebelum EF Core 3.0, entitas yang tidak terlacak yang ditemukan oleh DetectChanges
akan dilacak dalam Added
status dan dimasukkan sebagai baris baru saat SaveChanges
dipanggil.
Perilaku yang baru
Dimulai dengan EF Core 3.0, jika entitas menggunakan nilai kunci yang dihasilkan dan beberapa nilai kunci diatur, maka entitas akan dilacak dalam Modified
status .
Ini berarti bahwa baris untuk entitas diasumsikan ada dan akan diperbarui ketika SaveChanges
dipanggil.
Jika nilai kunci tidak diatur, atau jika jenis entitas tidak menggunakan kunci yang dihasilkan, maka entitas baru masih akan dilacak seperti Added
pada versi sebelumnya.
Mengapa
Perubahan ini dilakukan untuk mempermudah dan lebih konsisten untuk bekerja dengan grafik entitas yang terputus saat menggunakan kunci yang dihasilkan penyimpanan.
Mitigasi
Perubahan ini dapat merusak aplikasi jika jenis entitas dikonfigurasi untuk menggunakan kunci yang dihasilkan tetapi nilai kunci secara eksplisit diatur untuk instans baru. Perbaikannya adalah mengonfigurasi properti kunci secara eksplisit untuk tidak menggunakan nilai yang dihasilkan. Misalnya, dengan API yang fasih:
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.ValueGeneratedNever();
Atau dengan anotasi data:
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
Penghapusan kaskade sekarang terjadi segera secara default
Perilaku yang lama
Sebelum 3.0, tindakan berjenjang yang diterapkan EF Core (menghapus entitas dependen ketika prinsipal yang diperlukan dihapus atau ketika hubungan dengan prinsipal yang diperlukan terputus) tidak terjadi sampai SaveChanges dipanggil.
Perilaku yang baru
Dimulai dengan 3.0, EF Core menerapkan tindakan kaskade segera setelah kondisi pemicu terdeteksi.
Misalnya, panggilan context.Remove()
untuk menghapus entitas utama akan mengakibatkan semua dependen terkait yang diperlukan yang dilacak juga diatur ke Deleted
segera.
Mengapa
Perubahan ini dilakukan untuk meningkatkan pengalaman untuk skenario pengikatan dan audit data di mana penting untuk memahami entitas mana yang akan dihapus sebelum SaveChanges
dipanggil.
Mitigasi
Perilaku sebelumnya dapat dipulihkan melalui pengaturan pada context.ChangeTracker
.
Contohnya:
context.ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;
Pemuatan entitas terkait yang bersemangat sekarang terjadi dalam satu kueri
Perilaku yang lama
Sebelum 3.0, dengan bersemangat memuat navigasi pengumpulan melalui Include
operator menyebabkan beberapa kueri dihasilkan pada database relasional, satu untuk setiap jenis entitas terkait.
Perilaku yang baru
Dimulai dengan 3.0, EF Core menghasilkan satu kueri dengan JOIN pada database relasional.
Mengapa
Mengeluarkan beberapa kueri untuk mengimplementasikan satu kueri LINQ menyebabkan banyak masalah, termasuk performa negatif karena beberapa perjalanan pulang-pergi database diperlukan, dan masalah koherensi data karena setiap kueri dapat mengamati status database yang berbeda.
Mitigasi
Meskipun secara teknis ini bukan perubahan yang melanggar, ini bisa memiliki efek yang cukup besar pada performa aplikasi ketika satu kueri berisi sejumlah Include
besar operator pada navigasi koleksi. Lihat komentar ini untuk informasi selengkapnya dan untuk menulis ulang kueri dengan cara yang lebih efisien.
**
DeleteBehavior.Restrict memiliki semantik yang lebih bersih
Perilaku yang lama
Sebelum 3.0, DeleteBehavior.Restrict
kunci asing yang dibuat dalam database dengan semantik, tetapi juga mengubah perbaikan internal dengan Restrict
cara yang tidak jelas.
Perilaku yang baru
Dimulai dengan 3.0, DeleteBehavior.Restrict
memastikan bahwa kunci asing dibuat dengan Restrict
semantik --yaitu, tidak ada kaskade; melemparkan pelanggaran batasan --tanpa juga berdampak pada perbaikan internal EF.
Mengapa
Perubahan ini dilakukan untuk meningkatkan pengalaman penggunaan DeleteBehavior
dengan cara yang intuitif, tanpa efek samping yang tidak terduga.
Mitigasi
Perilaku sebelumnya dapat dipulihkan dengan menggunakan DeleteBehavior.ClientNoAction
.
Jenis kueri dikonsolidasikan dengan jenis entitas
Perilaku yang lama
Sebelum EF Core 3.0, jenis kueri adalah sarana untuk mengkueri data yang tidak menentukan kunci utama dengan cara terstruktur. Artinya, jenis kueri digunakan untuk memetakan jenis entitas tanpa kunci (kemungkinan besar dari tampilan, tetapi mungkin dari tabel) sementara jenis entitas reguler digunakan ketika kunci tersedia (kemungkinan besar dari tabel, tetapi mungkin dari tampilan).
Perilaku yang baru
Jenis kueri sekarang hanya menjadi jenis entitas tanpa kunci utama. Jenis entitas tanpa kunci memiliki fungsionalitas yang sama dengan jenis kueri di versi sebelumnya.
Mengapa
Perubahan ini dilakukan untuk mengurangi kebingungan sekeliling tujuan jenis kueri. Secara khusus, mereka adalah jenis entitas tanpa kunci dan secara inheren baca-saja karena ini, tetapi tidak boleh digunakan hanya karena jenis entitas perlu dibaca-saja. Demikian juga, mereka sering dipetakan ke tampilan, tetapi ini hanya karena tampilan sering tidak menentukan kunci.
Mitigasi
Bagian API berikut sekarang usang:
ModelBuilder.Query<>()
- SebaliknyaModelBuilder.Entity<>().HasNoKey()
perlu dipanggil untuk menandai jenis entitas karena tidak memiliki kunci. Ini masih tidak akan dikonfigurasi oleh konvensi untuk menghindari kesalahan konfigurasi ketika kunci primer diharapkan, tetapi tidak cocok dengan konvensi.DbQuery<>
- Sebagai gantinyaDbSet<>
harus digunakan.DbContext.Query<>()
- Sebagai gantinyaDbContext.Set<>()
harus digunakan.IQueryTypeConfiguration<TQuery>
- Sebagai gantinyaIEntityTypeConfiguration<TEntity>
harus digunakan.
Catatan
Karena masalah dalam 3.x saat mengkueri entitas tanpa kunci yang memiliki semua properti yang diatur ke null
null
akan dikembalikan alih-alih entitas, jika masalah ini berlaku untuk skenario Anda juga menambahkan logika untuk menangani null
hasil.
API Konfigurasi untuk hubungan jenis yang dimiliki telah berubah
Masalah Pelacakan #12444Masalah Pelacakan #9148Masalah Pelacakan #14153
Perilaku yang lama
Sebelum EF Core 3.0, konfigurasi hubungan yang dimiliki dilakukan langsung setelah OwnsOne
panggilan atau OwnsMany
.
Perilaku yang baru
Dimulai dengan EF Core 3.0, sekarang ada API yang fasih untuk mengonfigurasi properti navigasi kepada pemilik menggunakan WithOwner()
.
Contohnya:
modelBuilder.Entity<Order>.OwnsOne(e => e.Details).WithOwner(e => e.Order);
Konfigurasi yang terkait dengan hubungan antara pemilik dan yang dimiliki sekarang harus ditautkan setelah WithOwner()
mirip dengan bagaimana hubungan lain dikonfigurasi.
Sementara konfigurasi untuk jenis yang dimiliki itu sendiri masih akan ditautkan setelah OwnsOne()/OwnsMany()
.
Contohnya:
modelBuilder.Entity<Order>.OwnsOne(e => e.Details, eb =>
{
eb.WithOwner()
.HasForeignKey(e => e.AlternateId)
.HasConstraintName("FK_OrderDetails");
eb.ToTable("OrderDetails");
eb.HasKey(e => e.AlternateId);
eb.HasIndex(e => e.Id);
eb.HasOne(e => e.Customer).WithOne();
eb.HasData(
new OrderDetails
{
AlternateId = 1,
Id = -1
});
});
Selain itu memanggil Entity()
, HasOne()
, atau Set()
dengan target jenis yang dimiliki sekarang akan melemparkan pengecualian.
Mengapa
Perubahan ini dilakukan untuk membuat pemisahan yang lebih bersih antara mengonfigurasi jenis yang dimiliki itu sendiri dan hubungan dengan jenis yang dimiliki.
Ini pada gilirannya menghilangkan ambiguitas dan kebingungan di sekitar metode seperti HasForeignKey
.
Mitigasi
Ubah konfigurasi hubungan jenis yang dimiliki untuk menggunakan permukaan API baru seperti yang ditunjukkan pada contoh di atas.
Entitas dependen yang berbagi tabel dengan prinsipal sekarang bersifat opsional
Perilaku yang lama
Perhatikan model berikut:
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public OrderDetails Details { get; set; }
}
public class OrderDetails
{
public int Id { get; set; }
public string ShippingAddress { get; set; }
}
Sebelum EF Core 3.0, jika OrderDetails
dimiliki oleh Order
atau secara eksplisit dipetakan ke tabel yang sama maka OrderDetails
instans selalu diperlukan saat menambahkan baru Order
.
Perilaku yang baru
Dimulai dengan 3.0, EF Core memungkinkan untuk menambahkan Order
tanpa OrderDetails
dan memetakan semua OrderDetails
properti kecuali kunci utama ke kolom nullable.
Saat mengkueri EF Core diatur OrderDetails
ke null
jika salah satu properti yang diperlukan tidak memiliki nilai atau jika tidak memiliki properti yang diperlukan selain kunci utama dan semua properti adalah null
.
Mitigasi
Jika model Anda memiliki berbagi tabel tergantung pada semua kolom opsional, tetapi navigasi yang menunjuk ke null
model tersebut tidak diharapkan maka aplikasi harus dimodifikasi untuk menangani kasus ketika navigasi adalah null
. Jika ini tidak memungkinkan properti yang diperlukan harus ditambahkan ke jenis entitas atau setidaknya satu properti harus memiliki non-nilainull
yang ditetapkan untuk properti tersebut.
Semua entitas yang berbagi tabel dengan kolom token konkurensi harus memetakannya ke properti
Perilaku yang lama
Perhatikan model berikut:
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public byte[] Version { get; set; }
public OrderDetails Details { get; set; }
}
public class OrderDetails
{
public int Id { get; set; }
public string ShippingAddress { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.Property(o => o.Version).IsRowVersion().HasColumnName("Version");
}
Sebelum EF Core 3.0, jika OrderDetails
dimiliki oleh Order
atau secara eksplisit dipetakan ke tabel yang sama, maka memperbarui hanya OrderDetails
tidak akan memperbarui Version
nilai pada klien dan pembaruan berikutnya akan gagal.
Perilaku yang baru
Dimulai dengan 3.0, EF Core menyebarluaskan nilai baru Version
jika Order
memiliki OrderDetails
. Jika tidak, pengecualian dilemparkan selama validasi model.
Mengapa
Perubahan ini dilakukan untuk menghindari nilai token konkurensi kedaluarsa ketika hanya salah satu entitas yang dipetakan ke tabel yang sama yang diperbarui.
Mitigasi
Semua entitas yang berbagi tabel harus menyertakan properti yang dipetakan ke kolom token konkurensi. Ada kemungkinan membuatnya dalam keadaan bayangan:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<OrderDetails>()
.Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
}
Entitas yang dimiliki tidak dapat dikueri tanpa pemilik menggunakan kueri pelacakan
Perilaku yang lama
Sebelum EF Core 3.0, entitas yang dimiliki dapat dikueri sebagai navigasi lainnya.
context.People.Select(p => p.Address);
Perilaku yang baru
Dimulai dengan 3.0, EF Core akan melempar jika kueri pelacakan memproyeksikan entitas yang dimiliki tanpa pemilik.
Mengapa
Entitas yang dimiliki tidak dapat dimanipulasi tanpa pemilik, jadi dalam sebagian besar kasus yang mengkuerinya dengan cara ini adalah kesalahan.
Mitigasi
Jika entitas yang dimiliki harus dilacak untuk dimodifikasi dengan cara apa pun nanti, pemilik harus disertakan dalam kueri.
Jika tidak, tambahkan AsNoTracking()
panggilan:
context.People.Select(p => p.Address).AsNoTracking();
Properti yang diwariskan dari jenis yang tidak dipetakan sekarang dipetakan ke satu kolom untuk semua jenis turunan
Perilaku yang lama
Perhatikan model berikut:
public abstract class EntityBase
{
public int Id { get; set; }
}
public abstract class OrderBase : EntityBase
{
public int ShippingAddress { get; set; }
}
public class BulkOrder : OrderBase
{
}
public class Order : OrderBase
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<OrderBase>();
modelBuilder.Entity<EntityBase>();
modelBuilder.Entity<BulkOrder>();
modelBuilder.Entity<Order>();
}
Sebelum EF Core 3.0, ShippingAddress
properti akan dipetakan ke kolom terpisah untuk BulkOrder
dan Order
secara default.
Perilaku yang baru
Dimulai dengan 3.0, EF Core hanya membuat satu kolom untuk ShippingAddress
.
Mengapa
Perilaku lama itu tak terduga.
Mitigasi
Properti masih dapat dipetakan secara eksplisit untuk memisahkan kolom pada jenis turunan:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Ignore<OrderBase>();
modelBuilder.Entity<EntityBase>();
modelBuilder.Entity<BulkOrder>()
.Property(o => o.ShippingAddress).HasColumnName("BulkShippingAddress");
modelBuilder.Entity<Order>()
.Property(o => o.ShippingAddress).HasColumnName("ShippingAddress");
}
Konvensi properti kunci asing tidak lagi cocok dengan nama yang sama dengan properti utama
Perilaku yang lama
Perhatikan model berikut:
public class Customer
{
public int CustomerId { get; set; }
public ICollection<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
}
Sebelum EF Core 3.0, CustomerId
properti akan digunakan untuk kunci asing berdasarkan konvensi.
Namun, jika Order
adalah jenis yang dimiliki, maka ini juga akan membuat CustomerId
kunci primer dan ini biasanya bukan harapan.
Perilaku yang baru
Dimulai dengan 3.0, EF Core tidak mencoba menggunakan properti untuk kunci asing berdasarkan konvensi jika mereka memiliki nama yang sama dengan properti utama. Nama jenis utama yang digabungkan dengan nama properti utama, dan nama navigasi yang digabungkan dengan pola nama properti utama masih cocok. Contohnya:
public class Customer
{
public int Id { get; set; }
public ICollection<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
}
public class Customer
{
public int Id { get; set; }
public ICollection<Order> Orders { get; set; }
}
public class Order
{
public int Id { get; set; }
public int BuyerId { get; set; }
public Customer Buyer { get; set; }
}
Mengapa
Perubahan ini dilakukan untuk menghindari keliru menentukan properti kunci primer pada jenis yang dimiliki.
Mitigasi
Jika properti dimaksudkan untuk menjadi kunci asing, dan karenanya bagian dari kunci primer, maka konfigurasikan secara eksplisit seperti itu.
Koneksi database sekarang ditutup jika tidak digunakan lagi sebelum TransactionScope selesai
Perilaku yang lama
Sebelum EF Core 3.0, jika konteks membuka koneksi di dalam TransactionScope
, koneksi tetap terbuka saat saat ini TransactionScope
aktif.
using (new TransactionScope())
{
using (AdventureWorks context = new AdventureWorks())
{
context.ProductCategories.Add(new ProductCategory());
context.SaveChanges();
// Old behavior: Connection is still open at this point
var categories = context.ProductCategories().ToList();
}
}
Perilaku yang baru
Dimulai dengan 3.0, EF Core menutup koneksi segera setelah selesai menggunakannya.
Mengapa
Perubahan ini memungkinkan untuk menggunakan beberapa konteks dalam hal yang sama TransactionScope
. Perilaku baru juga cocok dengan EF6.
Mitigasi
Jika koneksi perlu tetap terbuka, panggilan eksplisit ke OpenConnection()
akan memastikan bahwa EF Core tidak menutupnya sebelum waktunya:
using (new TransactionScope())
{
using (AdventureWorks context = new AdventureWorks())
{
context.Database.OpenConnection();
context.ProductCategories.Add(new ProductCategory());
context.SaveChanges();
var categories = context.ProductCategories().ToList();
context.Database.CloseConnection();
}
}
Setiap properti menggunakan pembuatan kunci bilangan bulat dalam memori independen
Perilaku yang lama
Sebelum EF Core 3.0, satu generator nilai bersama digunakan untuk semua properti kunci bilangan bulat dalam memori.
Perilaku yang baru
Dimulai dengan EF Core 3.0, setiap properti kunci bilangan bulat mendapatkan generator nilainya sendiri saat menggunakan database dalam memori. Selain itu, jika database dihapus, pembuatan kunci diatur ulang untuk semua tabel.
Mengapa
Perubahan ini dilakukan untuk menyelaraskan pembuatan kunci dalam memori lebih dekat dengan pembuatan kunci database nyata dan untuk meningkatkan kemampuan untuk mengisolasi pengujian satu sama lain saat menggunakan database dalam memori.
Mitigasi
Ini dapat merusak aplikasi yang mengandalkan nilai kunci dalam memori tertentu untuk diatur. Pertimbangkan untuk tidak mengandalkan nilai kunci tertentu, atau memperbarui agar sesuai dengan perilaku baru.
Bidang penolakan digunakan secara default
Perilaku yang lama
Sebelum 3.0, bahkan jika bidang pencadangan untuk properti diketahui, EF Core masih akan secara default membaca dan menulis nilai properti menggunakan metode getter dan setter properti. Pengecualian untuk ini adalah eksekusi kueri, di mana bidang penolakan akan diatur secara langsung jika diketahui.
Perilaku yang baru
Dimulai dengan EF Core 3.0, jika bidang pencadangan untuk properti diketahui, maka EF Core akan selalu membaca dan menulis properti tersebut menggunakan bidang backing. Ini dapat menyebabkan pemutusan aplikasi jika aplikasi mengandalkan perilaku tambahan yang dikodekan ke dalam metode getter atau setter.
Mengapa
Perubahan ini dilakukan untuk mencegah EF Core secara keliru memicu logika bisnis secara default saat melakukan operasi database yang melibatkan entitas.
Mitigasi
Perilaku pra-3.0 dapat dipulihkan melalui konfigurasi mode akses properti pada ModelBuilder
.
Contohnya:
modelBuilder.UsePropertyAccessMode(PropertyAccessMode.PreferFieldDuringConstruction);
Lempar jika beberapa bidang backing yang kompatibel ditemukan
Perilaku yang lama
Sebelum EF Core 3.0, jika beberapa bidang cocok dengan aturan untuk menemukan bidang dukungan properti, maka satu bidang akan dipilih berdasarkan beberapa urutan prioritas. Ini dapat menyebabkan bidang yang salah digunakan dalam kasus ambigu.
Perilaku yang baru
Dimulai dengan EF Core 3.0, jika beberapa bidang dicocokkan dengan properti yang sama, maka pengecualian akan dilemparkan.
Mengapa
Perubahan ini dilakukan untuk menghindari diam-diam menggunakan satu bidang di atas bidang lain ketika hanya satu yang dapat benar.
Mitigasi
Properti dengan bidang dukungan ambigu harus memiliki bidang untuk digunakan secara eksplisit. Misalnya, menggunakan API yang fasih:
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.HasField("_id");
Nama properti bidang saja harus cocok dengan nama bidang
Perilaku yang lama
Sebelum EF Core 3.0, properti dapat ditentukan oleh nilai string dan jika tidak ada properti dengan nama tersebut yang ditemukan pada jenis .NET, maka EF Core akan mencoba mencocokkannya dengan bidang menggunakan aturan konvensi.
private class Blog
{
private int _id;
public string Name { get; set; }
}
modelBuilder
.Entity<Blog>()
.Property("Id");
Perilaku yang baru
Dimulai dengan EF Core 3.0, properti khusus bidang harus sama persis dengan nama bidang.
modelBuilder
.Entity<Blog>()
.Property("_id");
Mengapa
Perubahan ini dilakukan untuk menghindari penggunaan bidang yang sama untuk dua properti bernama serupa, ini juga membuat aturan pencocokan untuk properti khusus bidang sama seperti untuk properti yang dipetakan ke properti CLR.
Mitigasi
Properti khusus bidang harus dinamai sama dengan bidang yang dipetakan. Dalam rilis EF Core di masa mendatang setelah 3.0, kami berencana untuk mengaktifkan kembali konfigurasi nama bidang yang berbeda dari nama properti secara eksplisit (lihat masalah #15307):
modelBuilder
.Entity<Blog>()
.Property("Id")
.HasField("_id");
AddDbContext/AddDbContextPool tidak lagi memanggil AddLogging dan AddMemoryCache
Perilaku yang lama
Sebelum EF Core 3.0, memanggil AddDbContext
atau AddDbContextPool
juga akan mendaftarkan layanan pengelogan dan penembolokan memori dengan DI melalui panggilan ke AddLogging dan AddMemoryCache.
Perilaku yang baru
Dimulai dengan EF Core 3.0, AddDbContext
dan AddDbContextPool
tidak akan lagi mendaftarkan layanan ini dengan Dependency Injection (DI).
Mengapa
EF Core 3.0 tidak mengharuskan layanan ini berada dalam kontainer DI aplikasi. Namun, jika ILoggerFactory
terdaftar dalam kontainer DI aplikasi, maka itu masih akan digunakan oleh EF Core.
Mitigasi
Jika aplikasi Anda membutuhkan layanan ini, daftarkan secara eksplisit dengan kontainer DI menggunakan AddLogging atau AddMemoryCache.
AddEntityFramework* menambahkan IMemoryCache dengan batas ukuran
Perilaku yang lama
Sebelum EF Core 3.0, metode panggilan AddEntityFramework*
juga akan mendaftarkan layanan penembolokan memori dengan DI tanpa batas ukuran.
Perilaku yang baru
Dimulai dengan EF Core 3.0, AddEntityFramework*
akan mendaftarkan layanan IMemoryCache dengan batas ukuran. Jika ada layanan lain yang ditambahkan setelahnya bergantung pada IMemoryCache, layanan tersebut dapat dengan cepat mencapai batas default yang menyebabkan pengecualian atau performa yang terdegradasi.
Mengapa
Menggunakan IMemoryCache tanpa batas dapat mengakibatkan penggunaan memori yang tidak terkendali jika ada bug dalam logika penembolokan kueri atau kueri dihasilkan secara dinamis. Memiliki batas default mengurangi potensi serangan DoS.
Mitigasi
Dalam kebanyakan kasus, panggilan AddEntityFramework*
tidak diperlukan jika AddDbContext
atau AddDbContextPool
dipanggil juga. Oleh karena itu, mitigasi terbaik adalah menghapus AddEntityFramework*
panggilan.
Jika aplikasi Anda membutuhkan layanan ini, maka daftarkan implementasi IMemoryCache secara eksplisit dengan kontainer DI sebelumnya menggunakan AddMemoryCache.
DbContext.Entry sekarang melakukan DetectChanges lokal
Perilaku yang lama
Sebelum EF Core 3.0, panggilan DbContext.Entry
akan menyebabkan perubahan terdeteksi untuk semua entitas yang dilacak.
Ini memastikan bahwa status yang terekspos dalam EntityEntry
sudah diperbarui.
Perilaku yang baru
Dimulai dengan EF Core 3.0, panggilan DbContext.Entry
sekarang hanya akan mencoba mendeteksi perubahan pada entitas tertentu dan entitas utama yang dilacak yang terkait dengannya.
Ini berarti bahwa perubahan di tempat lain mungkin tidak terdeteksi dengan memanggil metode ini, yang dapat memiliki implikasi pada status aplikasi.
Perhatikan bahwa jika ChangeTracker.AutoDetectChangesEnabled
diatur ke false
maka bahkan deteksi perubahan lokal ini akan dinonaktifkan.
Metode lain yang menyebabkan deteksi perubahan--misalnya ChangeTracker.Entries
dan SaveChanges
--masih menyebabkan penuhnya DetectChanges
semua entitas yang dilacak.
Mengapa
Perubahan ini dilakukan untuk meningkatkan performa default penggunaan context.Entry
.
Mitigasi
Panggil ChangeTracker.DetectChanges()
secara eksplisit sebelum memanggil Entry
untuk memastikan perilaku pra-3.0.
Kunci array string dan byte tidak dihasilkan klien secara default
Perilaku yang lama
Sebelum EF Core 3.0, string
dan byte[]
properti kunci dapat digunakan tanpa secara eksplisit mengatur nilai non-null.
Dalam kasus seperti itu, nilai kunci akan dihasilkan pada klien sebagai GUID, diserialisasikan ke byte untuk byte[]
.
Perilaku yang baru
Dimulai dengan EF Core 3.0 pengecualian akan dilemparkan yang menunjukkan bahwa tidak ada nilai kunci yang telah ditetapkan.
Mengapa
Perubahan ini dilakukan karena nilai yang dihasilkan string
/byte[]
klien umumnya tidak berguna, dan perilaku default membuatnya sulit untuk beralasan tentang nilai kunci yang dihasilkan dengan cara yang sama.
Mitigasi
Perilaku pra-3.0 dapat diperoleh dengan secara eksplisit menentukan bahwa properti kunci harus menggunakan nilai yang dihasilkan jika tidak ada nilai non-null lainnya yang ditetapkan. Misalnya, dengan API yang fasih:
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.ValueGeneratedOnAdd();
Atau dengan anotasi data:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
ILoggerFactory sekarang menjadi layanan terlingkup
Perilaku yang lama
Sebelum EF Core 3.0, ILoggerFactory
terdaftar sebagai layanan singleton.
Perilaku yang baru
Dimulai dengan EF Core 3.0, ILoggerFactory
sekarang terdaftar sebagai cakupan.
Mengapa
Perubahan ini dilakukan untuk memungkinkan asosiasi pencatat dengan DbContext
instans, yang memungkinkan fungsionalitas lain dan menghapus beberapa kasus perilaku patologis seperti ledakan penyedia layanan internal.
Mitigasi
Perubahan ini tidak boleh memengaruhi kode aplikasi kecuali mendaftar dan menggunakan layanan kustom pada penyedia layanan internal EF Core.
Hal ini tidak umum.
Dalam kasus ini, sebagian besar hal masih akan berfungsi, tetapi layanan singleton apa pun yang bergantung pada ILoggerFactory
perlu diubah untuk mendapatkan dengan ILoggerFactory
cara yang berbeda.
Jika Anda mengalami situasi seperti ini, silakan ajukan masalah pada pelacak masalah EF Core GitHub untuk memberi tahu kami bagaimana Anda menggunakannya ILoggerFactory
sehingga kami dapat lebih memahami cara untuk tidak memecahkan ini lagi di masa depan.
Proksi pemuatan malas tidak lagi mengasumsikan properti navigasi dimuat sepenuhnya
Perilaku yang lama
Sebelum EF Core 3.0, setelah DbContext
dibuang, tidak ada cara untuk mengetahui apakah properti navigasi tertentu pada entitas yang diperoleh dari konteks tersebut dimuat sepenuhnya atau tidak.
Proksi akan mengasumsikan bahwa navigasi referensi dimuat jika memiliki nilai non-null, dan navigasi koleksi dimuat jika tidak kosong.
Dalam kasus ini, mencoba untuk malas-beban akan menjadi no-op.
Perilaku yang baru
Dimulai dengan EF Core 3.0, proksi melacak apakah properti navigasi dimuat atau tidak. Ini berarti mencoba mengakses properti navigasi yang dimuat setelah konteks dibuang akan selalu menjadi no-op, bahkan ketika navigasi yang dimuat kosong atau null. Sebaliknya, mencoba mengakses properti navigasi yang tidak dimuat akan memberikan pengecualian jika konteks dibuang meskipun properti navigasi adalah koleksi yang tidak kosong. Jika situasi ini muncul, itu berarti kode aplikasi mencoba menggunakan pemuatan malas pada waktu yang tidak valid, dan aplikasi harus diubah untuk tidak melakukan ini.
Mengapa
Perubahan ini dilakukan untuk membuat perilaku konsisten dan benar ketika mencoba untuk malas-beban pada instans yang dibuang DbContext
.
Mitigasi
Perbarui kode aplikasi untuk tidak mencoba pemuatan malas dengan konteks yang dibuang, atau konfigurasikan ini menjadi no-op seperti yang dijelaskan dalam pesan pengecualian.
Pembuatan penyedia layanan internal yang berlebihan sekarang menjadi kesalahan secara default
Perilaku yang lama
Sebelum EF Core 3.0, peringatan akan dicatat untuk aplikasi yang membuat jumlah patologis penyedia layanan internal.
Perilaku yang baru
Dimulai dengan EF Core 3.0, peringatan ini sekarang dipertimbangkan dan kesalahan dan pengecualian dilemparkan.
Mengapa
Perubahan ini dilakukan untuk mendorong kode aplikasi yang lebih baik melalui mengekspos kasus patologis ini secara lebih eksplisit.
Mitigasi
Penyebab tindakan yang paling tepat dalam mengalami kesalahan ini adalah memahami akar penyebab dan berhenti membuat begitu banyak penyedia layanan internal.
Namun, kesalahan dapat dikonversi kembali ke peringatan (atau diabaikan) melalui konfigurasi pada DbContextOptionsBuilder
.
Contohnya:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.ConfigureWarnings(w => w.Log(CoreEventId.ManyServiceProvidersCreatedWarning));
}
Perilaku baru untuk HasOne/HasMany dipanggil dengan satu string
Perilaku yang lama
Sebelum EF Core 3.0, panggilan HasOne
kode atau HasMany
dengan string tunggal ditafsirkan dengan cara yang membingungkan.
Contohnya:
modelBuilder.Entity<Samurai>().HasOne("Entrance").WithOne();
Kode terlihat seperti berkaitan dengan Samurai
beberapa jenis entitas lain menggunakan Entrance
properti navigasi, yang mungkin bersifat privat.
Pada kenyataannya, kode ini mencoba membuat hubungan ke beberapa jenis entitas yang disebut Entrance
tanpa properti navigasi.
Perilaku yang baru
Dimulai dengan EF Core 3.0, kode di atas sekarang melakukan apa yang seharusnya dilakukan sebelumnya.
Mengapa
Perilaku lama sangat membingungkan, terutama ketika membaca kode konfigurasi dan mencari kesalahan.
Mitigasi
Ini hanya akan memutus aplikasi yang secara eksplisit mengonfigurasi hubungan menggunakan string untuk nama jenis dan tanpa menentukan properti navigasi secara eksplisit.
Ini tidak umum.
Perilaku sebelumnya dapat diperoleh melalui passing null
eksplisit untuk nama properti navigasi.
Contohnya:
modelBuilder.Entity<Samurai>().HasOne("Some.Entity.Type.Name", null).WithOne();
Jenis pengembalian untuk beberapa metode asinkron telah diubah dari Tugas ke ValueTask
Perilaku yang lama
Metode asinkron berikut sebelumnya mengembalikan Task<T>
:
DbContext.FindAsync()
DbSet.FindAsync()
DbContext.AddAsync()
DbSet.AddAsync()
ValueGenerator.NextValueAsync()
(dan kelas turunan)
Perilaku yang baru
Metode yang disebutkan di atas sekarang mengembalikan lebih ValueTask<T>
dari yang sama T
seperti sebelumnya.
Mengapa
Perubahan ini mengurangi jumlah alokasi timbunan yang dikeluarkan saat memanggil metode ini, meningkatkan performa umum.
Mitigasi
Aplikasi hanya menunggu API di atas hanya perlu dikompilasi ulang - tidak ada perubahan sumber yang diperlukan.
Penggunaan yang lebih kompleks (misalnya meneruskan yang dikembalikan Task
ke Task.WhenAny()
) biasanya mengharuskan ValueTask<T>
pengembalian dikonversi ke dengan Task<T>
memanggilnya AsTask()
.
Perhatikan bahwa ini meniadakan pengurangan alokasi yang dibawa oleh perubahan ini.
Anotasi Relational:TypeMapping sekarang hanya TypeMapping
Perilaku yang lama
Nama anotasi untuk anotasi pemetaan jenis adalah "Relational:TypeMapping".
Perilaku yang baru
Nama anotasi untuk anotasi pemetaan jenis sekarang adalah "TypeMapping".
Mengapa
Pemetaan jenis sekarang digunakan untuk lebih dari sekadar penyedia database relasional.
Mitigasi
Ini hanya akan memutus aplikasi yang mengakses pemetaan jenis secara langsung sebagai anotasi, yang tidak umum. Tindakan yang paling tepat untuk diperbaiki adalah menggunakan permukaan API untuk mengakses pemetaan jenis daripada menggunakan anotasi secara langsung.
ToTable pada jenis turunan melempar pengecualian
Perilaku yang lama
Sebelum EF Core 3.0, ToTable()
yang dipanggil pada jenis turunan akan diabaikan karena hanya strategi pemetaan warisan adalah TPH di mana ini tidak valid.
Perilaku yang baru
Dimulai dengan EF Core 3.0 dan sebagai persiapan untuk menambahkan dukungan TPT dan TPC dalam rilis selanjutnya, ToTable()
yang dipanggil pada jenis turunan sekarang akan melemparkan pengecualian untuk menghindari perubahan pemetaan yang tidak terduga di masa depan.
Mengapa
Saat ini tidak valid untuk memetakan jenis turunan ke tabel yang berbeda. Perubahan ini menghindari pemecahan di masa depan ketika menjadi hal yang valid untuk dilakukan.
Mitigasi
Hapus upaya apa pun untuk memetakan jenis turunan ke tabel lain.
ForSqlServerHasIndex diganti dengan HasIndex
Perilaku yang lama
Sebelum EF Core 3.0, ForSqlServerHasIndex().ForSqlServerInclude()
menyediakan cara untuk mengonfigurasi kolom yang digunakan dengan INCLUDE
.
Perilaku yang baru
Dimulai dengan EF Core 3.0, menggunakan Include
pada indeks sekarang didukung pada tingkat relasional.
Gunakan HasIndex().ForSqlServerInclude()
.
Mengapa
Perubahan ini dilakukan untuk mengonsolidasikan API untuk indeks dengan Include
ke dalam satu tempat untuk semua penyedia database.
Mitigasi
Gunakan API baru, seperti yang ditunjukkan di atas.
Perubahan API metadata
Perilaku yang baru
Properti berikut dikonversi ke metode ekstensi:
IEntityType.QueryFilter
->GetQueryFilter()
IEntityType.DefiningQuery
->GetDefiningQuery()
IProperty.IsShadowProperty
->IsShadowProperty()
IProperty.BeforeSaveBehavior
->GetBeforeSaveBehavior()
IProperty.AfterSaveBehavior
->GetAfterSaveBehavior()
Mengapa
Perubahan ini menyederhanakan implementasi antarmuka yang disebutkan di atas.
Mitigasi
Gunakan metode ekstensi baru.
Perubahan API Metadata khusus penyedia
Perilaku yang baru
Metode ekstensi khusus penyedia akan diratakan:
IProperty.Relational().ColumnName
->IProperty.GetColumnName()
IEntityType.SqlServer().IsMemoryOptimized
->IEntityType.IsMemoryOptimized()
PropertyBuilder.UseSqlServerIdentityColumn()
->PropertyBuilder.UseIdentityColumn()
Mengapa
Perubahan ini menyederhanakan implementasi metode ekstensi yang disebutkan di atas.
Mitigasi
Gunakan metode ekstensi baru.
EF Core tidak lagi mengirim pragma untuk penegakan SQLite FK
Perilaku yang lama
Sebelum EF Core 3.0, EF Core akan mengirim PRAGMA foreign_keys = 1
ketika koneksi ke SQLite dibuka.
Perilaku yang baru
Dimulai dengan EF Core 3.0, EF Core tidak lagi mengirim PRAGMA foreign_keys = 1
ketika koneksi ke SQLite dibuka.
Mengapa
Perubahan ini dilakukan karena EF Core menggunakan SQLitePCLRaw.bundle_e_sqlite3
secara default, yang pada gilirannya berarti bahwa penegakan FK diaktifkan secara default dan tidak perlu diaktifkan secara eksplisit setiap kali koneksi dibuka.
Mitigasi
Kunci asing diaktifkan secara default di SQLitePCLRaw.bundle_e_sqlite3, yang digunakan secara default untuk EF Core.
Untuk kasus lain, kunci asing dapat diaktifkan dengan menentukan Foreign Keys=True
di string koneksi Anda.
Microsoft.EntityFrameworkCore.Sqlite sekarang bergantung pada SQLitePCLRaw.bundle_e_sqlite3
Perilaku yang lama
Sebelum EF Core 3.0, EF Core menggunakan SQLitePCLRaw.bundle_green
.
Perilaku yang baru
Dimulai dengan EF Core 3.0, EF Core menggunakan SQLitePCLRaw.bundle_e_sqlite3
.
Mengapa
Perubahan ini dilakukan agar versi SQLite yang digunakan pada iOS konsisten dengan platform lain.
Mitigasi
Untuk menggunakan versi SQLite asli di iOS, konfigurasikan Microsoft.Data.Sqlite
untuk menggunakan bundel yang berbeda SQLitePCLRaw
.
Nilai guid sekarang disimpan sebagai TEXT di SQLite
Perilaku yang lama
Nilai guid sebelumnya disimpan sebagai nilai BLOB pada SQLite.
Perilaku yang baru
Nilai guid sekarang disimpan sebagai TEXT.
Mengapa
Format biner Guids tidak distandarkan. Menyimpan nilai sebagai TEXT membuat database lebih kompatibel dengan teknologi lain.
Mitigasi
Anda dapat memigrasikan database yang sudah ada ke format baru dengan menjalankan SQL seperti berikut ini.
UPDATE MyTable
SET GuidColumn = hex(substr(GuidColumn, 4, 1)) ||
hex(substr(GuidColumn, 3, 1)) ||
hex(substr(GuidColumn, 2, 1)) ||
hex(substr(GuidColumn, 1, 1)) || '-' ||
hex(substr(GuidColumn, 6, 1)) ||
hex(substr(GuidColumn, 5, 1)) || '-' ||
hex(substr(GuidColumn, 8, 1)) ||
hex(substr(GuidColumn, 7, 1)) || '-' ||
hex(substr(GuidColumn, 9, 2)) || '-' ||
hex(substr(GuidColumn, 11, 6))
WHERE typeof(GuidColumn) == 'blob';
Di EF Core, Anda juga dapat terus menggunakan perilaku sebelumnya dengan mengonfigurasi pengonversi nilai pada properti ini.
modelBuilder
.Entity<MyEntity>()
.Property(e => e.GuidProperty)
.HasConversion(
g => g.ToByteArray(),
b => new Guid(b));
Microsoft.Data.Sqlite tetap mampu membaca nilai Guid dari kolom BLOB dan TEXT; namun, karena format default untuk parameter dan konstanta telah berubah, Anda mungkin perlu mengambil tindakan untuk sebagian besar skenario yang melibatkan Guid.
Nilai karakter sekarang disimpan sebagai TEKS di SQLite
Perilaku yang lama
Nilai karakter sebelumnya disimpan sebagai nilai BILANGAN BULAT pada SQLite. Misalnya, nilai karakter A disimpan sebagai nilai bilangan bulat 65.
Perilaku yang baru
Nilai karakter sekarang disimpan sebagai TEXT.
Mengapa
Menyimpan nilai karena TEXT lebih alami dan membuat database lebih kompatibel dengan teknologi lain.
Mitigasi
Anda dapat memigrasikan database yang sudah ada ke format baru dengan menjalankan SQL seperti berikut ini.
UPDATE MyTable
SET CharColumn = char(CharColumn)
WHERE typeof(CharColumn) = 'integer';
Di EF Core, Anda juga dapat terus menggunakan perilaku sebelumnya dengan mengonfigurasi pengonversi nilai pada properti ini.
modelBuilder
.Entity<MyEntity>()
.Property(e => e.CharProperty)
.HasConversion(
c => (long)c,
i => (char)i);
Microsoft.Data.Sqlite juga tetap mampu membaca nilai karakter dari kolom INTEGER dan TEXT, sehingga skenario tertentu mungkin tidak memerlukan tindakan apa pun.
ID migrasi sekarang dihasilkan menggunakan kalender budaya invariant
Perilaku yang lama
ID migrasi dibuat secara tidak sengaja menggunakan kalender budaya saat ini.
Perilaku yang baru
ID migrasi sekarang selalu dihasilkan menggunakan kalender budaya invariant (Gregorian).
Mengapa
Urutan migrasi penting saat memperbarui database atau menyelesaikan konflik penggabungan. Menggunakan kalender invarian menghindari masalah pemesanan yang dapat diakibatkan oleh anggota tim yang memiliki kalender sistem yang berbeda.
Mitigasi
Perubahan ini mempengaruhi siapa pun yang menggunakan kalender non-Gregorian di mana tahun lebih besar dari kalender Gregorian (seperti kalender Buddha Thailand). ID migrasi yang ada perlu diperbarui sehingga migrasi baru diurutkan setelah migrasi yang ada.
ID migrasi dapat ditemukan di atribut Migrasi dalam file perancang migrasi.
[DbContext(typeof(MyDbContext))]
-[Migration("25620318122820_MyMigration")]
+[Migration("20190318122820_MyMigration")]
partial class MyMigration
{
Tabel Riwayat Migrasi juga perlu diperbarui.
UPDATE __EFMigrationsHistory
SET MigrationId = CONCAT(LEFT(MigrationId, 4) - 543, SUBSTRING(MigrationId, 4, 150))
UseRowNumberForPaging telah dihapus
Perilaku yang lama
Sebelum EF Core 3.0, UseRowNumberForPaging
dapat digunakan untuk menghasilkan SQL untuk halaman yang kompatibel dengan SQL Server 2008.
Perilaku yang baru
Dimulai dengan EF Core 3.0, EF hanya akan menghasilkan SQL untuk halaman yang hanya kompatibel dengan versi SQL Server yang lebih baru.
Mengapa
Kami membuat perubahan ini karena SQL Server 2008 bukan lagi produk yang didukung dan memperbarui fitur ini untuk bekerja dengan perubahan kueri yang dibuat di EF Core 3.0 adalah pekerjaan yang signifikan.
Mitigasi
Sebaiknya perbarui ke versi SQL Server yang lebih baru, atau gunakan tingkat kompatibilitas yang lebih tinggi, sehingga SQL yang dihasilkan didukung. Meskipun demikian, jika Anda tidak dapat melakukan ini, silakan komentari masalah pelacakan dengan detail. Kami dapat mengunjungi kembali keputusan ini berdasarkan umpan balik.
Info/metadata ekstensi telah dihapus dari IDbContextOptionsExtension
Perilaku yang lama
IDbContextOptionsExtension
berisi metode untuk menyediakan metadata tentang ekstensi.
Perilaku yang baru
Metode ini telah dipindahkan ke kelas dasar abstrak baru DbContextOptionsExtensionInfo
, yang dikembalikan dari properti baru IDbContextOptionsExtension.Info
.
Mengapa
Selama rilis dari 2.0 hingga 3.0 kami perlu menambahkan atau mengubah metode ini beberapa kali. Memecahnya menjadi kelas dasar abstrak baru akan memudahkan untuk membuat perubahan semacam ini tanpa merusak ekstensi yang ada.
Mitigasi
Perbarui ekstensi untuk mengikuti pola baru.
Contoh ditemukan dalam banyak implementasi IDbContextOptionsExtension
untuk berbagai jenis ekstensi dalam kode sumber EF Core.
LogQueryPossibleExceptionWithAggregateOperator telah diganti namanya
Ubah
RelationalEventId.LogQueryPossibleExceptionWithAggregateOperator
telah diganti namanya menjadi RelationalEventId.LogQueryPossibleExceptionWithAggregateOperatorWarning
.
Mengapa
Menyelaraskan penamaan peristiwa peringatan ini dengan semua peristiwa peringatan lainnya.
Mitigasi
Gunakan nama baru. (Perhatikan bahwa nomor ID peristiwa tidak berubah.)
Mengklarifikasi API untuk nama batasan kunci asing
Perilaku yang lama
Sebelum EF Core 3.0, nama batasan kunci asing disebut sebagai "nama" saja. Contohnya:
var constraintName = myForeignKey.Name;
Perilaku yang baru
Dimulai dengan EF Core 3.0, nama batasan kunci asing sekarang disebut sebagai "nama batasan". Contohnya:
var constraintName = myForeignKey.ConstraintName;
Mengapa
Perubahan ini membawa konsistensi untuk penamaan di area ini, dan juga mengklarifikasi bahwa ini adalah nama batasan kunci asing, dan bukan nama kolom atau properti tempat kunci asing didefinisikan.
Mitigasi
Gunakan nama baru.
IRelationalDatabaseCreator.HasTables/HasTablesAsync telah dipublikasikan
Perilaku yang lama
Sebelum EF Core 3.0, metode ini dilindungi.
Perilaku yang baru
Dimulai dengan EF Core 3.0, metode ini bersifat publik.
Mengapa
Metode ini digunakan oleh EF untuk menentukan apakah database dibuat tetapi kosong. Ini juga dapat berguna dari luar EF saat menentukan apakah akan menerapkan migrasi atau tidak.
Mitigasi
Ubah aksesibilitas penimpaan apa pun.
Microsoft.EntityFrameworkCore.Design sekarang menjadi paket DevelopmentDependency
Perilaku yang lama
Sebelum EF Core 3.0, Microsoft.EntityFrameworkCore.Design adalah paket NuGet biasa yang perakitannya dapat direferensikan oleh proyek yang bergantung padanya.
Perilaku yang baru
Dimulai dengan EF Core 3.0, ini adalah paket DevelopmentDependency. Ini berarti bahwa dependensi tidak akan mengalir secara transitif ke proyek lain, dan Anda tidak dapat lagi, secara default, mereferensikan perakitannya.
Mengapa
Paket ini hanya dimaksudkan untuk digunakan pada waktu desain. Aplikasi yang disebarkan tidak boleh mereferensikannya. Membuat paket menjadi DevelopmentDependency memperkuat rekomendasi ini.
Mitigasi
Jika Anda perlu mereferensikan paket ini untuk mengambil alih perilaku waktu desain EF Core, maka Anda dapat memperbarui metadata item PackageReference dalam proyek Anda.
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.0.0">
<PrivateAssets>all</PrivateAssets>
<!-- Remove IncludeAssets to allow compiling against the assembly -->
<!--<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
</PackageReference>
Jika paket direferensikan secara transitif melalui Microsoft.EntityFrameworkCore.Tools, Anda harus menambahkan PackageReference eksplisit ke paket untuk mengubah metadatanya. Referensi eksplisit seperti itu harus ditambahkan ke proyek apa pun di mana jenis dari paket diperlukan.
SQLitePCL.raw diperbarui ke versi 2.0.0
Perilaku yang lama
Microsoft.EntityFrameworkCore.Sqlite sebelumnya bergantung pada versi 1.1.12 SQLitePCL.raw.
Perilaku yang baru
Kami telah memperbarui paket kami agar bergantung pada versi 2.0.0.
Mengapa
Versi 2.0.0 dari SQLitePCL.raw menargetkan .NET Standard 2.0. Ini sebelumnya menargetkan .NET Standard 1.1 yang memerlukan penutupan besar paket transitif untuk bekerja.
Mitigasi
SQLitePCL.raw versi 2.0.0 menyertakan beberapa perubahan yang melanggar. Lihat catatan rilis untuk detail lebih lanjut.
NetTopologySuite diperbarui ke versi 2.0.0
Perilaku yang lama
Paket spasial sebelumnya bergantung pada NetTopologySuite versi 1.15.1.
Perilaku yang baru
Kami telah memperbarui paket kami agar bergantung pada versi 2.0.0.
Mengapa
NetTopologySuite versi 2.0.0 bertujuan untuk mengatasi beberapa masalah kegunaan yang dihadapi oleh pengguna EF Core.
Mitigasi
NetTopologySuite versi 2.0.0 mencakup beberapa perubahan yang melanggar. Lihat catatan rilis untuk detail lebih lanjut.
Microsoft.Data.SqlClient digunakan alih-alih System.Data.SqlClient
Perilaku yang lama
Microsoft.EntityFrameworkCore.SqlServer sebelumnya bergantung pada System.Data.SqlClient.
Perilaku yang baru
Kami telah memperbarui paket agar bergantung pada Microsoft.Data.SqlClient.
Mengapa
Microsoft.Data.SqlClient adalah driver akses data unggulan untuk SQL Server ke depannya, dan System.Data.SqlClient tidak lagi menjadi fokus pengembangan. Beberapa fitur penting, seperti Always Encrypted, hanya tersedia di Microsoft.Data.SqlClient.
Mitigasi
Jika kode Anda mengambil dependensi langsung pada System.Data.SqlClient, Anda harus mengubahnya untuk mereferensikan Microsoft.Data.SqlClient sebagai gantinya; karena kedua paket mempertahankan tingkat kompatibilitas API yang sangat tinggi, ini seharusnya hanya menjadi paket sederhana dan perubahan namespace layanan.
Beberapa hubungan referensi mandiri ambigu harus dikonfigurasi
Perilaku yang lama
Jenis entitas dengan beberapa properti navigasi arah uni-arah yang merujuk sendiri dan FK yang cocok salah dikonfigurasi sebagai hubungan tunggal. Contohnya:
public class User
{
public Guid Id { get; set; }
public User CreatedBy { get; set; }
public User UpdatedBy { get; set; }
public Guid CreatedById { get; set; }
public Guid? UpdatedById { get; set; }
}
Perilaku yang baru
Skenario ini sekarang terdeteksi dalam pembuatan model dan pengecualian dilemparkan yang menunjukkan bahwa model ambigu.
Mengapa
Model yang dihasilkan ambigu dan kemungkinan akan salah untuk kasus ini.
Mitigasi
Gunakan konfigurasi lengkap hubungan. Contohnya:
modelBuilder
.Entity<User>()
.HasOne(e => e.CreatedBy)
.WithMany();
modelBuilder
.Entity<User>()
.HasOne(e => e.UpdatedBy)
.WithMany();
DbFunction.Schema menjadi string null atau kosong mengonfigurasinya agar berada dalam skema default model
Perilaku yang lama
DbFunction yang dikonfigurasi dengan skema sebagai string kosong diperlakukan sebagai fungsi bawaan tanpa skema. Misalnya kode berikut akan memetakan DatePart
fungsi CLR ke DATEPART
fungsi bawaan di SqlServer.
[DbFunction("DATEPART", Schema = "")]
public static int? DatePart(string datePartArg, DateTime? date) => throw new Exception();
Perilaku yang baru
Semua pemetaan DbFunction dianggap dipetakan ke fungsi yang ditentukan pengguna. Oleh karena itu, nilai string kosong akan menempatkan fungsi di dalam skema default untuk model. Yang bisa menjadi skema yang dikonfigurasi secara eksplisit melalui API modelBuilder.HasDefaultSchema()
yang fasih atau dbo
sebaliknya.
Mengapa
Skema yang sebelumnya kosong adalah cara untuk memperlakukan fungsi tersebut bawaan tetapi logika itu hanya berlaku untuk SqlServer di mana fungsi bawaan bukan milik skema apa pun.
Mitigasi
Konfigurasikan terjemahan DbFunction secara manual untuk memetakannya ke fungsi bawaan.
modelBuilder
.HasDbFunction(typeof(MyContext).GetMethod(nameof(MyContext.DatePart)))
.HasTranslation(args => SqlFunctionExpression.Create("DatePart", args, typeof(int?), null));
EF Core 3.0 menargetkan .NET Standard 2.1 daripada .NET Standard 2.0 Reverted
EF Core 3.0 menargetkan .NET Standard 2.1, yang merupakan perubahan mencolok yang mengecualikan aplikasi .NET Framework. EF Core 3.1 mengembalikan ini dan menargetkan .NET Standard 2.0 lagi.
Eksekusi kueri dicatat di tingkat Debug Dikembalikan
Kami mengembalikan perubahan ini karena konfigurasi baru di EF Core 3.0 memungkinkan tingkat log untuk setiap peristiwa ditentukan oleh aplikasi. Misalnya, untuk mengalihkan pengelogan SQL ke Debug
, secara eksplisit mengonfigurasi tingkat di OnConfiguring
atau AddDbContext
:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(connectionString)
.ConfigureWarnings(c => c.Log((RelationalEventId.CommandExecuting, LogLevel.Debug)));