EF Core 8'de (EF8) hataya neden olan değişiklikler
Bu sayfada, EF Core 7'den EF Core 8'e güncelleştirilen mevcut uygulamaların bozulmasına neden olabilecek API ve davranış değişiklikleri belgelenmiştir. EF Core'un önceki bir sürümünden güncelleştirme yapıyorsanız, önceki hataya neden olan değişiklikleri gözden geçirmeyi unutmayın:
Hedef Çerçeve
EF Core 8, .NET 8'i hedefler. Eski .NET, .NET Core ve .NET Framework sürümlerini hedefleyen uygulamaların .NET 8'i hedefleyecek şekilde güncelleştirilmeleri gerekir.
Özet
Yüksek etkili değişiklikler
Contains
LINQ sorguları eski SQL Server sürümlerinde çalışmayı durdurabilir
Eski davranış
Contains
Daha önce işleç parametreli değer listesiyle LINQ sorgularında kullanıldığında EF, verimsiz olan ancak tüm SQL Server sürümlerinde çalışan SQL'i oluşturmuştu.
Yeni davranış
EF Core 8.0'dan başlayarak, EF artık daha verimli, ancak SQL Server 2014 ve altında desteklenmeyen SQL oluşturur.
Daha yeni SQL Server sürümlerinin daha eski bir uyumluluk düzeyiyle yapılandırılarak yeni SQL ile uyumsuz hale getirilebileceğini unutmayın. Bu durum, eski uyumluluk düzeyini taşıyan önceki bir şirket içi SQL Server örneğinden geçirilen bir Azure SQL veritabanıyla da oluşabilir.
Neden?
EF Core tarafından oluşturulan önceki SQL, Contains
parametreli değerleri SQL'e sabit olarak eklemiş. Örneğin, aşağıdaki LINQ sorgusu:
var names = new[] { "Blog1", "Blog2" };
var blogs = await context.Blogs
.Where(b => names.Contains(b.Name))
.ToArrayAsync();
... aşağıdaki SQL'e çevrilebilir:
SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] IN (N'Blog1', N'Blog2')
Sql'e sabit değerlerin eklenmesi, sorgu planını önbelleğe almayı bozan ve diğer sorguların gereksiz çıkarmalarına neden olan birçok performans sorununa neden olur. Yeni EF Core 8.0 çevirisi, değerleri JSON dizisi olarak aktarmak için SQL Server OPENJSON
işlevini kullanır. Bu, önceki teknikte bulunan performans sorunlarını çözer; ancak işlev OPENJSON
SQL Server 2014 ve altında kullanılamaz.
Bu değişiklik hakkında daha fazla bilgi için bu blog gönderisini inceleyin.
Risk Azaltıcı Etkenler
Veritabanınız SQL Server 2016 (13.x) veya daha yeniyse ya da Azure SQL kullanıyorsanız aşağıdaki komutu kullanarak veritabanınızın yapılandırılmış uyumluluk düzeyini denetleyin:
SELECT name, compatibility_level FROM sys.databases;
Uyumluluk düzeyi 130'un altındaysa (SQL Server 2016), bunu daha yeni bir değerle (belgeler) değiştirmeyi göz önünde bulundurun.
Aksi takdirde, veritabanı sürümünüz SQL Server 2016'dan gerçekten daha eskiyse veya bazı nedenlerden dolayı değiştiremeyeceğiniz eski bir uyumluluk düzeyine ayarlanmışsa EF Core'ı aşağıdaki gibi daha eski, daha az verimli SQL'e geri dönecek şekilde yapılandırın:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer(@"<CONNECTION STRING>", o => o.UseCompatibilityLevel(120));
JSON'daki sabit listeleri varsayılan olarak dizeler yerine int olarak depolanır
Eski davranış
EF7'de, JSON ile eşlenen sabit listeleri varsayılan olarak JSON belgesinde dize değerleri olarak depolanır.
Yeni davranış
EF Core 8.0'dan başlayarak, EF varsayılan olarak, sabit listeleri JSON belgesindeki tamsayı değerleriyle eşler.
Neden?
EF her zaman varsayılan olarak ilişkisel veritabanlarındaki sayısal bir sütuna eşlenmiş sabit listeleri vardır. EF, JSON değerlerinin sütunlardan ve parametrelerden gelen değerlerle etkileşimde bulunduğu sorguları desteklediğinden, JSON'daki değerlerin JSON olmayan sütundaki değerlerle eşleşmesi önemlidir.
Risk Azaltıcı Etkenler
Dizeleri kullanmaya devam etmek için enum özelliğini bir dönüştürme ile yapılandırın. Örneğin:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().Property(e => e.Status).HasConversion<string>();
}
Veya sabit listesi türünün tüm özellikleri için:
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Properties<StatusEnum>().HaveConversion<string>();
}
Orta düzeyde etki eden değişiklikler
SQL Server date
ve time
şimdi .NET DateOnly
ve TimeOnly
Eski davranış
Daha önce, SQL Server veritabanının time
veya sütunlarının date
iskelesini oluştururken EF, ve TimeSpantürlerine DateTime sahip varlık özellikleri oluştururdu.
Yeni davranış
EF Core 8.0 date
ile başlayarak ve time
olarak DateOnly TimeOnlyyapı iskelesi oluşturulur.
Neden?
DateOnly ve TimeOnly .NET 6.0 ile kullanıma sunulmuştur ve veritabanı tarih ve saat türlerini eşlemek için mükemmel bir eşleşmedir. DateTime özellikle kullanılmayan bir zaman bileşeni içerir ve bunu date
ile eşlerken karışıklığa neden olabilir ve TimeSpan bir olayın gerçekleştiği günün saati yerine bir zaman aralığını (büyük olasılıkla günler de dahil) temsil eder. Yeni türlerin kullanılması hataları ve karışıklığı önler ve amacın netliğini sağlar.
Risk Azaltıcı Etkenler
Bu değişiklik yalnızca veritabanlarını düzenli olarak bir EF kod modeline ("önce veritabanı" akışı) yeniden iskeleleyen kullanıcıları etkiler.
Kodunuzu yeni iskeleyi DateOnly ve TimeOnly türleri kullanacak şekilde değiştirerek bu değişikliğe tepki vermeniz önerilir. Ancak bu mümkün değilse, yapı iskelesi şablonlarını düzenleyerek önceki eşlemeye dönebilirsiniz. Bunu yapmak için şablonları bu sayfada açıklandığı gibi ayarlayın. Ardından dosyayı düzenleyin EntityType.t4
, varlık özelliklerinin nerede oluşturulduğunu bulun (araması property.ClrType
yapın) ve kodu aşağıdaki şekilde değiştirin:
var clrType = property.GetColumnType() switch
{
"date" when property.ClrType == typeof(DateOnly) => typeof(DateTime),
"date" when property.ClrType == typeof(DateOnly?) => typeof(DateTime?),
"time" when property.ClrType == typeof(TimeOnly) => typeof(TimeSpan),
"time" when property.ClrType == typeof(TimeOnly?) => typeof(TimeSpan?),
_ => property.ClrType
};
usings.AddRange(code.GetRequiredUsings(clrType));
var needsNullable = Options.UseNullableReferenceTypes && property.IsNullable && !clrType.IsValueType;
var needsInitializer = Options.UseNullableReferenceTypes && !property.IsNullable && !clrType.IsValueType;
#>
public <#= code.Reference(clrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
<#
Veritabanı tarafından oluşturulan değere sahip Boole sütunları artık null atanabilir olarak yapı iskelesi oluşturulmuyor
Eski davranış
Daha önce, veritabanı varsayılan kısıtlaması olan null atanamayan bool
sütunlar, boş değer atanabilir bool?
özellikler olarak iskeleye alınıyordu.
Yeni davranış
EF Core 8.0'dan başlayarak, boş değer atanamayan bool
sütunlar her zaman boş değer atanamayan özellikler olarak iskelelenir.
Neden?
Bir bool
özelliğin değeri, clr varsayılanı olan false
değerine sahipse veritabanına gönderilmez. Veritabanı sütun için varsayılan değerine true
sahipse, özelliğinin false
değeri olsa bile veritabanındaki değer olarak true
biter. Ancak EF8'de, bir özelliğin değeri olup olmadığını belirlemek için kullanılan sentinel değiştirilebilir. Bu, veritabanı tarafından oluşturulan değeri true
olan özellikler için bool
otomatik olarak yapılır. Bu, artık özelliklerin null atanabilir olarak iskelesinin oluşturulmasının gerekli olmadığı anlamına gelir.
Risk Azaltıcı Etkenler
Bu değişiklik yalnızca veritabanlarını düzenli olarak bir EF kod modeline ("önce veritabanı" akışı) yeniden iskeleleyen kullanıcıları etkiler.
Kodunuzu null atanamaz bool özelliğini kullanacak şekilde değiştirerek bu değişikliğe tepki vermeniz önerilir. Ancak bu mümkün değilse, yapı iskelesi şablonlarını düzenleyerek önceki eşlemeye dönebilirsiniz. Bunu yapmak için şablonları bu sayfada açıklandığı gibi ayarlayın. Ardından dosyayı düzenleyin EntityType.t4
, varlık özelliklerinin nerede oluşturulduğunu bulun (araması property.ClrType
yapın) ve kodu aşağıdaki şekilde değiştirin:
#>
var propertyClrType = property.ClrType != typeof(bool)
|| (property.GetDefaultValueSql() == null && property.GetDefaultValue() != null)
? property.ClrType
: typeof(bool?);
#>
public <#= code.Reference(propertyClrType) #><#= needsNullable ? "?" : "" #> <#= property.Name #> { get; set; }<#= needsInitializer ? " = null!;" : "" #>
<#
<#
Düşük etkili değişiklikler
SQLite Math
yöntemleri artık SQL'e çevrilir
Eski Davranış
Daha önce yalnızca üzerindeki Math
Abs, Max, Min ve Round yöntemleri SQL'e çevrilmişti. Diğer tüm üyeler, sorgunun son Select ifadesinde görünürlerse istemcide değerlendirilir.
Yeni davranış
EF Core 8.0'da, karşılık gelen SQLite matematik işlevlerine sahip tüm Math
yöntemler SQL'e çevrilir.
Bu matematik işlevleri varsayılan olarak sağladığımız yerel SQLite kitaplığında etkinleştirilmiştir (SQLitePCLRaw.bundle_e_sqlite3 NuGet paketine bağımlılığımız aracılığıyla). Bunlar, SQLitePCLRaw.bundle_e_sqlcipher tarafından sağlanan kitaplıkta da etkinleştirilmiştir. Bu kitaplıklardan birini kullanıyorsanız, uygulamanız bu değişiklikten etkilenmemelidir.
Ancak, diğer yollarla yerel SQLite kitaplığını içeren uygulamaların matematik işlevlerini etkinleştirmeme olasılığı vardır. Bu gibi durumlarda Math
yöntemler SQL'e çevrilir ve yürütülürken böyle bir işlev hatasıyla karşılaşmaz.
Neden?
SQLite, 3.35.0 sürümüne yerleşik matematik işlevleri ekledi. Varsayılan olarak devre dışı bırakılmış olsalar da, EF Core SQLite sağlayıcımızda onlar için varsayılan çeviriler sağlamaya karar verdik.
Ayrıca Eric Sink ile SQLitePCLRaw projesi üzerinde işbirliği yaparak projenin bir parçası olarak sağlanan tüm yerel SQLite kitaplıklarında matematik işlevlerini etkinleştirdik.
Risk Azaltıcı Etkenler
Kesmeleri düzeltmenin en basit yolu, mümkün olduğunda derleme zamanı seçeneğini belirterek matematik işlevini etkinleştirmek SQLITE_ENABLE_MATH_FUNCTIONS için yerel SQLite kitaplığıdır.
Yerel kitaplığın derlenmesini denetlemezseniz, Microsoft.Data.Sqlite API'lerini kullanarak çalışma zamanında işlevleri kendiniz oluşturarak da kesmeleri düzeltebilirsiniz.
sqliteConnection
.CreateFunction<double, double, double>(
"pow",
Math.Pow,
isDeterministic: true);
Alternatif olarak, Select ifadesini ile AsEnumerable
ayrılmış iki bölüme bölerek istemci değerlendirmesini zorlayabilirsiniz.
// Before
var query = dbContext.Cylinders
.Select(
c => new
{
Id = c.Id
// May throw "no such function: pow"
Volume = Math.PI * Math.Pow(c.Radius, 2) * c.Height
});
// After
var query = dbContext.Cylinders
// Select the properties you'll need from the database
.Select(
c => new
{
c.Id,
c.Radius,
c.Height
})
// Switch to client-eval
.AsEnumerable()
// Select the final results
.Select(
c => new
{
Id = c.Id,
Volume = Math.PI * Math.Pow(c.Radius, 2) * c.Height
});
ITypeBase bazı API'lerde IEntityType'ın yerini alır
Eski davranış
Daha önce tüm eşlenmiş yapısal türler varlık türleriydi.
Yeni davranış
EF8'de karmaşık türlerin kullanıma sunulmasıyla birlikte, daha önce kullanılmış IEntityType
olan bazı API'ler artık api'lerin varlık veya karmaşık türlerle kullanılabilmesi için kullanılır ITypeBase
. Buna aşağıdakiler dahildir:
IProperty.DeclaringEntityType
artık kullanım dışıdır veIProperty.DeclaringType
bunun yerine kullanılmalıdır.IEntityTypeIgnoredConvention
artık kullanım dışıdır veITypeIgnoredConvention
bunun yerine kullanılmalıdır.IValueGeneratorSelector.Select
şimdi olabilir ancak olması gerekmeyen birIEntityType
kabul ederITypeBase
.
Neden?
EF8'de karmaşık türlerin kullanıma sunulmasıyla birlikte, bu API'ler veya IComplexType
ile IEntityType
kullanılabilir.
Risk Azaltıcı Etkenler
Eski API'ler engellenir, ancak EF10'a kadar kaldırılmaz. Kod, en kısa sürede yeni API'leri kullanacak şekilde güncelleştirilmelidir.
ValueConverter ve ValueComparer ifadelerinin derlenen model için genel API'leri kullanması gerekir
Eski davranış
ValueConverter
Daha önce ve ValueComparer
tanımları derlenen modele dahil edilmediğinden rastgele kod içerebiliyordu.
Yeni davranış
EF artık ve ValueComparer
nesnelerinden ValueConverter
ifadeleri ayıklar ve derlenen modele bu C# değerlerini ekler. Bu, bu ifadelerin yalnızca genel API kullanması gerektiği anlamına gelir.
Neden?
EF ekibi, gelecekte EF Core'un AOT ile kullanılmasını desteklemek için derlenen modele aşamalı olarak daha fazla yapı taşıyor.
Risk Azaltıcı Etkenler
Karşılaştırıcı tarafından kullanılan API'leri genel yapın. Örneğin, şu basit dönüştürücüye göz atın:
public class MyValueConverter : ValueConverter<string, byte[]>
{
public MyValueConverter()
: base(v => ConvertToBytes(v), v => ConvertToString(v))
{
}
private static string ConvertToString(byte[] bytes)
=> ""; // ... TODO: Conversion code
private static byte[] ConvertToBytes(string chars)
=> Array.Empty<byte>(); // ... TODO: Conversion code
}
Bu dönüştürücüsü EF8 ConvertToString
ile derlenmiş bir modelde kullanmak için ve ConvertToBytes
yöntemleri genele açık hale getirilmelidir. Örneğin:
public class MyValueConverter : ValueConverter<string, byte[]>
{
public MyValueConverter()
: base(v => ConvertToBytes(v), v => ConvertToString(v))
{
}
public static string ConvertToString(byte[] bytes)
=> ""; // ... TODO: Conversion code
public static byte[] ConvertToBytes(string chars)
=> Array.Empty<byte>(); // ... TODO: Conversion code
}
ExcludeFromMigrations artık TPC hiyerarşisindeki diğer tabloları dışlamıyor
Eski davranış
Daha önce, TPC hiyerarşisindeki bir tabloda kullanmak ExcludeFromMigrations
, hiyerarşideki diğer tabloları da dışlardı.
Yeni davranış
EF Core 8.0'dan başlayarak diğer ExcludeFromMigrations
tabloları etkilemez.
Neden?
Eski davranış bir hataydı ve geçişlerin projeler arasında hiyerarşileri yönetmek için kullanılmasını engelledi.
Risk Azaltıcı Etkenler
Dışlanması gereken diğer tüm tablolarda açıkça kullanın ExcludeFromMigrations
.
Gölge olmayan tamsayı anahtarları Cosmos belgelerinde kalıcı hale getiriliyor
Eski davranış
Daha önce, sentezlenmiş anahtar özelliği ölçütleri ile eşleşen gölge olmayan tamsayı özellikleri JSON belgesinde kalıcı hale getirilmeyecek, ancak çıkışta yeniden sentezlenmişti.
Yeni davranış
EF Core 8.0'dan itibaren bu özellikler kalıcı hale getiriliyor.
Neden?
Eski davranış bir hataydı ve sentezlenen anahtar ölçütleriyle eşleşen özelliklerin Cosmos'ta kalıcı olmasını engelledi.
Risk Azaltıcı Etkenler
Değeri kalıcı olmaması gerekiyorsa özelliği modelin dışında tutun.
Ayrıca, AppContext anahtarını olarak ayarlayarak Microsoft.EntityFrameworkCore.Issue31664
bu davranışı tamamen devre dışı bırakabilirsiniz. Daha fazla ayrıntı için bkz. Kitaplık tüketicileri için AppContext.true
AppContext.SetSwitch("Microsoft.EntityFrameworkCore.Issue31664", isEnabled: true);
İlişkisel model derlenmiş modelde oluşturulur
Eski davranış
Daha önce ilişkisel model, derlenmiş bir model kullanılırken bile çalışma zamanında hesaplandı.
Yeni davranış
EF Core 8.0'dan başlayarak ilişkisel model, oluşturulan derlenmiş modelin bir parçasıdır. Ancak, özellikle büyük modeller için oluşturulan dosya derlenemiyor olabilir.
Neden?
Bu, başlangıç süresini daha da geliştirmek için yapıldı.
Risk Azaltıcı Etkenler
Oluşturulan *ModelBuilder.cs
dosyayı düzenleyin ve yönteminin CreateRelationalModel()
yanı sıra satırını AddRuntimeAnnotation("Relational:RelationalModel", CreateRelationalModel());
da kaldırın.
yapı iskelesi farklı gezinti adları oluşturabilir
Eski davranış
Daha önce var olan bir veritabanından ve DbContext
varlık türlerinin iskelesini oluştururken, ilişkiler için gezinti adları bazen birden çok yabancı anahtar sütun adının ortak ön ekinden türetilirdi.
Yeni davranış
EF Core 8.0'dan başlayarak, bileşik yabancı anahtardan sütun adlarının ortak ön ekleri artık gezinti adları oluşturmak için kullanılmaz.
Neden?
Bu, bazen gibi S
Student_
çok kötü adlar oluşturan, hatta yalnızca _
gibi belirsiz bir adlandırma kuralıdır. Bu kural olmadan, garip adlar artık oluşturulmaz ve gezintiler için adlandırma kuralları da daha basit hale getirildiğinden, hangi adların oluşturulacağını anlamak ve tahmin etmek daha kolay hale gelir.
Risk Azaltıcı Etkenler
EF Core Power Tools'un gezintileri eski yöntemle oluşturmaya devam etme seçeneği vardır. Alternatif olarak, oluşturulan kod T4 şablonları kullanılarak tamamen özelleştirilebilir. Bu, yapı iskelesi ilişkilerinin yabancı anahtar özelliklerini örnek almak ve ihtiyacınız olan gezinti adlarını oluşturmak için kodunuza uygun olan kuralı kullanmak için kullanılabilir.
Ayrımcıların artık uzunluk üstleri
Eski davranış
Daha önce, TPH devralma eşlemesi için oluşturulan ayrımcı sütunlar SQL Server/Azure SQL'de veya diğer veritabanlarında eşdeğer ilişkisiz dize türünde olarak nvarchar(max)
yapılandırıldı.
Yeni davranış
EF Core 8.0'dan başlayarak, ayırıcı sütunlar bilinen tüm ayrımcı değerleri kapsayan maksimum uzunlukla oluşturulur. EF, bu değişikliği yapmak için bir geçiş oluşturur. Ancak, ayrımcı sütun bir şekilde kısıtlanmışsa (örneğin, bir dizinin parçası olarak) Migrations AlterColumn
tarafından oluşturulan başarısız olabilir.
Neden?
nvarchar(max)
tüm olası değerlerin uzunlukları bilindiğinde sütunlar verimsiz ve gereksizdir.
Risk Azaltıcı Etkenler
Sütun boyutu açıkça ilişkisiz yapılabilir:
modelBuilder.Entity<Foo>()
.Property<string>("Discriminator")
.HasMaxLength(-1);
SQL Server anahtar değerleri büyük/küçük harfe duyarsız olarak karşılaştırılır
Eski davranış
Daha önce SQL Server/Azure SQL veritabanı sağlayıcılarıyla dize anahtarları olan varlıkları izlerken, anahtar değerleri varsayılan .NET büyük/küçük harfe duyarlı sıralı karşılaştırıcı kullanılarak karşılaştırıldı.
Yeni davranış
EF Core 8.0'dan başlayarak, SQL Server/Azure SQL dize anahtarı değerleri varsayılan .NET büyük/küçük harfe duyarlı olmayan sıralı karşılaştırıcı kullanılarak karşılaştırılır.
Neden?
Varsayılan olarak, SQL Server eşleşmeler için yabancı anahtar değerlerini asıl anahtar değerleriyle karşılaştırırken büyük/küçük harfe duyarsız karşılaştırmalar kullanır. Bu, EF büyük/küçük harfe duyarlı karşılaştırmalar kullandığında, gerektiğinde yabancı anahtarı bir asıl anahtara bağlamayabileceği anlamına gelir.
Risk Azaltıcı Etkenler
Büyük/küçük harfe duyarlı karşılaştırmalar, özel ValueComparer
ayarlanarak kullanılabilir. Örnek:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var comparer = new ValueComparer<string>(
(l, r) => string.Equals(l, r, StringComparison.Ordinal),
v => v.GetHashCode(),
v => v);
modelBuilder.Entity<Blog>()
.Property(e => e.Id)
.Metadata.SetValueComparer(comparer);
modelBuilder.Entity<Post>(
b =>
{
b.Property(e => e.Id).Metadata.SetValueComparer(comparer);
b.Property(e => e.BlogId).Metadata.SetValueComparer(comparer);
});
}