EF Core 6,0 ' deki yenilikler
EF Core 6,0 NuGetgönderildi. Bu sayfa, bu yayında sunulan ilginç değişikliklere genel bakış içerir.
İpucu
GitHub örnek kodu indirerekaşağıda gösterilen örneklere çalıştırabilir ve hata ayıklaması yapabilirsiniz.
SQL Server zamana bağlı tablolar
GitHub sorunu: #4693.
SQL Server zamana bağlı tablolar, veriler güncelleştirildikten veya silindikten sonra bile, bir tabloda depolanan tüm verileri otomatik olarak takip edin. Bu, ana tabloya her değişiklik yapıldığında, zaman damgası alınan geçmiş verilerinin depolandığı paralel bir "geçmiş tablosu" oluşturarak elde edilir. Bu, denetim veya geri yükleme gibi geçmiş verilerin sorgulanmasını (örneğin, yanlışlıkla ya da silinmeden sonra kurtarma için) sağlar.
EF Core Şu anda şunları destekler:
- Geçişler kullanılarak zamana bağlı tabloların oluşturulması
- Mevcut tabloların, geçiş kullanarak yeniden zamana bağlı tablolara dönüştürülmesi
- Geçmiş verileri sorgulama
- Geçmişteki bir noktadan verileri geri yükleme
Zamana bağlı bir tablo yapılandırma
Model Oluşturucu, bir tabloyu zamana bağlı olarak yapılandırmak için kullanılabilir. Örnek:
modelBuilder
.Entity<Employee>()
.ToTable("Employees", b => b.IsTemporal());
veritabanını oluşturmak için EF Core kullanırken, yeni tablo zaman damgaları ve geçmiş tablosu için SQL Server varsayılan değerleri ile bir zamana bağlı tablo olarak yapılandırılır. Örneğin, bir varlık türünü göz önünde bulundurun Employee :
public class Employee
{
public Guid EmployeeId { get; set; }
public string Name { get; set; }
public string Position { get; set; }
public string Department { get; set; }
public string Address { get; set; }
public decimal AnnualSalary { get; set; }
}
Oluşturulan zamana bağlı tablo şöyle görünür:
DECLARE @historyTableSchema sysname = SCHEMA_NAME()
EXEC(N'CREATE TABLE [Employees] (
[EmployeeId] uniqueidentifier NOT NULL,
[Name] nvarchar(100) NULL,
[Position] nvarchar(100) NULL,
[Department] nvarchar(100) NULL,
[Address] nvarchar(1024) NULL,
[AnnualSalary] decimal(10,2) NOT NULL,
[PeriodEnd] datetime2 GENERATED ALWAYS AS ROW END NOT NULL,
[PeriodStart] datetime2 GENERATED ALWAYS AS ROW START NOT NULL,
CONSTRAINT [PK_Employees] PRIMARY KEY ([EmployeeId]),
PERIOD FOR SYSTEM_TIME([PeriodStart], [PeriodEnd])
) WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = [' + @historyTableSchema + N'].[EmployeeHistory]))');
SQL Server datetime2 , ve adlı iki gizli sütun oluşturduğunu PeriodEnd unutmayın PeriodStart . Bu "dönem sütunları", satırdaki verilerin varolduğu zaman aralığını temsil eder. Bu sütunlar EF Core modelinde Gölge özelliklerle eşlenir ve bu, daha sonra gösterildiği gibi sorgularda kullanılmasına izin verir.
Önemli
Bu sütunlardaki süreler her zaman SQL Server tarafından üretilen UTC zamanlardır. UTC zamanları, aşağıda gösterilen sorgularda olduğu gibi, zamana bağlı tablolar içeren tüm işlemler için kullanılır.
Ayrıca, adlı ilişkili bir geçmiş tablosunun EmployeeHistory otomatik olarak oluşturulduğunu fark edebilirsiniz. Süre sütunları ve geçmiş tablosunun adları, model oluşturucunun ek yapılandırmasıyla değiştirilebilir. Örnek:
modelBuilder
.Entity<Employee>()
.ToTable(
"Employees",
b => b.IsTemporal(
b =>
{
b.HasPeriodStart("ValidFrom");
b.HasPeriodEnd("ValidTo");
b.UseHistoryTable("EmployeeHistoricalData");
}));
Bu, SQL Server tarafından oluşturulan tabloda yansıtılır:
DECLARE @historyTableSchema sysname = SCHEMA_NAME()
EXEC(N'CREATE TABLE [Employees] (
[EmployeeId] uniqueidentifier NOT NULL,
[Name] nvarchar(100) NULL,
[Position] nvarchar(100) NULL,
[Department] nvarchar(100) NULL,
[Address] nvarchar(1024) NULL,
[AnnualSalary] decimal(10,2) NOT NULL,
[ValidFrom] datetime2 GENERATED ALWAYS AS ROW START NOT NULL,
[ValidTo] datetime2 GENERATED ALWAYS AS ROW END NOT NULL,
CONSTRAINT [PK_Employees] PRIMARY KEY ([EmployeeId]),
PERIOD FOR SYSTEM_TIME([ValidFrom], [ValidTo])
) WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = [' + @historyTableSchema + N'].[EmployeeHistoricalData]))');
Zamana bağlı tabloları kullanma
Çoğu zaman zamana bağlı tablolar tıpkı diğer tüm tablolar gibi kullanılır. diğer bir deyişle, dönem sütunları ve geçmiş verileri, uygulamanın onları yoksayması için SQL Server tarafından saydam olarak işlenir. Örneğin, yeni varlıklar veritabanına normal şekilde kaydedilebilir:
context.AddRange(
new Employee
{
Name = "Pinky Pie",
Address = "Sugarcube Corner, Ponyville, Equestria",
Department = "DevDiv",
Position = "Party Organizer",
AnnualSalary = 100.0m
},
new Employee
{
Name = "Rainbow Dash",
Address = "Cloudominium, Ponyville, Equestria",
Department = "DevDiv",
Position = "Ponyville weather patrol",
AnnualSalary = 900.0m
},
new Employee
{
Name = "Fluttershy",
Address = "Everfree Forest, Equestria",
Department = "DevDiv",
Position = "Animal caretaker",
AnnualSalary = 30.0m
});
context.SaveChanges();
Bu veriler daha sonra normal şekilde sorgulanabilir, güncelleştirilir ve silinebilir. Örnek:
var employee = context.Employees.Single(e => e.Name == "Rainbow Dash");
context.Remove(employee);
context.SaveChanges();
Ayrıca, normal bir izleme sorgusundansonra, geçerli verilerin dönem sütunlarındaki değerlere izlenen varlıklardan erişilebilir. Örnek:
var employees = context.Employees.ToList();
foreach (var employee in employees)
{
var employeeEntry = context.Entry(employee);
var validFrom = employeeEntry.Property<DateTime>("ValidFrom").CurrentValue;
var validTo = employeeEntry.Property<DateTime>("ValidTo").CurrentValue;
Console.WriteLine($" Employee {employee.Name} valid from {validFrom} to {validTo}");
}
Bu baskı:
Starting data:
Employee Pinky Pie valid from 8/26/2021 4:38:58 PM to 12/31/9999 11:59:59 PM
Employee Rainbow Dash valid from 8/26/2021 4:38:58 PM to 12/31/9999 11:59:59 PM
Employee Fluttershy valid from 8/26/2021 4:38:58 PM to 12/31/9999 11:59:59 PM
ValidToSütunun (varsayılan olarak çağrılan PeriodEnd ) datetime2 en büyük değeri içerdiğini unutmayın. Bu, tablodaki geçerli satırlar için her zaman durumdur. ValidFromSütunlar (varsayılan olarak çağrılır PeriodStart ) SATıRıN eklendiği UTC saatini içerir.
Geçmiş verileri sorgulama
EF Core, tablodaki geçmiş verilerin bir çok yeni sorgu işleçleriyle sorgulanmasını destekler:
TemporalAsOf: Belirtilen UTC saatinde etkin (geçerli) satırları döndürür. Bu, belirli bir birincil anahtarın geçmiş tablosundan tek bir satırdır.TemporalAll: Geçmiş verilerdeki tüm satırları döndürür. Bu, genellikle belirli bir birincil anahtarın geçmiş tablosundaki çok sayıda satırdır.TemporalFromTo: Belirtilen iki UTC saati arasında etkin olan tüm satırları döndürür. Bu, belirli bir birincil anahtar için geçmiş tablosundan çok sayıda satır olabilir.TemporalBetween:TemporalFromToÜst sınır üzerinde etkin hale gelen satırların dahil olması dışında, ile aynıdır.TemporalContainedIn: Etkin olan ve belirtilen iki UTC saati arasında etkin olan tüm satırları döndürür. Bu, belirli bir birincil anahtar için geçmiş tablosundan çok sayıda satır olabilir.
Not
bu işleçlerin her biri için tam olarak hangi satırların dahil edildiğini öğrenmek için SQL Server zamana bağlı tabloları belgelerine bakın.
Örneğin, verilerimizde bazı güncelleştirmeler yaptıktan sonra, TemporalAll geçmiş verileri görmek için kullanarak bir sorgu çalıştırabiliriz:
var history = context
.Employees
.TemporalAll()
.Where(e => e.Name == "Rainbow Dash")
.OrderBy(e => EF.Property<DateTime>(e, "ValidFrom"))
.Select(
e => new
{
Employee = e,
ValidFrom = EF.Property<DateTime>(e, "ValidFrom"),
ValidTo = EF.Property<DateTime>(e, "ValidTo")
})
.ToList();
foreach (var pointInTime in history)
{
Console.WriteLine(
$" Employee {pointInTime.Employee.Name} was '{pointInTime.Employee.Position}' from {pointInTime.ValidFrom} to {pointInTime.ValidTo}");
}
EF 'in nasıl olduğunu fark edin . Özellik yöntemi , dönem sütunlarından değerlere erişmek için kullanılabilir. Bu, OrderBy yan tümcelerde verileri sıralamak için kullanılır ve sonra bu değerleri döndürülen verilere dahil eder.
Bu sorgu aşağıdaki verileri geri getirir:
Historical data for Rainbow Dash:
Employee Rainbow Dash was 'Ponyville weather patrol' from 8/26/2021 4:38:58 PM to 8/26/2021 4:40:29 PM
Employee Rainbow Dash was 'Wonderbolt Trainee' from 8/26/2021 4:40:29 PM to 8/26/2021 4:41:59 PM
Employee Rainbow Dash was 'Wonderbolt Reservist' from 8/26/2021 4:41:59 PM to 8/26/2021 4:43:29 PM
Employee Rainbow Dash was 'Wonderbolt' from 8/26/2021 4:43:29 PM to 8/26/2021 4:44:59 PM
Döndürülen son satırın 8/26/2021 4:44:59 PM 'de etkin durduğuna dikkat edin. Bunun nedeni, gökkuşağı Dash satırının o anda ana tablodan silindiği içindir. Bu verilerin nasıl geri yüklenebildiğinden daha sonra görülecektir.
Benzer sorgular,, veya kullanılarak TemporalFromTo yazılabilir TemporalBetweenTemporalContainedIn . Örnek:
var history = context
.Employees
.TemporalBetween(timeStamp2, timeStamp3)
.Where(e => e.Name == "Rainbow Dash")
.OrderBy(e => EF.Property<DateTime>(e, "ValidFrom"))
.Select(
e => new
{
Employee = e,
ValidFrom = EF.Property<DateTime>(e, "ValidFrom"),
ValidTo = EF.Property<DateTime>(e, "ValidTo")
})
.ToList();
Bu sorgu aşağıdaki satırları döndürür:
Historical data for Rainbow Dash between 8/26/2021 4:41:14 PM and 8/26/2021 4:42:44 PM:
Employee Rainbow Dash was 'Wonderbolt Trainee' from 8/26/2021 4:40:29 PM to 8/26/2021 4:41:59 PM
Employee Rainbow Dash was 'Wonderbolt Reservist' from 8/26/2021 4:41:59 PM to 8/26/2021 4:43:29 PM
Geçmiş verileri geri yükleme
Yukarıda belirtildiği gibi, gökkuşağı Dash Employees tablodan silindi. Bu çok açık bir hata olduğundan, bir zaman noktasına geri dönüp eksik satırı bu süreden geri yükleyebilir.
var employee = context
.Employees
.TemporalAsOf(timeStamp2)
.Single(e => e.Name == "Rainbow Dash");
context.Add(employee);
context.SaveChanges();
Bu sorgu, belirtilen UTC saatinde olduğu gibi gökkuşağı Dash için tek bir satır döndürür. Zamana bağlı işleçleri kullanan tüm sorgular varsayılan olarak izlenmez ve burada döndürülen varlık izlenmez. Bu, şu anda ana tabloda mevcut olmadığından anlamlı hale gelir. Varlığı ana tabloya yeniden eklemek için, bunu olarak işaretliyoruz Added ve sonra çağrıyoruz SaveChanges .
Satır gökkuşağı Dash satırını yeniden ekledikten sonra, geçmiş verileri sorgulamak satırın belirtilen UTC saatinde olduğu şekilde geri yüklendiğini gösterir:
Historical data for Rainbow Dash:
Employee Rainbow Dash was 'Ponyville weather patrol' from 8/26/2021 4:38:58 PM to 8/26/2021 4:40:29 PM
Employee Rainbow Dash was 'Wonderbolt Trainee' from 8/26/2021 4:40:29 PM to 8/26/2021 4:41:59 PM
Employee Rainbow Dash was 'Wonderbolt Reservist' from 8/26/2021 4:41:59 PM to 8/26/2021 4:43:29 PM
Employee Rainbow Dash was 'Wonderbolt' from 8/26/2021 4:43:29 PM to 8/26/2021 4:44:59 PM
Employee Rainbow Dash was 'Wonderbolt Trainee' from 8/26/2021 4:44:59 PM to 12/31/9999 11:59:59 PM
Geçiş demeti
GitHub sorunu: #19693.
EF Core geçişleri, EF modelindeki değişikliklere dayalı olarak veritabanı şema güncelleştirmeleri oluşturmak için kullanılır. Bu şema güncelleştirmeleri, genellikle sürekli tümleştirme/sürekli dağıtımın (C.I./C.exe) bir parçası olarak uygulama dağıtım zamanına uygulanmalıdır. sistemin.
EF Core şimdi bu şema güncelleştirmelerini uygulamak için yeni bir yol içermektedir: geçiş paketleri. Geçiş paketi, geçişleri ve bu geçişleri veritabanına uygulamak için gereken kodu içeren küçük bir yürütülebilir dosyadır.
Not
geçişler, paketler ve dağıtıma yönelik daha ayrıntılı bir tartışma için .net bloguna yönelik DevOps kullanımı kolay EF Core geçiş demeti tanıtma konusuna bakın.
Geçiş paketleri dotnet ef komut satırı aracı kullanılarak oluşturulur. Devam etmeden önce aracın en son sürümünü yüklediğinizden emin olun.
Bir paketin dahil etmek için geçişlere ihtiyacı vardır. Bunlar dotnet ef migrations add , dotnet ef migrations addaçıklandığı gibi kullanılarak oluşturulur. Dağıtıma hazırladıktan sonra, kullanarak bir paket oluşturun dotnet ef migrations bundle . Örnek:
PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations bundle
Build started...
Build succeeded.
Building bundle...
Done. Migrations Bundle: C:\local\AllTogetherNow\SixOh\efbundle.exe
PS C:\local\AllTogetherNow\SixOh>
Çıktı, hedef işletim sisteminiz için uygun bir yürütülebilir dosyadır. bu durumda Windows x64 olduğundan, efbundle.exe yerel klasörümdeki bir bırakıldım. Bu yürütülebilir dosyayı çalıştırmak, içinde yer alan geçişleri uygular:
PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe
Applying migration '20210903083845_MyMigration'.
Done.
PS C:\local\AllTogetherNow\SixOh>
Geçişler veritabanına yalnızca henüz uygulanmamış olmaları durumunda uygulanır. Örneğin, uygulanacak yeni geçişler bulunmadığından aynı paketi yeniden çalıştırmak hiçbir şey yapmaz:
PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe
No migrations were applied. The database is already up to date.
Done.
PS C:\local\AllTogetherNow\SixOh>
Ancak, modelde değişiklik yapılırsa ve ile daha fazla geçiş oluşturulursa dotnet ef migrations add , bunlar uygulamaya uygun yeni bir yürütülebilir dosya halinde paketlenmiştir. Örnek:
PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations add SecondMigration
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations add Number3
Build started...
Build succeeded.
Done. To undo this action, use 'ef migrations remove'
PS C:\local\AllTogetherNow\SixOh> dotnet ef migrations bundle --force
Build started...
Build succeeded.
Building bundle...
Done. Migrations Bundle: C:\local\AllTogetherNow\SixOh\efbundle.exe
PS C:\local\AllTogetherNow\SixOh>
--forceSeçeneğinin, mevcut paketin üzerine yazmak için yeni bir paket ile kullanılabileceğini unutmayın.
Bu yeni paketin yürütülmesi, bu iki yeni geçişi veritabanına uygular:
PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe
Applying migration '20210903084526_SecondMigration'.
Applying migration '20210903084538_Number3'.
Done.
PS C:\local\AllTogetherNow\SixOh>
Varsayılan olarak, paket uygulamanızın yapılandırmasından veritabanı bağlantı dizesini kullanır. Ancak, komut satırına bağlantı dizesi geçirerek farklı bir veritabanı geçirilebilir. Örnek:
PS C:\local\AllTogetherNow\SixOh> .\efbundle.exe --connection "Data Source=(LocalDb)\MSSQLLocalDB;Database=SixOhProduction"
Applying migration '20210903083845_MyMigration'.
Applying migration '20210903084526_SecondMigration'.
Applying migration '20210903084538_Number3'.
Done.
PS C:\local\AllTogetherNow\SixOh>
Bu zamanın, hiçbir zaman üretim veritabanına uygulanmadığından, üç geçişin uygulandığını unutmayın.
Diğer seçenekler komut satırına geçirilebilir. Bazı yaygın seçenekler şunlardır:
--outputoluşturulacak yürütülebilir dosyanın yolunu belirtmek için.--contextproje birden çok bağlam türü içerdiğinde kullanılacak DbContext türünü belirtmek için.--projectkullanılacak projeyi belirtmek için. Geçerli çalışma dizinini varsayılan olarak belirler.--startup-projectkullanılacak başlangıç projesini belirtmek için. Geçerli çalışma dizinini varsayılan olarak belirler.--no-buildprojeyi komutunu çalıştırmadan önce oluşturulmasını engellemek için. Bu, yalnızca projenin güncel olduğu bilindiğinde kullanılmalıdır.--verbosekomutun yaptığına ilişkin ayrıntılı bilgileri görmek için. Hata raporlarına bilgi eklerken bu seçeneği kullanın.
dotnet ef migrations bundle --helpTüm kullanılabilir seçenekleri görmek için kullanın.
Varsayılan olarak, her geçişin kendi işleminde uygulandığını unutmayın. bu alandaki olası gelecekteki geliştirmelerin tartışılması için GitHub sorun #22616 bakın.
Kural öncesi model yapılandırması
GitHub sorunu: #12229.
Önceki EF Core sürümleri, belirli bir türün her özelliği için eşlemenin, eşleme varsayılanından farklı olduğunda açık şekilde yapılandırılmasını gerektirir. Bu, en fazla dize uzunluğu ve ondalık duyarlık gibi özellik türü için de değer dönüştürme gibi "modeller" içerir.
Bu, aşağıdakilerden biri olabilir:
- Her özellik için model Oluşturucu yapılandırması
- Her özellikte bir eşleme özniteliği
- Model oluşturulurken tüm varlık türlerinin tüm özellikleri ve alt düzey meta veri API 'Lerinin kullanımı üzerinde açık yineleme.
Varlık türleri ve eşlenmiş özellikler listesi bu yineleme gerçekleştiğinde son olarak gerçekleşmediğinden açık yineleme hata ile açıktır ve robustly zor olduğunu unutmayın.
EF Core 6,0, belirli bir tür için bu eşleme yapılandırmasının bir kez belirtilmesini sağlar. Daha sonra, bu türün modeldeki tüm özelliklerine uygulanır. Bu, modelin model oluşturma kuralları tarafından kullanılan yönlerini yapılandırdığından "kural öncesi model yapılandırması" olarak adlandırılır. Bu yapılandırma, şu şekilde geçersiz kılınarak uygulanır ConfigureConventionsDbContext :
public class SomeDbContext : DbContext
{
protected override void ConfigureConventions(
ModelConfigurationBuilder configurationBuilder)
{
// Pre-convention model configuration goes here
}
}
Örneğin, aşağıdaki varlık türlerini göz önünde bulundurun:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public Money AccountValue { get; set; }
public Session CurrentSession { get; set; }
public ICollection<Order> Orders { get; } = new List<Order>();
}
public class Order
{
public int Id { get; set; }
public string SpecialInstructions { get; set; }
public DateTime OrderDate { get; set; }
public bool IsComplete { get; set; }
public Money Price { get; set; }
public Money? Discount { get; set; }
public Customer Customer { get; set; }
}
Tüm dize özellikleri ANSI olacak şekilde yapılandırılabilir (Unicode yerine) ve en fazla 1024 uzunluğunda olmalıdır:
configurationBuilder
.Properties<string>()
.AreUnicode(false)
.HaveMaxLength(1024);
Tüm tarih saat özellikleri veritabanındaki 64 bitlik tamsayıya dönüştürülebilir ve varsayılan olarak DateTimes 'den Long 'ye dönüştürülebilir:
configurationBuilder
.Properties<DateTime>()
.HaveConversion<long>();
Tüm bool özellikleri tamsayılara dönüştürülebilir 0 veya 1 yerleşik değer dönüştürücülerinin biri kullanılarak kullanılabilir:
configurationBuilder
.Properties<bool>()
.HaveConversion<BoolToZeroOneConverter<int>>();
SessionVarlığının geçici bir özelliği olduğu ve kalıcı olmaması gereken varsayılarak, modelde her yerde yoksayılabilir:
configurationBuilder
.IgnoreAny<Session>();
Değer nesneleriyle çalışırken kural öncesi model yapılandırması çok yararlı olur. Örneğin, Money Yukarıdaki modeldeki tür salt okunurdur struct ile temsil edilir:
public readonly struct Money
{
[JsonConstructor]
public Money(decimal amount, Currency currency)
{
Amount = amount;
Currency = currency;
}
public override string ToString()
=> (Currency == Currency.UsDollars ? "$" : "£") + Amount;
public decimal Amount { get; }
public Currency Currency { get; }
}
public enum Currency
{
UsDollars,
PoundsSterling
}
Bu daha sonra özel bir değer dönüştürücüsü kullanılarak JSON öğesine ve öğesinden seri hale getirilebilir:
public class MoneyConverter : ValueConverter<Money, string>
{
public MoneyConverter()
: base(
v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
v => JsonSerializer.Deserialize<Money>(v, (JsonSerializerOptions)null))
{
}
}
Bu değer Dönüştürücüsü, tüm para kullanımları için bir kez yapılandırılabilir:
configurationBuilder
.Properties<Money>()
.HaveConversion<MoneyConverter>()
.HaveMaxLength(64);
Ayrıca, seri hale getirilen JSON 'un depolandığı dize sütunu için ek modeller belirtilebildiğine dikkat edin. Bu durumda, sütun en fazla 64 uzunlukla sınırlıdır.
geçişler kullanılarak SQL Server için oluşturulan tablolar yapılandırmanın eşlenen tüm sütunlara nasıl uygulandığını gösterir:
CREATE TABLE [Customers] (
[Id] int NOT NULL IDENTITY,
[Name] varchar(1024) NULL,
[IsActive] int NOT NULL,
[AccountValue] nvarchar(64) NOT NULL,
CONSTRAINT [PK_Customers] PRIMARY KEY ([Id])
);
CREATE TABLE [Order] (
[Id] int NOT NULL IDENTITY,
[SpecialInstructions] varchar(1024) NULL,
[OrderDate] bigint NOT NULL,
[IsComplete] int NOT NULL,
[Price] nvarchar(64) NOT NULL,
[Discount] nvarchar(64) NULL,
[CustomerId] int NULL,
CONSTRAINT [PK_Order] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Order_Customers_CustomerId] FOREIGN KEY ([CustomerId]) REFERENCES [Customers] ([Id])
);
Ayrıca, belirli bir tür için varsayılan bir tür eşlemesi belirtmek mümkündür. Örnek:
configurationBuilder
.DefaultTypeMapping<string>()
.IsUnicode(false);
Bu nadiren gereklidir, ancak bir tür sorguda, modelin eşlenmiş özelliğiyle bağıntılı bir şekilde kullanılırsa yararlı olabilir.
Not
Bkz. duyurusu Entity Framework Core 6,0 Preview 6: .net blogda daha fazla tartışma ve kural öncesi model yapılandırması örnekleri Için kuralları yapılandırma.
Derlenen modeller
GitHub sorunu: #1906.
Derlenen modeller, büyük modellerdeki uygulamalar için EF Core başlangıç süresini iyileştirebilir. Büyük bir model tipik olarak, varlık türlerinin ve ilişkilerin bir Yüs-1000 olması anlamına gelir.
Başlangıç zamanı, bu DbContext türü uygulamada ilk kez kullanıldığında bir DbContext üzerinde ilk işlemin gerçekleştirileceği zaman anlamına gelir. Yalnızca DbContext örneği oluşturmanın EF modelinin başlatılmasına neden olmadığına unutmayın. Bunun yerine, modelin başlatılmasına neden olan tipik ilk işlemler, DbContext.Add ilk sorgunun çağrılmasını veya yürütülmesini içerir.
Derlenen modeller dotnet ef komut satırı aracı kullanılarak oluşturulur. Devam etmeden önce aracın en son sürümünü yüklediğinizden emin olun.
dbcontext optimizeDerlenmiş modeli oluşturmak için yeni bir komut kullanılır. Örnek:
dotnet ef dbcontext optimize
--output-dirVe --namespace seçenekleri, derlenmiş modelin üretilecektir dizin ve ad alanını belirtmek için kullanılabilir. Örnek:
PS C:\dotnet\efdocs\samples\core\Miscellaneous\CompiledModels> dotnet ef dbcontext optimize --output-dir MyCompiledModels --namespace MyCompiledModels
Build started...
Build succeeded.
Successfully generated a compiled model, to use it call 'options.UseModel(MyCompiledModels.BlogsContextModel.Instance)'. Run this command again when the model is modified.
PS C:\dotnet\efdocs\samples\core\Miscellaneous\CompiledModels>
Bu komutu çalıştırmanın çıktısı, derlenmiş modeli EF Core sağlamak üzere DbContext yapılandırmanıza kopyalamak ve yapıştırmak için bir kod parçası içerir. Örnek:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseModel(MyCompiledModels.BlogsContextModel.Instance)
.UseSqlite(@"Data Source=test.db");
Derlenen model önyüklemesi
Genellikle oluşturulan önyükleme koduna bakmak gerekmez. Ancak, bazen modeli veya yüklemeyi özelleştirmek yararlı olabilir. Önyükleme kodu şuna benzer:
[DbContext(typeof(BlogsContext))]
partial class BlogsContextModel : RuntimeModel
{
private static BlogsContextModel _instance;
public static IModel Instance
{
get
{
if (_instance == null)
{
_instance = new BlogsContextModel();
_instance.Initialize();
_instance.Customize();
}
return _instance;
}
}
partial void Initialize();
partial void Customize();
}
Bu, modeli gerektiği gibi özelleştirmek için uygulanabilecek kısmi yöntemlere sahip kısmi bir sınıftır.
Ayrıca, bazı çalışma zamanı yapılandırmasına bağlı olarak farklı modeller kullanan DbContext türleri için birden çok derlenmiş model oluşturulabilir. Bunlar yukarıda gösterildiği gibi farklı klasörlere ve ad alanlarına yerleştirilmelidir. Bağlantı dizesi gibi çalışma zamanı bilgileri daha sonra incelenmek ve gerektiği şekilde geri döndürülen doğru modellenebilir. Örnek:
public static class RuntimeModelCache
{
private static readonly ConcurrentDictionary<string, IModel> _runtimeModels
= new();
public static IModel GetOrCreateModel(string connectionString)
=> _runtimeModels.GetOrAdd(
connectionString, cs =>
{
if (cs.Contains("X"))
{
return BlogsContextModel1.Instance;
}
if (cs.Contains("Y"))
{
return BlogsContextModel2.Instance;
}
throw new InvalidOperationException("No appropriate compiled model found.");
});
}
Sınırlamalar
Derlenen modeller bazı sınırlamalara sahiptir:
- Genel sorgu filtreleri desteklenmiyor.
- Yavaş yükleme ve değişiklik izleme proxy 'leri desteklenmez.
- Özel IModelCacheKeyFactory uygulamaları desteklenmez. Ancak, birden çok modeli derleyebilir ve gerektiğinde uygun olanı yükleyebilirsiniz.
- Model tanımı veya yapılandırma değişikliği her seferinde yeniden oluşturuluyor, modelin el ile eşitlenmesi gerekir.
Bu sınırlamalar nedeniyle, yalnızca EF Core başlangıç zamanı çok yavaşsa derlenen modeller kullanmanız gerekir. Küçük modellerin derlenmesi genellikle buna değer vermez.
Bu özelliklerden herhangi birini desteklemek, başarınız için kritik öneme sahip olursa, lütfen yukarıda bağlantılı olan ilgili sorunları oylayın.
Karşılaştırmalar
İpucu
GitHub ' den örnek kodu indirerek, büyük bir modeli derlemeyi ve üzerinde bir kıyaslama çalıştırmayı deneyebilirsiniz.
yukarıda başvurulan GitHub depo, 449 varlık türünü, 6390 özelliklerini ve 720 ilişkilerini içerir. Bu, oldukça büyük bir modeldir. Ölçmek için Benchmarkdotnet kullanarak, ortalama sorgu süresi, makul bir şekilde güçlü bir dizüstü bilgisayarda 1,02 saniyedir. Derlenmiş modelleri kullanmak, bunu aynı donanımda 117 milisaniyeye getirir. Bu şekilde bir 8X-10 kat geliştirmesi, model boyutu arttıkça görece sabit kalır.

Not
EF Core başlangıç performansı ve derlenmiş modellerin daha ayrıntılı bir açıklaması için bkz. Entity Framework Core 6,0 Preview 5: derlenmiş modeller
TechEmpower Fortunes üzerinde geliştirilmiş performans
GitHub sorunu: #23611.
EF Core 6,0 ' de sorgu performansı için önemli geliştirmeler yaptık. Özellikle:
- EF Core 6,0 performansı artık sektör standardı TechEmpower Fortunes kıyaslaması üzerinde 70 ' e kıyasla daha hızlı bir 5,0 şekilde.
- Bu, kıyaslama kodundaki geliştirmeler, .NET çalışma zamanı vb. dahil olmak üzere tam yığın performans iyileştirmedir.
- EF Core 6.0'ın kendisi izlenmeyen sorguların yürütülmesini %31 daha hızlı yürütür.
- Sorgu yürüten yığın ayırmaları %43 azaltıldı.
Bu geliştirmelerden sonra TechEmpower Fortunes karşılaştırması kapsamındaki popüler "mikro ORM" Dapper ile EF Core arasındaki fark %55'in altında kaldı ve %5'in biraz altında kaldı.
Not
Entity Framework Core 6.0'daki sorgu performansı geliştirmeleri hakkında ayrıntılı bilgi için .NET Blog'da Entity Framework Core 6.0 Preview 4: Performance Edition EF Core bakın.
Cosmos sağlayıcı geliştirmeleri
EF Core 6.0, Azure Cosmos DB veritabanı sağlayıcısında birçok geliştirme içerir.
İpucu
GitHub'den örnek kodu indirerek Cosmos örneklere özel tüm örneklerde çalıştırabilir ve hata GitHub.
Varsayılan olarak örtülü sahiplik
GitHub Sorun: #24803.
Cosmos sağlayıcısı için bir model EF Core 6.0, alt varlık türlerini varsayılan olarak üst varlığa ait olarak işaret eder. Bu, modelde ve OwnsManyOwnsOne çağrılarının büyük bir Cosmos ortadan kaldırır. Bu, üst tür için alt türleri belgeye eklemeyi kolaylaştırır. Bu, genellikle bir belge veritabanında üst ve alt öğe modellemenin uygun yolu olur.
Örneğin, şu varlık türlerini göz önünde bulundurabilirsiniz:
public class Family
{
[JsonPropertyName("id")]
public string Id { get; set; }
public string LastName { get; set; }
public bool IsRegistered { get; set; }
public Address Address { get; set; }
public IList<Parent> Parents { get; } = new List<Parent>();
public IList<Child> Children { get; } = new List<Child>();
}
public class Parent
{
public string FamilyName { get; set; }
public string FirstName { get; set; }
}
public class Child
{
public string FamilyName { get; set; }
public string FirstName { get; set; }
public int Grade { get; set; }
public string Gender { get; set; }
public IList<Pet> Pets { get; } = new List<Pet>();
}
5 EF Core 5.0'da bu türler aşağıdaki yapılandırmayla Cosmos için modele alındı:
modelBuilder.Entity<Family>()
.HasPartitionKey(e => e.LastName)
.OwnsMany(f => f.Parents);
modelBuilder.Entity<Family>()
.OwnsMany(f => f.Children)
.OwnsMany(c => c.Pets);
modelBuilder.Entity<Family>()
.OwnsOne(f => f.Address);
6 EF Core 6.0'da sahiplik örtülü olur ve model yapılandırması şu şekilde azalt olur:
modelBuilder.Entity<Family>().HasPartitionKey(e => e.LastName);
Sonuçta elde Cosmos aile belgesine aile ebeveynleri, çocukların, evcil hayvanlar ve adreslerinin ekli olduğu belgelerde yer almaktadır. Örnek:
{
"Id": "Wakefield.7",
"LastName": "Wakefield",
"Discriminator": "Family",
"IsRegistered": true,
"id": "Family|Wakefield.7",
"Address": {
"City": "NY",
"County": "Manhattan",
"State": "NY"
},
"Children": [
{
"FamilyName": "Merriam",
"FirstName": "Jesse",
"Gender": "female",
"Grade": 8,
"Pets": [
{
"GivenName": "Goofy"
},
{
"GivenName": "Shadow"
}
]
},
{
"FamilyName": "Miller",
"FirstName": "Lisa",
"Gender": "female",
"Grade": 1,
"Pets": []
}
],
"Parents": [
{
"FamilyName": "Wakefield",
"FirstName": "Robin"
},
{
"FamilyName": "Miller",
"FirstName": "Ben"
}
],
"_rid": "x918AKh6p20CAAAAAAAAAA==",
"_self": "dbs/x918AA==/colls/x918AKh6p20=/docs/x918AKh6p20CAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-adee-87f30c8c01d7\"",
"_attachments": "attachments/",
"_ts": 1632121802
}
Not
Bu sahip olunan türleri daha OwnsOne/OwnsMany fazla yapılandırmanız gerekirse yapılandırmanın kullan gerektiğini unutmayın.
Temel tür koleksiyonları
GitHub Sorun: #14762.
EF Core 6.0, veritabanı sağlayıcısını kullanırken ilkel tür koleksiyonlarını Cosmos eşler. Örneğin, şu varlık türünü göz önünde bulundurabilirsiniz:
public class Book
{
public Guid Id { get; set; }
public string Title { get; set; }
public IList<string> Quotes { get; set; }
public IDictionary<string, string> Notes { get; set; }
}
Hem liste hem de sözlük normal şekilde doldurularak veritabanına eklenebilir:
using var context = new BooksContext();
var book = new Book
{
Title = "How It Works: Incredible History",
Quotes = new List<string>
{
"Thomas (Tommy) Flowers was the British engineer behind the design of the Colossus computer.",
"Invented originally for Guinness, plastic widgets are nitrogen-filled spheres.",
"For 20 years after its introduction in 1979, the Walkman dominated the personal stereo market."
},
Notes = new Dictionary<string, string>
{
{ "121", "Fridges" },
{ "144", "Peter Higgs" },
{ "48", "Saint Mark's Basilica" },
{ "36", "The Terracotta Army" }
}
};
context.Add(book);
context.SaveChanges();
Bu, aşağıdaki JSON belgesiyle sonuç verir:
{
"Id": "0b32283e-22a8-4103-bb4f-6052604868bd",
"Discriminator": "Book",
"Notes": {
"36": "The Terracotta Army",
"48": "Saint Mark's Basilica",
"121": "Fridges",
"144": "Peter Higgs"
},
"Quotes": [
"Thomas (Tommy) Flowers was the British engineer behind the design of the Colossus computer.",
"Invented originally for Guinness, plastic widgets are nitrogen-filled spheres.",
"For 20 years after its introduction in 1979, the Walkman dominated the personal stereo market."
],
"Title": "How It Works: Incredible History",
"id": "Book|0b32283e-22a8-4103-bb4f-6052604868bd",
"_rid": "t-E3AIxaencBAAAAAAAAAA==",
"_self": "dbs/t-E3AA==/colls/t-E3AIxaenc=/docs/t-E3AIxaencBAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-9b50-fc769dc901d7\"",
"_attachments": "attachments/",
"_ts": 1630075016
}
Bu koleksiyonlar daha sonra normal şekilde yeniden güncelleştirilebilir:
book.Quotes.Add("Pressing the emergency button lowered the rods again.");
book.Notes["48"] = "Chiesa d'Oro";
context.SaveChanges();
Sınırlamalar:
- Yalnızca dize anahtarlı sözlükler de destekle
- Temel koleksiyonların içeriğini sorgulamak şu anda desteklenmiyor. Bu özellikler sizin için önemli ise #16926, #25700ve #25701 için oy verme.
Yerleşik işlevlere çeviriler
GitHub Sorun: #16143.
Cosmos sağlayıcısı artık daha fazla Temel Sınıf Kitaplığı (BCL) yöntemini Cosmos işlevlerine çevirir. Aşağıdaki tablolarda, EF Core 6.0'da yeni olan çeviriler yer almaktadır.
Dize çevirileri
| BCL yöntemi | Yerleşik işlev | Notlar |
|---|---|---|
String.Length |
LENGTH |
|
String.ToLower |
LOWER |
|
String.TrimStart |
LTRIM |
|
String.TrimEnd |
RTRIM |
|
String.Trim |
TRIM |
|
String.ToUpper |
UPPER |
|
String.Substring |
SUBSTRING |
|
+ Işleç |
CONCAT |
|
String.IndexOf |
INDEX_OF |
|
String.Replace |
REPLACE |
|
String.Equals |
STRINGEQUAL |
Yalnızca büyük/büyük/büyük harfe duyarlı olmayan çağrılar |
, LOWER , LTRIM , , , RTRIM ve TRIMUPPERSUBSTRING çevirileri, @Marusyk. LOWER Çok teşekkürler!
Örneğin:
var stringResults
= context.Triangles.Where(
e => e.Name.Length > 4
&& e.Name.Trim().ToLower() != "obtuse"
&& e.Name.TrimStart().Substring(2, 2).Equals("uT", StringComparison.OrdinalIgnoreCase))
.ToList();
Bunu şöyle gösterebiliriz:
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Triangle") AND (((LENGTH(c["Name"]) > 4) AND (LOWER(TRIM(c["Name"])) != "obtuse")) AND STRINGEQUALS(SUBSTRING(LTRIM(c["Name"]), 2, 2), "uT", true)))
Matematik çevirileri
| BCL yöntemi | Yerleşik işlev |
|---|---|
Math.Abs veya MathF.Abs |
ABS |
Math.Acos veya MathF.Acos |
ACOS |
Math.Asin veya MathF.Asin |
ASIN |
Math.Atan veya MathF.Atan |
ATAN |
Math.Atan2 veya MathF.Atan2 |
ATN2 |
Math.Ceiling veya MathF.Ceiling |
CEILING |
Math.Cos veya MathF.Cos |
COS |
Math.Exp veya MathF.Exp |
EXP |
Math.Floor veya MathF.Floor |
FLOOR |
Math.Log veya MathF.Log |
LOG |
Math.Log10 veya MathF.Log10 |
LOG10 |
Math.Pow veya MathF.Pow |
POWER |
Math.Round veya MathF.Round |
ROUND |
Math.Sign veya MathF.Sign |
SIGN |
Math.Sin veya MathF.Sin |
SIN |
Math.Sqrt veya MathF.Sqrt |
SQRT |
Math.Tan veya MathF.Tan |
TAN |
Math.Truncate veya MathF.Truncate |
TRUNC |
DbFunctions.Random |
RAND |
Bu çeviriler, @Marusyk. Çok teşekkürler!
Örneğin:
var hypotenuse = 42.42;
var mathResults
= context.Triangles.Where(
e => (Math.Round(e.Angle1) == 90.0
|| Math.Round(e.Angle2) == 90.0)
&& (hypotenuse * Math.Sin(e.Angle1) > 30.0
|| hypotenuse * Math.Cos(e.Angle2) > 30.0))
.ToList();
Bunu şöyle gösterebiliriz:
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Triangle") AND (((ROUND(c["Angle1"]) = 90.0) OR (ROUND(c["Angle2"]) = 90.0)) AND (((@__hypotenuse_0 * SIN(c["Angle1"])) > 30.0) OR ((@__hypotenuse_0 * COS(c["Angle2"])) > 30.0))))
DateTime çevirileri
| BCL yöntemi | Yerleşik işlev |
|---|---|
DateTime.UtcNow |
GetCurrentDateTime |
Bu çeviriler, @Marusyk. Çok teşekkürler!
Örneğin:
var timeResults
= context.Triangles.Where(
e => e.InsertedOn <= DateTime.UtcNow)
.ToList();
Bunu şöyle gösterebiliriz:
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Triangle") AND (c["InsertedOn"] <= GetCurrentDateTime()))
FromSql SQL ham sorgular
GitHub Sorun: #17311.
Bazen LINQ kullanmak yerine ham SQL sorgusunu yürütmek gerekir. Bu artık yöntemi aracılığıyla Cosmos sağlayıcıda FromSql de destekleni. Bu, ilişkisel sağlayıcılarla her zaman olduğu gibi çalışır. Örnek:
var maxAngle = 60;
var rawResults
= context.Triangles.FromSqlRaw(
@"SELECT * FROM root c WHERE c[""Angle1""] <= {0} OR c[""Angle2""] <= {0}",
maxAngle)
.ToList();
Şu şekilde yürütülür:
SELECT c
FROM (
SELECT * FROM root c WHERE c["Angle1"] <= @p0 OR c["Angle2"] <= @p0
) c
WHERE (c["Discriminator"] = "Triangle")
Ayrı sorgular
GitHub Sorun: #16144.
kullanan basit Distinct sorgular artık çevrilmiştir. Örnek:
var distinctResults
= context.Triangles
.Select(e => e.Angle1).OrderBy(e => e).Distinct()
.ToList();
Bunu şöyle gösterebiliriz:
SELECT DISTINCT c["Angle1"]
FROM root c
WHERE (c["Discriminator"] = "Triangle")
ORDER BY c["Angle1"]
Tanılama
GitHub Sorun: #17298.
Veri Cosmos artık veritabanından veri ekleme, sorgulama, güncelleştirme ve silme olayları da dahil olmak üzere daha fazla tanılama bilgisini günlüğe kaydeder. İstek birimleri (RU) uygun olduğunda bu olaylara dahil edilir.
Not
Günlükler burada kimlik EnableSensitiveDataLogging() değerlerinin gösterildiği şekilde kullanır.
Veritabanına bir öğe Cosmos olayı CosmosEventId.ExecutedCreateItem oluşturur. Örneğin, bu kod:
var triangle = new Triangle
{
Name = "Impossible",
PartitionKey = "TrianglesPartition",
Angle1 = 90,
Angle2 = 90,
InsertedOn = DateTime.UtcNow
};
context.Add(triangle);
context.SaveChanges();
Aşağıdaki tanılama olaylarını günlüğe kaydeder:
info: 8/30/2021 14:41:13.356 CosmosEventId.ExecutedCreateItem[30104] (Microsoft.EntityFrameworkCore.Database.Command)
Executed CreateItem (5 ms, 7.43 RU) ActivityId='417db46f-fcdd-49d9-a7f0-77210cd06f84', Container='Shapes', Id='Impossible', Partition='TrianglesPartition'
Sorgu kullanarak Cosmos veritabanından öğe almak olayı oluşturur ve ardından okunan öğeler CosmosEventId.ExecutingSqlQuery için bir veya daha fazla olay CosmosEventId.ExecutedReadNext oluşturur. Örneğin, bu kod:
var equilateral = context.Triangles.Single(e => e.Name == "Equilateral");
Aşağıdaki tanılama olaylarını günlüğe kaydeder:
info: 8/30/2021 14:41:13.475 CosmosEventId.ExecutingSqlQuery[30100] (Microsoft.EntityFrameworkCore.Database.Command)
Executing SQL query for container 'Shapes' in partition '(null)' [Parameters=[]]
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Triangle") AND (c["id"] = "Equilateral"))
OFFSET 0 LIMIT 2
info: 8/30/2021 14:41:13.651 CosmosEventId.ExecutedReadNext[30102] (Microsoft.EntityFrameworkCore.Database.Command)
Executed ReadNext (169.6126 ms, 2.93 RU) ActivityId='4e465fae-3d49-4c1f-bd04-142bc5d0b0a1', Container='Shapes', Partition='(null)', Parameters=[]
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Triangle") AND (c["id"] = "Equilateral"))
OFFSET 0 LIMIT 2
Bir bölüm anahtarı ile kullanarak Cosmos Find veritabanından tek bir öğe almak ve CosmosEventId.ExecutingReadItem olaylarını CosmosEventId.ExecutedReadItem oluşturur. Örneğin, bu kod:
var isosceles = context.Triangles.Find("Isosceles", "TrianglesPartition");
Aşağıdaki tanılama olaylarını günlüğe kaydeder:
info: 8/30/2021 14:53:39.326 CosmosEventId.ExecutingReadItem[30101] (Microsoft.EntityFrameworkCore.Database.Command)
Reading resource 'Isosceles' item from container 'Shapes' in partition 'TrianglesPartition'.
info: 8/30/2021 14:53:39.330 CosmosEventId.ExecutedReadItem[30103] (Microsoft.EntityFrameworkCore.Database.Command)
Executed ReadItem (1 ms, 1 RU) ActivityId='3c278643-4e7f-4bb2-9953-6055b5f1288f', Container='Shapes', Id='Isosceles', Partition='TrianglesPartition'
Güncelleştirilmiş bir öğeyi Cosmos veritabanı olayı CosmosEventId.ExecutedReplaceItem oluşturur. Örneğin, bu kod:
triangle.Angle2 = 89;
context.SaveChanges();
Aşağıdaki tanılama olaylarını günlüğe kaydeder:
info: 8/30/2021 14:53:39.343 CosmosEventId.ExecutedReplaceItem[30105] (Microsoft.EntityFrameworkCore.Database.Command)
Executed ReplaceItem (6 ms, 10.67 RU) ActivityId='1525b958-fea1-49e8-89f9-d429d0351fdb', Container='Shapes', Id='Impossible', Partition='TrianglesPartition'
Cosmos veritabanından bir öğeyi silmek olayı CosmosEventId.ExecutedDeleteItem oluşturur. Örneğin, bu kod:
context.Remove(triangle);
context.SaveChanges();
Aşağıdaki tanılama olaylarını günlüğe kaydeder:
info: 8/30/2021 14:53:39.359 CosmosEventId.ExecutedDeleteItem[30106] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DeleteItem (6 ms, 7.43 RU) ActivityId='cbc54463-405b-48e7-8c32-2c6502a4138f', Container='Shapes', Id='Impossible', Partition='TrianglesPartition'
Aktarım hızını yapılandırma
GitHub Sorun: #17301.
Cosmos modeli artık el ile veya otomatik ölçeklendirme aktarım hızıyla yalıtıldı. Bu değerler veritabanında aktarım hızı sağlar. Örnek:
modelBuilder.HasManualThroughput(2000);
modelBuilder.HasAutoscaleThroughput(6000);
Ayrıca, tek tek varlık türleri ilgili kapsayıcı için aktarım hızı sağacak şekilde yalıtabilirsiniz. Örnek:
modelBuilder.Entity<Family>(
entityTypeBuilder =>
{
entityTypeBuilder.HasAutoscaleThroughput(3000);
entityTypeBuilder.HasAutoscaleThroughput(12000);
});
Yaşam süresi yapılandırma
GitHub Sorun: #17307.
Cosmos modelde varlık türleri artık analiz deposu için varsayılan yaşam süresi ve yaşam süresi ile yalıtabilirsiniz. Örnek:
modelBuilder.Entity<Family>(
entityTypeBuilder =>
{
entityTypeBuilder.HasDefaultTimeToLive(100);
entityTypeBuilder.HasAnalyticalStoreTimeToLive(200);
});
HTTP istemci fabrikasını çözümleme
GitHub Sorun: #21274. Bu özellik, tarafından @dnperfors. Çok teşekkürler!
Sağlayıcı HttpClientFactory tarafından Cosmos artık açıkça ayarlanabilirsiniz. Bu özellikle test sırasında yararlı olabilir, örneğin Linux'ta Cosmos öykünücüsü kullanılırken sertifika doğrulamayı atlamak için:
optionsBuilder
.EnableSensitiveDataLogging()
.UseCosmos(
"https://localhost:8081",
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
"PrimitiveCollections",
cosmosOptionsBuilder =>
{
cosmosOptionsBuilder.HttpClientFactory(
() => new HttpClient(
new HttpClientHandler
{
ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
}));
});
Not
Mevcut bir uygulamaya EF Core geliştirmeleri uygulamanın ayrıntılı bir örneği için .NET Blog'da Test Sürüşu için Azure Cosmos DB Sağlayıcısı'Cosmos'sini alma'ya bakın.
Mevcut veritabanından yapı iskelesi geliştirmeleri
EF Core 6.0, bir EF modelini mevcut bir veritabanından tersine mühendislikle ilgili çeşitli geliştirmeler içerir.
Çoka çok ilişkilerin iskelesi
GitHub Sorun: #22475.
EF Core 6.0 basit birleştirme tablolarını algılar ve otomatik olarak onlar için çoka çok eşlemesi oluşturur. Örneğin, ve tablolarını Posts ve bunları bağlayan bir birleştirme tablosu TagsPostTag düşünün:
CREATE TABLE [Tags] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NOT NULL,
[Description] nvarchar(max) NULL,
CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));
CREATE TABLE [Posts] (
[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NOT NULL,
[Contents] nvarchar(max) NOT NULL,
[PostedOn] datetime2 NOT NULL,
[UpdatedOn] datetime2 NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]));
CREATE TABLE [PostTag] (
[PostsId] int NOT NULL,
[TagsId] int NOT NULL,
CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
CONSTRAINT [FK_PostTag_Posts_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tags] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_PostTag_Tags_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([Id]) ON DELETE CASCADE);
Bu tablolar, komut satırıyla iskelede yer alan tablolar olabilir. Örnek:
dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=BloggingWithNRTs" Microsoft.EntityFrameworkCore.SqlServer
Bu, Post için bir sınıfa neden olur:
public partial class Post
{
public Post()
{
Tags = new HashSet<Tag>();
}
public int Id { get; set; }
public string Title { get; set; } = null!;
public string Contents { get; set; } = null!;
public DateTime PostedOn { get; set; }
public DateTime? UpdatedOn { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; } = null!;
public virtual ICollection<Tag> Tags { get; set; }
}
Tag için de bir sınıf:
public partial class Tag
{
public Tag()
{
Posts = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; } = null!;
public string? Description { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
Ancak tablo için bir sınıf PostTag yoktur. Bunun yerine, çoka çok ilişkisinin yapılandırması iskelededir:
entity.HasMany(d => d.Tags)
.WithMany(p => p.Posts)
.UsingEntity<Dictionary<string, object>>(
"PostTag",
l => l.HasOne<Tag>().WithMany().HasForeignKey("PostsId"),
r => r.HasOne<Post>().WithMany().HasForeignKey("TagsId"),
j =>
{
j.HasKey("PostsId", "TagsId");
j.ToTable("PostTag");
j.HasIndex(new[] { "TagsId" }, "IX_PostTag_TagsId");
});
İskele C# null değere değiştirilebilir başvuru türleri
GitHub Sorun: #15520.
EF Core 6.0 artık C# null değere değiştirilebilir başvuru türlerini (NRT) kullanan bir EF modeli ve varlık türleri iskelesi sağlar. Kodun iskeleye alınarak C# projesinde NRT desteği etkinleştirildiğinde NRT kullanımı otomatik olarak iskelelenir.
Örneğin, aşağıdaki tablo Tags null değere sahip olmayan her iki dize sütunlarını da içerir:
CREATE TABLE [Tags] (
[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NOT NULL,
[Description] nvarchar(max) NULL,
CONSTRAINT [PK_Tags] PRIMARY KEY ([Id]));
Bu, oluşturulan sınıfta karşılık gelen null değer ve null değerdilemez dize özelliklerine neden olur:
public partial class Tag
{
public Tag()
{
Posts = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; } = null!;
public string? Description { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
Benzer şekilde, aşağıdaki Posts tablolar tabloyla gerekli bir ilişki Blogs içerir:
CREATE TABLE [Posts] (
[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NOT NULL,
[Contents] nvarchar(max) NOT NULL,
[PostedOn] datetime2 NOT NULL,
[UpdatedOn] datetime2 NULL,
[BlogId] int NOT NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]));
Bu, bloglar arasında null değere sahip olmayan (gerekli) bir ilişkinin iskelesi ile sonuçlanabilir:
public partial class Blog
{
public Blog()
{
Posts = new HashSet<Post>();
}
public int Id { get; set; }
public string Name { get; set; } = null!;
public virtual ICollection<Post> Posts { get; set; }
}
Ve gönderiler:
public partial class Post
{
public Post()
{
Tags = new HashSet<Tag>();
}
public int Id { get; set; }
public string Title { get; set; } = null!;
public string Contents { get; set; } = null!;
public DateTime PostedOn { get; set; }
public DateTime? UpdatedOn { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; } = null!;
public virtual ICollection<Tag> Tags { get; set; }
}
Son olarak, oluşturulan DbContext'deki DbSet özellikleri NRT kolay bir şekilde oluşturulur. Örnek:
public virtual DbSet<Blog> Blogs { get; set; } = null!;
public virtual DbSet<Post> Posts { get; set; } = null!;
public virtual DbSet<Tag> Tags { get; set; } = null!;
Veritabanı açıklamalarını kod açıklamalarını iskeleye alma
GitHub Sorun: #19113. Bu özellik, tarafından @ErikEJ. Çok teşekkürler!
Tablo ve SQL açıklamalar artık mevcut bir EF Core veritabanından bir EF Core model ters mühendislikle oluşturulurken oluşturulan varlık türlerine SQL Server oluşturulur.
/// <summary>
/// The Blog table.
/// </summary>
public partial class Blog
{
/// <summary>
/// The primary key.
/// </summary>
[Key]
public int Id { get; set; }
}
LINQ sorgu geliştirmeleri
EF Core 6.0, LINQ sorgularının çeviri ve yürütmesinde çeşitli geliştirmeler içerir.
GroupBy desteği iyileştirildi
GitHub Sorunlar: #12088, #13805ve #22609.
EF Core 6.0, sorgular için daha iyi GroupBy destek içerir. Özellikle EF Core:
- GroupBy'yi ve ardından
FirstOrDefault(veya benzerlerini) bir grup üzerinden çevirme - Bir gruptan ilk N sonuç seçmeyi destekler
- İşleç uygulandıktan sonra
GroupBygezintileri genişleten
Aşağıda müşteri raporlarından alınan örnek sorgular ve bu sorguların SQL Server.
Örnek 1:
var people = context.People
.Include(e => e.Shoes)
.GroupBy(e => e.FirstName)
.Select(
g => g.OrderBy(e => e.FirstName)
.ThenBy(e => e.LastName)
.FirstOrDefault())
.ToList();
SELECT [t0].[Id], [t0].[Age], [t0].[FirstName], [t0].[LastName], [t0].[MiddleInitial], [t].[FirstName], [s].[Id], [s].[Age], [s].[PersonId], [s].[Style]
FROM (
SELECT [p].[FirstName]
FROM [People] AS [p]
GROUP BY [p].[FirstName]
) AS [t]
LEFT JOIN (
SELECT [t1].[Id], [t1].[Age], [t1].[FirstName], [t1].[LastName], [t1].[MiddleInitial]
FROM (
SELECT [p0].[Id], [p0].[Age], [p0].[FirstName], [p0].[LastName], [p0].[MiddleInitial], ROW_NUMBER() OVER(PARTITION BY [p0].[FirstName] ORDER BY [p0].[FirstName], [p0].[LastName]) AS [row]
FROM [People] AS [p0]
) AS [t1]
WHERE [t1].[row] <= 1
) AS [t0] ON [t].[FirstName] = [t0].[FirstName]
LEFT JOIN [Shoes] AS [s] ON [t0].[Id] = [s].[PersonId]
ORDER BY [t].[FirstName], [t0].[FirstName]
Örnek 2:
var group = context.People
.Select(
p => new
{
p.FirstName,
FullName = p.FirstName + " " + p.MiddleInitial + " " + p.LastName
})
.GroupBy(p => p.FirstName)
.Select(g => g.First())
.First();
SELECT [t0].[FirstName], [t0].[FullName], [t0].[c]
FROM (
SELECT TOP(1) [p].[FirstName]
FROM [People] AS [p]
GROUP BY [p].[FirstName]
) AS [t]
LEFT JOIN (
SELECT [t1].[FirstName], [t1].[FullName], [t1].[c]
FROM (
SELECT [p0].[FirstName], (((COALESCE([p0].[FirstName], N'') + N' ') + COALESCE([p0].[MiddleInitial], N'')) + N' ') + COALESCE([p0].[LastName], N'') AS [FullName], 1 AS [c], ROW_NUMBER() OVER(PARTITION BY [p0].[FirstName] ORDER BY [p0].[FirstName]) AS [row]
FROM [People] AS [p0]
) AS [t1]
WHERE [t1].[row] <= 1
) AS [t0] ON [t].[FirstName] = [t0].[FirstName]
Örnek 3:
var people = context.People
.Where(e => e.MiddleInitial == "Q" && e.Age == 20)
.GroupBy(e => e.LastName)
.Select(g => g.First().LastName)
.OrderBy(e => e.Length)
.ToList();
SELECT (
SELECT TOP(1) [p1].[LastName]
FROM [People] AS [p1]
WHERE (([p1].[MiddleInitial] = N'Q') AND ([p1].[Age] = 20)) AND (([p].[LastName] = [p1].[LastName]) OR ([p].[LastName] IS NULL AND [p1].[LastName] IS NULL)))
FROM [People] AS [p]
WHERE ([p].[MiddleInitial] = N'Q') AND ([p].[Age] = 20)
GROUP BY [p].[LastName]
ORDER BY CAST(LEN((
SELECT TOP(1) [p1].[LastName]
FROM [People] AS [p1]
WHERE (([p1].[MiddleInitial] = N'Q') AND ([p1].[Age] = 20)) AND (([p].[LastName] = [p1].[LastName]) OR ([p].[LastName] IS NULL AND [p1].[LastName] IS NULL)))) AS int)
Örnek 4:
var results = (from person in context.People
join shoes in context.Shoes on person.Age equals shoes.Age
group shoes by shoes.Style
into people
select new
{
people.Key,
Style = people.Select(p => p.Style).FirstOrDefault(),
Count = people.Count()
})
.ToList();
SELECT [s].[Style] AS [Key], (
SELECT TOP(1) [s0].[Style]
FROM [People] AS [p0]
INNER JOIN [Shoes] AS [s0] ON [p0].[Age] = [s0].[Age]
WHERE ([s].[Style] = [s0].[Style]) OR ([s].[Style] IS NULL AND [s0].[Style] IS NULL)) AS [Style], COUNT(*) AS [Count]
FROM [People] AS [p]
INNER JOIN [Shoes] AS [s] ON [p].[Age] = [s].[Age]
GROUP BY [s].[Style]
Örnek 5:
var results = context.People
.GroupBy(e => e.FirstName)
.Select(g => g.First().LastName)
.OrderBy(e => e)
.ToList();
SELECT (
SELECT TOP(1) [p1].[LastName]
FROM [People] AS [p1]
WHERE ([p].[FirstName] = [p1].[FirstName]) OR ([p].[FirstName] IS NULL AND [p1].[FirstName] IS NULL))
FROM [People] AS [p]
GROUP BY [p].[FirstName]
ORDER BY (
SELECT TOP(1) [p1].[LastName]
FROM [People] AS [p1]
WHERE ([p].[FirstName] = [p1].[FirstName]) OR ([p].[FirstName] IS NULL AND [p1].[FirstName] IS NULL))
Örnek 6:
var results = context.People.Where(e => e.Age == 20)
.GroupBy(e => e.Id)
.Select(g => g.First().MiddleInitial)
.OrderBy(e => e)
.ToList();
SELECT (
SELECT TOP(1) [p1].[MiddleInitial]
FROM [People] AS [p1]
WHERE ([p1].[Age] = 20) AND ([p].[Id] = [p1].[Id]))
FROM [People] AS [p]
WHERE [p].[Age] = 20
GROUP BY [p].[Id]
ORDER BY (
SELECT TOP(1) [p1].[MiddleInitial]
FROM [People] AS [p1]
WHERE ([p1].[Age] = 20) AND ([p].[Id] = [p1].[Id]))
Örnek 7:
var size = 11;
var results
= context.People
.Where(
p => p.Feet.Size == size
&& p.MiddleInitial != null
&& p.Feet.Id != 1)
.GroupBy(
p => new
{
p.Feet.Size,
p.Feet.Person.LastName
})
.Select(
g => new
{
g.Key.LastName,
g.Key.Size,
Min = g.Min(p => p.Feet.Size),
})
.ToList();
Executed DbCommand (12ms) [Parameters=[@__size_0='11'], CommandType='Text', CommandTimeout='30']
SELECT [p0].[LastName], [f].[Size], MIN([f0].[Size]) AS [Min]
FROM [People] AS [p]
LEFT JOIN [Feet] AS [f] ON [p].[Id] = [f].[Id]
LEFT JOIN [People] AS [p0] ON [f].[Id] = [p0].[Id]
LEFT JOIN [Feet] AS [f0] ON [p].[Id] = [f0].[Id]
WHERE (([f].[Size] = @__size_0) AND [p].[MiddleInitial] IS NOT NULL) AND (([f].[Id] <> 1) OR [f].[Id] IS NULL)
GROUP BY [f].[Size], [p0].[LastName]
Örnek 8:
var result = context.People
.Include(x => x.Shoes)
.Include(x => x.Feet)
.GroupBy(
x => new
{
x.Feet.Id,
x.Feet.Size
})
.Select(
x => new
{
Key = x.Key.Id + x.Key.Size,
Count = x.Count(),
Sum = x.Sum(el => el.Id),
SumOver60 = x.Sum(el => el.Id) / (decimal)60,
TotalCallOutCharges = x.Sum(el => el.Feet.Size == 11 ? 1 : 0)
})
.Count();
SELECT COUNT(*)
FROM (
SELECT [f].[Id], [f].[Size]
FROM [People] AS [p]
LEFT JOIN [Feet] AS [f] ON [p].[Id] = [f].[Id]
GROUP BY [f].[Id], [f].[Size]
) AS [t]
Örnek 9:
var results = context.People
.GroupBy(n => n.FirstName)
.Select(g => new
{
Feet = g.Key,
Total = g.Sum(n => n.Feet.Size)
})
.ToList();
SELECT [p].[FirstName] AS [Feet], COALESCE(SUM([f].[Size]), 0) AS [Total]
FROM [People] AS [p]
LEFT JOIN [Feet] AS [f] ON [p].[Id] = [f].[Id]
GROUP BY [p].[FirstName]
Örnek 10:
var results = from Person person1
in from Person person2
in context.People
select person2
join Shoes shoes
in context.Shoes
on person1.Age equals shoes.Age
group shoes by
new
{
person1.Id,
shoes.Style,
shoes.Age
}
into temp
select
new
{
temp.Key.Id,
temp.Key.Age,
temp.Key.Style,
Values = from t
in temp
select
new
{
t.Id,
t.Style,
t.Age
}
};
SELECT [t].[Id], [t].[Age], [t].[Style], [t0].[Id], [t0].[Style], [t0].[Age], [t0].[Id0]
FROM (
SELECT [p].[Id], [s].[Age], [s].[Style]
FROM [People] AS [p]
INNER JOIN [Shoes] AS [s] ON [p].[Age] = [s].[Age]
GROUP BY [p].[Id], [s].[Style], [s].[Age]
) AS [t]
LEFT JOIN (
SELECT [s0].[Id], [s0].[Style], [s0].[Age], [p0].[Id] AS [Id0]
FROM [People] AS [p0]
INNER JOIN [Shoes] AS [s0] ON [p0].[Age] = [s0].[Age]
) AS [t0] ON (([t].[Id] = [t0].[Id0]) AND (([t].[Style] = [t0].[Style]) OR ([t].[Style] IS NULL AND [t0].[Style] IS NULL))) AND ([t].[Age] = [t0].[Age])
ORDER BY [t].[Id], [t].[Style], [t].[Age], [t0].[Id0]
Örnek 11:
var grouping = context.People
.GroupBy(i => i.LastName)
.Select(g => new { LastName = g.Key, Count = g.Count() , First = g.FirstOrDefault(), Take = g.Take(2)})
.OrderByDescending(e => e.LastName)
.ToList();
SELECT [t].[LastName], [t].[c], [t0].[Id], [t2].[Id], [t2].[Age], [t2].[FirstName], [t2].[LastName], [t2].[MiddleInitial], [t0].[Age], [t0].[FirstName], [t0].[LastName], [t0].[MiddleInitial]
FROM (
SELECT [p].[LastName], COUNT(*) AS [c]
FROM [People] AS [p]
GROUP BY [p].[LastName]
) AS [t]
LEFT JOIN (
SELECT [t1].[Id], [t1].[Age], [t1].[FirstName], [t1].[LastName], [t1].[MiddleInitial]
FROM (
SELECT [p0].[Id], [p0].[Age], [p0].[FirstName], [p0].[LastName], [p0].[MiddleInitial], ROW_NUMBER() OVER(PARTITION BY [p0].[LastName] ORDER BY [p0].[Id]) AS [row]
FROM [People] AS [p0]
) AS [t1]
WHERE [t1].[row] <= 1
) AS [t0] ON [t].[LastName] = [t0].[LastName]
LEFT JOIN (
SELECT [t3].[Id], [t3].[Age], [t3].[FirstName], [t3].[LastName], [t3].[MiddleInitial]
FROM (
SELECT [p1].[Id], [p1].[Age], [p1].[FirstName], [p1].[LastName], [p1].[MiddleInitial], ROW_NUMBER() OVER(PARTITION BY [p1].[LastName] ORDER BY [p1].[Id]) AS [row]
FROM [People] AS [p1]
) AS [t3]
WHERE [t3].[row] <= 2
) AS [t2] ON [t].[LastName] = [t2].[LastName]
ORDER BY [t].[LastName] DESC, [t0].[Id], [t2].[LastName], [t2].[Id]
Örnek 12:
var grouping = context.People
.Include(e => e.Shoes)
.OrderBy(e => e.FirstName)
.ThenBy(e => e.LastName)
.GroupBy(e => e.FirstName)
.Select(g => new { Name = g.Key, People = g.ToList()})
.ToList();
SELECT [t].[FirstName], [t0].[Id], [t0].[Age], [t0].[FirstName], [t0].[LastName], [t0].[MiddleInitial], [t0].[Id0], [t0].[Age0], [t0].[PersonId], [t0].[Style]
FROM (
SELECT [p].[FirstName]
FROM [People] AS [p]
GROUP BY [p].[FirstName]
) AS [t]
LEFT JOIN (
SELECT [p0].[Id], [p0].[Age], [p0].[FirstName], [p0].[LastName], [p0].[MiddleInitial], [s].[Id] AS [Id0], [s].[Age] AS [Age0], [s].[PersonId], [s].[Style]
FROM [People] AS [p0]
LEFT JOIN [Shoes] AS [s] ON [p0].[Id] = [s].[PersonId]
) AS [t0] ON [t].[FirstName] = [t0].[FirstName]
ORDER BY [t].[FirstName], [t0].[Id]
Örnek 13:
var grouping = context.People
.GroupBy(m => new {m.FirstName, m.MiddleInitial })
.Select(am => new
{
Key = am.Key,
Items = am.ToList()
})
.ToList();
SELECT [t].[FirstName], [t].[MiddleInitial], [p0].[Id], [p0].[Age], [p0].[FirstName], [p0].[LastName], [p0].[MiddleInitial]
FROM (
SELECT [p].[FirstName], [p].[MiddleInitial]
FROM [People] AS [p]
GROUP BY [p].[FirstName], [p].[MiddleInitial]
) AS [t]
LEFT JOIN [People] AS [p0] ON (([t].[FirstName] = [p0].[FirstName]) OR ([t].[FirstName] IS NULL AND [p0].[FirstName] IS NULL)) AND (([t].[MiddleInitial] = [p0].[MiddleInitial]) OR ([t].[MiddleInitial] IS NULL AND [p0].[MiddleInitial] IS NULL))
ORDER BY [t].[FirstName], [t].[MiddleInitial]
Modelleme
Bu örnekler için kullanılan varlık türleri:
public class Person
{
public int Id { get; set; }
public int Age { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string MiddleInitial { get; set; }
public Feet Feet { get; set; }
public ICollection<Shoes> Shoes { get; } = new List<Shoes>();
}
public class Shoes
{
public int Id { get; set; }
public int Age { get; set; }
public string Style { get; set; }
public Person Person { get; set; }
}
public class Feet
{
public int Id { get; set; }
public int Size { get; set; }
public Person Person { get; set; }
}
String.Concat'ı birden çok bağımsız değişkenle çevirme
GitHub Sorun: #23859. Bu özellik, tarafından @wmeints. Çok teşekkürler!
6.0 EF Core başlayarak, birden çok bağımsız değişkenle String.Concat'a yapılan çağrılar artık SQL. Örneğin, aşağıdaki sorgu:
var shards = context.Shards
.Where(e => string.Concat(e.Token1, e.Token2, e.Token3) != e.TokensProcessed).ToList();
Komutlar kullanırken aşağıdaki SQL çevrilecek SQL Server:
SELECT [s].[Id], [s].[Token1], [s].[Token2], [s].[Token3], [s].[TokensProcessed]
FROM [Shards] AS [s]
WHERE (([s].[Token1] + ([s].[Token2] + [s].[Token3])) <> [s].[TokensProcessed]) OR [s].[TokensProcessed] IS NULL
System.Linq.Async ile daha sorunsuz tümleştirme
GitHub Sorun: #24041.
System.Linq.Async paketi istemci tarafı zaman uyumsuz LINQ işleme ekler. Zaman uyumsuz LINQ yöntemlerinde ad alanı EF Core nedeniyle bu paketin önceki sürümleriyle birlikte kullanımı yorucu oldu. EF Core 6.0'da, ortaya konulan EF Core > arabirimi doğrudan uygulamasına gerek kalmadan IAsyncEnumerable T > için C# desen eşleştirmeden yararlanmıştık.
Çoğu uygulamanın System.Linq.Async'i kullanması gerek EF Core genellikle tam olarak sunucuda çevrilir.
Serbest metin SQL Server daha esnektir
GitHub Sorun: #23921.
6 EF Core 6.0'da FreeText(DbFunctions, String, String) ve Contains için parametre gereksinimlerini gevşetmiş oldu. Bu, bu işlevlerin ikili sütunlarla veya değer dönüştürücü kullanılarak eşlenmiş sütunlarla birlikte kullanımına olanak sağlar. Örneğin, değer nesnesi olarak tanımlanmış bir Name özelliği olan bir varlık türünü göz önünde bulundurabilirsiniz:
public class Customer
{
public int Id { get; set; }
public Name Name{ get; set; }
}
public class Name
{
public string First { get; set; }
public string MiddleInitial { get; set; }
public string Last { get; set; }
}
Bu, veritabanındaki JSON ile eşlenmiş:
modelBuilder.Entity<Customer>()
.Property(e => e.Name)
.HasConversion(
v => JsonSerializer.Serialize(v, (JsonSerializerOptions)null),
v => JsonSerializer.Deserialize<Name>(v, (JsonSerializerOptions)null));
Bir sorgu artık kullanılarak veya ContainsFreeText özelliğinin türü olmasına rağmen Name yürütülebilirsiniz. string Örnek:
var result = context.Customers.Where(e => EF.Functions.Contains(e.Name, "Martin")).ToList();
Bu, SQL kullanırken aşağıdaki SQL Server:
SELECT [c].[Id], [c].[Name]
FROM [Customers] AS [c]
WHERE CONTAINS([c].[Name], N'Martin')
SQLite üzerinde ToString'i çevirme
GitHub Sorun: #17223. Bu özellik, tarafından @ralmsdeveloper. Çok teşekkürler!
ToString() çağrıları artık SQLite SQL sağlayıcısı kullanılırken veritabanına çevrilir. Bu, dize olmayan sütunları içeren metin aramaları için yararlı olabilir. Örneğin, telefon numaralarını User sayısal değer olarak depolar bir varlık türü düşünün:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public long PhoneNumber { get; set; }
}
ToString , s numarayı veritabanındaki bir dizeye dönüştürmek için kullanılabilir. Daha sonra bu dizeyi gibi bir işlevle kullanarak LIKE bir desenle eşan sayıları buluruz. Örneğin, 555 içeren tüm sayıları bulmak için:
var users = context.Users.Where(u => EF.Functions.Like(u.PhoneNumber.ToString(), "%555%")).ToList();
Bu, SQLite SQL aşağıdaki kodlara çevrilir:
SELECT "u"."Id", "u"."PhoneNumber", "u"."Username"
FROM "Users" AS "u"
WHERE CAST("u"."PhoneNumber" AS TEXT) LIKE '%555%'
SQL Server için ToString() çevirisinin EF Core 5.0'da desteklenene ve diğer veritabanı sağlayıcıları tarafından da desteklenene dikkat olun.
EF. Functions.Random
GitHub Sorun: #16141. Bu özellik, tarafından @RaymondHuy. Çok teşekkürler!
EF.Functions.Random , özel olarak 0 ile 1 arasında rastgele bir sayı döndüren bir veritabanı işleviyle eşler. Çeviriler EF Core, SQLite ve SQL Server için Cosmos. Örneğin, özelliğine User sahip bir varlık türünü göz önünde Popularity bulundurabilirsiniz:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public int Popularity { get; set; }
}
Popularity 1 ile 5 arasında değerlere sahip olabilir. Kullanarak EF.Functions.Random rastgele seçilen popülerliği olan tüm kullanıcıları iade etmek için bir sorgu yazabilirsiniz:
var users = context.Users.Where(u => u.Popularity == (int)(EF.Functions.Random() * 4.0) + 1).ToList();
Bu, SQL Server veritabanı SQL aşağıdaki SQL Server çevirir:
SELECT [u].[Id], [u].[Popularity], [u].[Username]
FROM [Users] AS [u]
WHERE [u].[Popularity] = (CAST((RAND() * 4.0E0) AS int) + 1)
IsNullOrWhitespace SQL Server geliştirilmiş çeviri
GitHub Sorun: #22916. Bu özellik, tarafından @Marusyk. Çok teşekkürler!
Şu sorguyu inceleyin:
var users = context.Users.Where(
e => string.IsNullOrWhiteSpace(e.FirstName)
|| string.IsNullOrWhiteSpace(e.LastName)).ToList();
6 EF Core 6.0'dan önce bu, SQL Server:
SELECT [u].[Id], [u].[FirstName], [u].[LastName]
FROM [Users] AS [u]
WHERE ([u].[FirstName] IS NULL OR (LTRIM(RTRIM([u].[FirstName])) = N'')) OR ([u].[LastName] IS NULL OR (LTRIM(RTRIM([u].[LastName])) = N''))
Bu çeviri, 6.0 EF Core şu şekilde geliştirilmiştir:
SELECT [u].[Id], [u].[FirstName], [u].[LastName]
FROM [Users] AS [u]
WHERE ([u].[FirstName] IS NULL OR ([u].[FirstName] = N'')) OR ([u].[LastName] IS NULL OR ([u].[LastName] = N''))
Bellek içinde sağlayıcı için sorgu tanımlama
GitHub Sorun: #24600.
Yeni bir yöntem, verilen varlık türü için bellek ToInMemoryQuery içinde veritabanına bir tanımlama sorgusu yazmak için kullanılabilir. Bu, özellikle de bu görünümler anahtarsız varlık türleri iade ettiylerinde, bellek içinde veritabanında görünümlerin eşdeğerini oluşturmak için en kullanışlıdır. Örneğin, Birleşik Krallık'ta tabanlı müşteriler için bir müşteri veritabanı düşünün. Her müşterinin bir adresi vardır:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public int Id { get; set; }
public string House { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
Şimdi, her bir kod sonrası alanda kaç müşteri olduğunu gösteren bu veriler üzerinde bir görünüme sahip olmak istediğinizi düşünün. Bunu temsil etmek için anahtarsız varlık türü oluşturabiliriz:
public class CustomerDensity
{
public string Postcode { get; set; }
public int CustomerCount { get; set; }
}
Ayrıca dbContext üzerinde bunun için bir DbSet özelliği ve diğer üst düzey varlık türleri için kümeler tanımlayın:
public DbSet<Customer> Customers { get; set; }
public DbSet<CustomerDensity> CustomerDensities { get; set; }
Ardından OnModelCreating içinde, için döndürülacak verileri tanımlayan bir LINQ sorgusu CustomerDensities yazabilirsiniz:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<CustomerDensity>()
.HasNoKey()
.ToInMemoryQuery(
() => Customers
.GroupBy(c => c.Address.Postcode.Substring(0, 3))
.Select(
g =>
new CustomerDensity
{
Postcode = g.Key,
CustomerCount = g.Count()
}));
}
Daha sonra bu sorgu diğer DbSet özelliği gibi sorgu olabilir:
var results = context.CustomerDensities.ToList();
Tek parametreli Alt Dizeyi Çevirme
GitHub Sorun: #20173. Bu özellik, tarafından @stevendarby. Çok teşekkürler!
EF Core 6.0 artık kullanımlarını tek bir string.Substring bağımsız değişkenle çevirir. Örnek:
var result = context.Customers
.Select(a => new { Name = a.Name.Substring(3) })
.FirstOrDefault(a => a.Name == "hur");
Bu, SQL kullanırken aşağıdaki SQL Server:
SELECT TOP(1) SUBSTRING([c].[Name], 3 + 1, LEN([c].[Name])) AS [Name]
FROM [Customers] AS [c]
WHERE SUBSTRING([c].[Name], 3 + 1, LEN([c].[Name])) = N'hur'
Gezinti dışı koleksiyonlar için bölünmüş sorgular
GitHub Sorun: #21234.
EF Core linq sorgusunu birden çok sorguya bölmeyi SQL destekler. Bu EF Core 6.0'da, gezinti dışı koleksiyonların sorgu projeksiyonda yer alan durumlarını içerecek şekilde genişletildi.
Aşağıda, tek bir sorguya veya birden çok SQL Server sorguya yapılan çeviriyi gösteren örnek sorgular yer almaktadır.
Örnek 1:
LINQ sorgusu:
context.Customers
.Select(
c => new
{
c,
Orders = c.Orders
.Where(o => o.Id > 1)
})
.ToList();
Tek SQL sorgu:
SELECT [c].[Id], [t].[Id], [t].[CustomerId], [t].[OrderDate]
FROM [Customers] AS [c]
LEFT JOIN (
SELECT [o].[Id], [o].[CustomerId], [o].[OrderDate]
FROM [Order] AS [o]
WHERE [o].[Id] > 1
) AS [t] ON [c].[Id] = [t].[CustomerId]
ORDER BY [c].[Id]
Birden SQL sorgu:
SELECT [c].[Id]
FROM [Customers] AS [c]
ORDER BY [c].[Id]
SELECT [t].[Id], [t].[CustomerId], [t].[OrderDate], [c].[Id]
FROM [Customers] AS [c]
INNER JOIN (
SELECT [o].[Id], [o].[CustomerId], [o].[OrderDate]
FROM [Order] AS [o]
WHERE [o].[Id] > 1
) AS [t] ON [c].[Id] = [t].[CustomerId]
ORDER BY [c].[Id]
Örnek 2:
LINQ sorgusu:
context.Customers
.Select(
c => new
{
c,
OrderDates = c.Orders
.Where(o => o.Id > 1)
.Select(o => o.OrderDate)
})
.ToList();
Tek SQL sorgu:
SELECT [c].[Id], [t].[OrderDate], [t].[Id]
FROM [Customers] AS [c]
LEFT JOIN (
SELECT [o].[OrderDate], [o].[Id], [o].[CustomerId]
FROM [Order] AS [o]
WHERE [o].[Id] > 1
) AS [t] ON [c].[Id] = [t].[CustomerId]
ORDER BY [c].[Id]
Birden SQL sorgu:
SELECT [c].[Id]
FROM [Customers] AS [c]
ORDER BY [c].[Id]
SELECT [t].[Id], [t].[CustomerId], [t].[OrderDate], [c].[Id]
FROM [Customers] AS [c]
INNER JOIN (
SELECT [o].[Id], [o].[CustomerId], [o].[OrderDate]
FROM [Order] AS [o]
WHERE [o].[Id] > 1
) AS [t] ON [c].[Id] = [t].[CustomerId]
ORDER BY [c].[Id]
Örnek 3:
LINQ sorgusu:
context.Customers
.Select(
c => new
{
c,
OrderDates = c.Orders
.Where(o => o.Id > 1)
.Select(o => o.OrderDate)
.Distinct()
})
.ToList();
Tek SQL sorgu:
SELECT [c].[Id], [t].[OrderDate]
FROM [Customers] AS [c]
OUTER APPLY (
SELECT DISTINCT [o].[OrderDate]
FROM [Order] AS [o]
WHERE ([c].[Id] = [o].[CustomerId]) AND ([o].[Id] > 1)
) AS [t]
ORDER BY [c].[Id]
Birden SQL sorgu:
SELECT [c].[Id]
FROM [Customers] AS [c]
ORDER BY [c].[Id]
SELECT [t].[OrderDate], [c].[Id]
FROM [Customers] AS [c]
CROSS APPLY (
SELECT DISTINCT [o].[OrderDate]
FROM [Order] AS [o]
WHERE ([c].[Id] = [o].[CustomerId]) AND ([o].[Id] > 1)
) AS [t]
ORDER BY [c].[Id]
Koleksiyon için katılırken son ORDER BY yan tümcesini kaldırma
GitHub Sorun: #19828.
İlgili bire çok varlıkları yüklerken, EF Core ilgili tüm varlıkların birlikte grup olduğundan emin olmak için ORDER BY yan tümceleri ekler. Ancak, EF'nin gerekli gruplamaları oluşturması için son ORDER BY yan tümcesi gerekli değildir ve performans üzerinde bir etkisi olabilir. Bu nedenle EF Core 6.0'da bu yan tümce kaldırılır.
Örneğin, şu sorguyu düşünün:
context.Customers
.Select(
e => new
{
e.Id,
FirstOrder = e.Orders.Where(i => i.Id == 1).ToList()
})
.ToList();
Bu EF Core 5.0 SQL Server sorgu şu şekilde çevrilir:
SELECT [c].[Id], [t].[Id], [t].[CustomerId], [t].[OrderDate]
FROM [Customers] AS [c]
LEFT JOIN (
SELECT [o].[Id], [o].[CustomerId], [o].[OrderDate]
FROM [Order] AS [o]
WHERE [o].[Id] = 1
) AS [t] ON [c].[Id] = [t].[CustomerId]
ORDER BY [c].[Id], [t].[Id]
6.0 EF Core yerine şu şekilde çevrilir:
SELECT [c].[Id], [t].[Id], [t].[CustomerId], [t].[OrderDate]
FROM [Customers] AS [c]
LEFT JOIN (
SELECT [o].[Id], [o].[CustomerId], [o].[OrderDate]
FROM [Order] AS [o]
WHERE [o].[Id] = 1
) AS [t] ON [c].[Id] = [t].[CustomerId]
ORDER BY [c].[Id]
Sorguları dosya adı ve satır numarasıyla etiketleme
GitHub Sorun: #14176. Bu özellik, tarafından @michalczerwinski. Çok teşekkürler!
Sorgu etiketleri, LINQ sorgusuna metin etiketi ekleyerek oluşturulan sorguya dahil SQL. Bu EF Core 6.0'da, sorguları LINQ kodunun dosya adı ve satır numarasıyla etiketlemek için kullanılabilir. Örnek:
var results1 = context
.Customers
.TagWithCallSite()
.Where(c => c.Name.StartsWith("A"))
.ToList();
Bu durum, SQL Server kullanırken SQL oluşturulan SQL Server:
-- file: C:\dotnet\efdocs\samples\core\Miscellaneous\NewInEFCore6\TagWithFileAndLineSample.cs:21
SELECT [c].[Id], [c].[Name]
FROM [Customers] AS [c]
WHERE [c].[Name] IS NOT NULL AND ([c].[Name] LIKE N'A%')
Sahip olunan isteğe bağlı bağımlı işleme değişiklikleri
GitHub Sorun: #24558.
Bir tabloyu asıl varlığıyla paylaştığında isteğe bağlı bağımlı bir varlığın var olup olmadığını bilmek karmaşık bir hale gelir. Bunun nedeni, bağımlının mevcut olup olmadığı ne olursa olsun sorumluya ihtiyacı olduğundan tabloda bağımlı için bir satır olmasıdır. Bunu belirsiz bir şekilde işlemenin yolu, bağımlının en az bir gerekli özelik olduğundan emin olmaktır. Gerekli bir özellik null olamaz, bu özellik için sütundaki değerin null olması, bağımlı varlığın mevcut olmadığını gösterir.
Örneğin, her müşterinin Customer sahip olduğu bir sınıf Address düşünün:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string House { get; set; }
public string Street { get; set; }
public string City { get; set; }
[Required]
public string Postcode { get; set; }
}
Adres isteğe bağlıdır; başka bir anlama gelir; adresiz bir müşteriyi kaydetmek için geçerlidir:
context.Customers1.Add(
new()
{
Name = "Foul Ole Ron"
});
Ancak müşterinin bir adresi varsa, bu adres en az null olmayan bir postcodea sahip olmalıdır:
context.Customers1.Add(
new()
{
Name = "Havelock Vetinari",
Address = new()
{
Postcode = "AN1 1PL",
}
});
Bu, özelliğini olarak işaretleyerek Postcode emin Required olmaktır.
Artık müşteriler sorgulansa Postcode sütunu null ise bu, müşterinin adresi olmadığını ve gezinti özelliğinin Customer.Address null olarak bırakılı olduğu anlamına gelir. Örneğin, müşteriler üzerinden tekrarlar ve Adresin null olup olduğunu kontrol eder:
foreach (var customer in context.Customers1)
{
Console.Write(customer.Name);
if (customer.Address == null)
{
Console.WriteLine(" has no address.");
}
else
{
Console.WriteLine($" has postcode {customer.Address.Postcode}.");
}
}
Aşağıdaki sonuçları üretir:
Foul Ole Ron has no address.
Havelock Vetinari has postcode AN1 1PL.
Bunun yerine adresin hiçbir özelliğinin gerekli olmadığını göz önünde bulundurabilirsiniz:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string House { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
Artık hem adresi olmayan bir müşteriyi hem de tüm adres özelliklerinin null olduğu bir adresi olan bir müşteriyi kaydetmek mümkündür:
context.Customers2.Add(
new()
{
Name = "Foul Ole Ron"
});
context.Customers2.Add(
new()
{
Name = "Havelock Vetinari",
Address = new()
});
Ancak veritabanı sütunlarını doğrudan sorgulayarak göreceğimiz gibi veritabanında bu iki durum ayırt edilemez:
Id Name House Street City Postcode
1 Foul Ole Ron NULL NULL NULL NULL
2 Havelock Vetinari NULL NULL NULL NULL
Bu nedenle, EF Core 6.0 artık isteğe bağlı bir bağımlıyı tüm özelliklerinin null olduğu yere kaydederek sizi uyaracak. Örnek:
uyarma: 27.09.2021 09:25:01.338 RelationalEventId.OptionalDependentWithAllNullPropertiesWarning[20704] (Microsoft.EntityFrameworkCore.Update) Birincil anahtar değerleri {CustomerId: -2147482646} olan 'Address' türündeki varlık, tablo paylaşımını kullanan isteğe bağlı bir bağımlıdır. Varlığın var olup olmadığını belirlemek için varsayılan olmayan değere sahip herhangi bir özelliği yoktur. Bu, sorgulanan tüm özellikler varsayılan değerlere ayarlanmış bir örnek yerine hiçbir nesne örneği oluşturulmayacak anlamına gelir. İç içe geçmiş tüm bağımlılar da kaybolur. Herhangi bir örneği yalnızca varsayılan değerlerle kaydetme veya gelen gezintiyi modelde gerekli olarak işaretleme.
Bu durum, isteğe bağlı bağımlının aynı tabloya da eşlenmiş, daha isteğe bağlı bir bağımlı için sorumlu gibi davranarak daha da karmaşık hale gelir. 6.0'EF Core bir uyarı yerine, yalnızca iç içe isteğe bağlı bağımlı durumlara izin veser. Örneğin, aşağıdaki modeli göz önünden alalım; burada ve sahibine aittir ve ContactInfoCustomer şu Address modele ContactInfo aittir:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public ContactInfo ContactInfo { get; set; }
}
public class ContactInfo
{
public string Phone { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string House { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
Şimdi null ise, EF Core isteğe bağlı ise adresin kendisi veriye sahip olsa bile bir ContactInfo.PhoneAddress örneği oluşturmaz. Bu tür bir model için EF Core 6.0 aşağıdaki özel durumu oluşturur:
System.InvalidOperationException: 'ContactInfo' varlık türü, tablo paylaşımı kullanılarak isteğe bağlı bir bağımlıdır ve varlığın mevcut olup olmadığını belirlemek için herhangi bir gerekli paylaşılmayan özellik olmadan diğer bağımlıları içerir. Tüm null değere sahip özellikler veritabanında null değer içeriyorsa sorguda bir nesne örneği oluşturulmaz ve iç içe bağımlı değerlerin kaybedilebilir olması gerekir. Diğer özellikler için null değerlere sahip örnekler oluşturmak için gerekli bir özellik ekleyin veya her zaman bir örnek oluşturmak için gelen gezintiyi gerekli olarak işaretlenin.
Burada en alt satır, isteğe bağlı bağımlının tüm null değerdilebilir özellik değerlerini içerenin ve bir tabloyu sorumlusuyla paylaştığı durumdan kaçınmaktır. Bunu önlemenin üç kolay yolu vardır:
- Bağımlı gerekli hale gelir. Bu, bağımlı varlığın tüm özellikleri null olsa bile, sorgu sonrasında her zaman bir değere sahip olduğu anlamına gelir.
- Bağımlının, yukarıda açıklandığı gibi en az bir gerekli özellik içerdiğini emin olun.
- Sorumluyla bir tablo paylaşmak yerine isteğe bağlı bağımlıları kendi tablolarına kaydedin.
Bağımlı, gezintide özniteliği Required kullanılarak gerekli hale olabilir:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public Address Address { get; set; }
}
public class Address
{
public string House { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string Postcode { get; set; }
}
Veya içinde gerekli olduğunu belirterek OnModelCreating de olabilir:
modelBuilder.Entity<WithRequiredNavigation.Customer>(
b =>
{
b.OwnsOne(e => e.Address);
b.Navigation(e => e.Address).IsRequired();
});
'de kullanmak üzere tablolar belirterek bağımlılar farklı bir tabloya OnModelCreating kaydedilebilir:
modelBuilder
.Entity<WithDifferentTable.Customer>(
b =>
{
b.ToTable("Customers");
b.OwnsOne(
e => e.Address,
b => b.ToTable("CustomerAddresses"));
});
İç içe isteğe bağlı bağımlı durumlar da dahil olmak GitHub daha fazla isteğe bağlı bağımlı örnek için GitHub optionalDependentsSample'a bakın.
Yeni eşleme öznitelikleri
EF Core 6.0, veritabanıyla eşlenmiş şekilde değiştirmek için koda uygulanabilir birkaç yeni öznitelik içerir.
UnicodeAttribute
GitHub Sorun: #19794. Bu özellik, tarafından @RaymondHuy. Çok teşekkürler!
6.0 EF Core başlayarak, bir dize özelliği artık veritabanı türünü doğrudan belirtmeden eşleme özniteliği kullanılarak Unicode olmayan bir sütuna eşleniyor. Örneğin, Uluslararası Standart Kitap Numarası BookBook için "ISBN 978-3-16-148410-0" şeklinde bir özelliği olan bir varlık türü düşünün:
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
[Unicode(false)]
[MaxLength(22)]
public string Isbn { get; set; }
}
ISBN'ler unicode olmayan karakterler içerene kadar öznitelik, Unicode olmayan bir Unicode dize türünün kullanılmaya neden olur. Ayrıca, MaxLength veritabanı sütunlarının boyutunu sınırlamak için kullanılır. Örneğin, bir SQL Server veritabanı sütunuyla sonuç varchar(22) verir:
CREATE TABLE [Book] (
[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NULL,
[Isbn] varchar(22) NULL,
CONSTRAINT [PK_Book] PRIMARY KEY ([Id]));
Not
EF Core özellikleri varsayılan olarak Unicode sütunlarıyla eşler. UnicodeAttribute veritabanı sistemi yalnızca Unicode türlerini desteklediği zaman yoksayılır.
PrecisionAttribute
GitHub Sorun: #17914. Bu özellik, tarafından @RaymondHuy. Çok teşekkürler!
Veritabanı sütunlarının duyarlığı ve ölçeği artık doğrudan veritabanı türü belirtmeden eşleme öznitelikleri kullanılarak yalıtabilirsiniz. Örneğin, ondalık Product özelliğine sahip bir varlık türü Price düşünün:
public class Product
{
public int Id { get; set; }
[Precision(precision: 10, scale: 2)]
public decimal Price { get; set; }
}
EF Core özelliği duyarlık 10 ve ölçek 2'ye sahip bir veritabanı sütunuyla eşler. Örneğin, SQL Server:
CREATE TABLE [Product] (
[Id] int NOT NULL IDENTITY,
[Price] decimal(10,2) NOT NULL,
CONSTRAINT [PK_Product] PRIMARY KEY ([Id]));
EntityTypeConfigurationAttribute
GitHub Sorun: #23163. Bu özellik, tarafından @KaloyanIT. Çok teşekkürler!
IEntityTypeConfiguration TEntity > örnekleri, her varlık türü için > yapılandırmasının kendi yapılandırma sınıfında yer alan izinlerini sağlar. Örnek:
public class BookConfiguration : IEntityTypeConfiguration<Book>
{
public void Configure(EntityTypeBuilder<Book> builder)
{
builder
.Property(e => e.Isbn)
.IsUnicode(false)
.HasMaxLength(22);
}
}
Normalde bu yapılandırma sınıfının örneği başlat olmalı ve DbContext.OnModelCreating içinde içine çağrılmış olur. Örnek:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
new BookConfiguration().Configure(modelBuilder.Entity<Book>());
}
6.0 EF Core başlayarak, varlık türüne, uygun yapılandırmayı bulup EF Core şekilde yer alan bir EntityTypeConfigurationAttribute kullanılabilir. Örnek:
[EntityTypeConfiguration(typeof(BookConfiguration))]
public class Book
{
public int Id { get; set; }
public string Title { get; set; }
public string Isbn { get; set; }
}
Bu öznitelik, EF Core türü modele ekli olduğunda belirtilen IEntityTypeConfigurationBook uygulamanın kullan anlamına gelir. Varlık türü, normal mekanizmalardan biri kullanılarak modele dahil edilir. Örneğin, varlık türü için bir DbSet TEntity > özelliği oluşturarak:
public class BooksContext : DbContext
{
public DbSet<Book> Books { get; set; }
//...
Veya OnModelCreating'e kaydederek:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Book>();
}
Not
EntityTypeConfigurationAttribute türleri bir derlemede otomatik olarak keşfedilmayacak. Öznitelik bu varlık türünde keşfedilmeden önce modele varlık türleri eklenmiştir.
Model geliştirmeleri
EF Core 6.0, yeni eşleme özniteliklerine ek olarak model oluşturma sürecinde birkaç geliştirme daha içerir.
Seyrek SQL Server desteği
GitHub Sorun: #8023.
SQL Server sütunları, null değerleri depolamak için iyileştirilmiş sıradan sütunlardır. Nadiren kullanılan bir alt türün özelliklerinin tablodaki çoğu satır için null sütun değerlerine neden olduğu TPH devralma eşlemesi kullanılırken bu yararlı olabilir. Örneğin, ile ForumModerator genişleten bir sınıf ForumUser düşünün:
public class ForumUser
{
public int Id { get; set; }
public string Username { get; set; }
}
public class ForumModerator : ForumUser
{
public string ForumName { get; set; }
}
Milyonlarca kullanıcı olabilir ve bunların yalnızca birkaçı denetleyicidir. Bu, ForumName eşlemenin seyrek olarak burada anlamlı olabileceği anlamına gelir. Bu artık IsSparseIsSparse Örnek:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<ForumModerator>()
.Property(e => e.ForumName)
.IsSparse();
}
EF Core geçişler sütunu seyrek olarak işaretletir. Örnek:
CREATE TABLE [ForumUser] (
[Id] int NOT NULL IDENTITY,
[Username] nvarchar(max) NULL,
[Discriminator] nvarchar(max) NOT NULL,
[ForumName] nvarchar(max) SPARSE NULL,
CONSTRAINT [PK_ForumUser] PRIMARY KEY ([Id]));
Not
Seyrek sütunların sınırlamaları vardır. Seyrek sütunların senaryo SQL Server emin olmak için seyrek sütunlar belgelerini okuduğundan emin olun.
HasConversion API geliştirmeleri
GitHub Sorun: #25468.
6.0 EF Core önce yöntemlerin genel aşırı yüklemeleri, dönüştürülecek türü belirtmek HasConversionHasConversion Örneğin, bir Currency enum düşünün:
public enum Currency
{
UsDollars,
PoundsSterling,
Euros
}
EF Core, kullanılarak bu enum değerlerini "UsDollars", "PoundStirling" ve "Euro" dizeleri olarak kaydedecek şekilde HasConversion<string> yaydı. Örnek:
modelBuilder.Entity<TestEntity1>()
.Property(e => e.Currency)
.HasConversion<string>();
6.0 EF Core başlayarak, genel tür bunun yerine bir değer dönüştürücü türü belirterek. Bu, yerleşik değer dönüştürücülerinden biri olabilir. Örneğin, enum değerlerini veritabanında 16 bitlik sayılar olarak depolamak için:
modelBuilder.Entity<TestEntity2>()
.Property(e => e.Currency)
.HasConversion<EnumToNumberConverter<Currency, short>>();
Veya özel bir değer dönüştürücü türü olabilir. Örneğin, enum değerlerini para birimi simgeleri olarak depolar bir dönüştürücü düşünün:
public class CurrencyToSymbolConverter : ValueConverter<Currency, string>
{
public CurrencyToSymbolConverter()
: base(
v => v == Currency.PoundsSterling ? "£" : v == Currency.Euros ? "€" : "$",
v => v == "£" ? Currency.PoundsSterling : v == "€" ? Currency.Euros : Currency.UsDollars)
{
}
}
Bu artık genel yöntem kullanılarak HasConversion yalıtıldığında:
modelBuilder.Entity<TestEntity3>()
.Property(e => e.Currency)
.HasConversion<CurrencyToSymbolConverter>();
Çoka çok ilişkiler için daha az yapılandırma
GitHub Sorun: #21535.
İki varlık türü arasındaki belirsiz çoka çok ilişkileri kural tarafından keşfedildi. Gerektiğinde veya istenirse gezintiler açıkça belirtilebilir. Örnek:
modelBuilder.Entity<Cat>()
.HasMany(e => e.Humans)
.WithMany(e => e.Cats);
Her iki durumda da EF Core türü arasında birleştirme varlığı olarak hareket etmek için temel alınan Dictionary<string, object> bir paylaşılan varlık oluşturulur. 6.0 EF Core başlayarak, ek yapılandırmaya gerek kalmadan yalnızca bu türü UsingEntity değiştirmek için yapılandırmaya eklenebilir. Örnek:
modelBuilder.Entity<Cat>()
.HasMany(e => e.Humans)
.WithMany(e => e.Cats)
.UsingEntity<CatHuman>();
Ayrıca, JOIN varlık türü, açık olarak sol ve sağ ilişkilerin belirtilmesi gerekmeden daha fazla yapılandırılabilir. Örnek:
modelBuilder.Entity<Cat>()
.HasMany(e => e.Humans)
.WithMany(e => e.Cats)
.UsingEntity<CatHuman>(
e => e.HasKey(e => new { e.CatsId, e.HumansId }));
Son olarak, tam yapılandırma sağlanabilir. Örnek:
modelBuilder.Entity<Cat>()
.HasMany(e => e.Humans)
.WithMany(e => e.Cats)
.UsingEntity<CatHuman>(
e => e.HasOne<Human>().WithMany().HasForeignKey(e => e.CatsId),
e => e.HasOne<Cat>().WithMany().HasForeignKey(e => e.HumansId),
e => e.HasKey(e => new { e.CatsId, e.HumansId }));
Değer dönüştürücülerinin null dönüştürmesine izin ver
GitHub sorunu: #13850.
Önemli
Aşağıda özetlenen sorunlar nedeniyle, ValueConverter null değerlere dönüştürmeye izin veren oluşturucuların [EntityFrameworkInternal] EF Core 6,0 sürümü için ile işaretlenmiş olması gerekir. Bu oluşturucuların kullanılması şimdi bir yapı uyarısı oluşturacak.
Değer dönüştürücüler genellikle null değeri başka bir değere dönüştürmeye izin vermez. Bunun nedeni, aynı değer dönüştürücünün hem null yapılabilir hem de null yapılamayan türler için kullanılabileceği anlamına gelir. Bu, FK genellikle null değer atanabilir ve PK 'nin olmadığı PK/FK birleşimleri için çok kullanışlıdır.
EF Core 6,0 ' den başlayarak, null değerleri dönüştüren bir değer Dönüştürücüsü oluşturulabilir. Bununla birlikte, bu özelliğin doğrulanması, çok sayıda izin ile uygulamada çok sorunlu olduğunu gösterdi. Örnek:
- Depodaki null değerine dönüştürme hatalı sorgular oluşturuyor
- Depodaki null değerinden değer dönüştürme hatalı sorgular oluşturuyor
- Değer dönüştürücüler, veritabanı sütununun aynı değere dönüştüren birden çok farklı değere sahip olduğu durumları işlemez
- Değer dönüştürücülerinin Nullable sütunları değiştirmesine izin ver
Bunlar önemsiz sorunlar değildir ve sorgu sorunları için kolayca algılanmazlar. Bu nedenle, bu özelliği EF Core 6,0 için iç olarak işaretledik. Yine de kullanabilirsiniz, ancak bir derleyici uyarısı alırsınız. Uyarısı kullanılarak devre dışı bırakılabilir #pragma warning disable EF1001 .
Null değerleri dönüştürmenin yararlı olması, veritabanının null içermesi ve varlık türünün özellik için başka bir varsayılan değer kullanmak istemektedir. Örneğin, varsayılan değeri "bilinmiyor" olan bir Enum düşünün:
public enum Breed
{
Unknown,
Burmese,
Tonkinese
}
Ancak, iyisi bilinmiyorsa veritabanında null değerler olabilir. EF Core 6,0 ' de, bunun için bir değer Dönüştürücüsü kullanılabilir:
public class BreedConverter : ValueConverter<Breed, string>
{
#pragma warning disable EF1001
public BreedConverter()
: base(
v => v == Breed.Unknown ? null : v.ToString(),
v => v == null ? Breed.Unknown : Enum.Parse<Breed>(v),
convertsNulls: true)
{
}
#pragma warning restore EF1001
}
"Unknown" olarak ayarlanmış kediler, Breed sütunu veritabanında null olarak ayarlanır. Örnek:
context.AddRange(
new Cat { Name = "Mac", Breed = Breed.Unknown },
new Cat { Name = "Clippy", Breed = Breed.Burmese },
new Cat { Name = "Sid", Breed = Breed.Tonkinese });
context.SaveChanges();
SQL Server şu ekleme deyimlerini oluşturur:
info: 9/27/2021 19:43:55.966 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (16ms) [Parameters=[@p0=NULL (Size = 4000), @p1='Mac' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
INSERT INTO [Cats] ([Breed], [Name])
VALUES (@p0, @p1);
SELECT [Id]
FROM [Cats]
WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
info: 9/27/2021 19:43:55.983 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (0ms) [Parameters=[@p0='Burmese' (Size = 4000), @p1='Clippy' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
INSERT INTO [Cats] ([Breed], [Name])
VALUES (@p0, @p1);
SELECT [Id]
FROM [Cats]
WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
info: 9/27/2021 19:43:55.983 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command)
Executed DbCommand (0ms) [Parameters=[@p0='Tonkinese' (Size = 4000), @p1='Sid' (Size = 4000)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
INSERT INTO [Cats] ([Breed], [Name])
VALUES (@p0, @p1);
SELECT [Id]
FROM [Cats]
WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
DbContext Factory geliştirmeleri
AddDbContextFactory Ayrıca DbContext 'i doğrudan kaydeder
GitHub sorunu: #25164.
Bazen, uygulamalar bağımlılığı ekleme (D.I.) içinde her ikisi de bir DbContext türü ve bu türün bağlamları için bir fabrika olması yararlı olur. kapsayıcı. Bu, örneğin, DbContext 'in kapsamlı bir örneğinin istek kapsamından çözülmesini sağlar, ancak fabrika gerektiğinde birden çok bağımsız örnek oluşturmak için kullanılabilir.
Bunu desteklemek için, AddDbContextFactory artık DbContext türünü kapsamlı bir hizmet olarak kaydeder. Örneğin, bu kaydı uygulamanın D.I. kapsayıcısında göz önünde bulundurun:
var container = services
.AddDbContextFactory<SomeDbContext>(
builder => builder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFCoreSample"))
.BuildServiceProvider();
Bu kayıtla, fabrika, önceki sürümlerde olduğu gibi kök D.I. kapsayıcısından çözülebilir:
var factory = container.GetService<IDbContextFactory<SomeDbContext>>();
using (var context = factory.CreateDbContext())
{
// Contexts obtained from the factory must be explicitly disposed
}
Fabrika tarafından oluşturulan bağlam örneklerinin açıkça atılmalıdır.
Ayrıca, bir DbContext örneği doğrudan bir kapsayıcı kapsamından çözülebilir:
using (var scope = container.CreateScope())
{
var context = scope.ServiceProvider.GetService<SomeDbContext>();
// Context is disposed when the scope is disposed
}
Bu durumda, kapsayıcı kapsamı atıldığı zaman bağlam örneği de atıldı; bağlam açıkça atılmamalıdır.
Daha yüksek bir düzeyde bu, fabrikasının DbContext 'in diğer D.I. türlerine eklenebilir olduğu anlamına gelir. Örnek:
private class MyController2
{
private readonly IDbContextFactory<SomeDbContext> _contextFactory;
public MyController2(IDbContextFactory<SomeDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
public void DoSomething()
{
using var context1 = _contextFactory.CreateDbContext();
using var context2 = _contextFactory.CreateDbContext();
var results1 = context1.Blogs.ToList();
var results2 = context2.Blogs.ToList();
// Contexts obtained from the factory must be explicitly disposed
}
}
Veya
private class MyController1
{
private readonly SomeDbContext _context;
public MyController1(SomeDbContext context)
{
_context = context;
}
public void DoSomething()
{
var results = _context.Blogs.ToList();
// Injected context is disposed when the request scope is disposed
}
}
DbContextFactory, DbContext parametresiz oluşturucuyu yok sayıyor
GitHub sorunu: #24124.
EF Core 6,0, hem parametresiz DbContext oluşturucusuna hem de DbContextOptions fabrika tarafından kaydedildiği sırada aynı bağlam türünde kullanılmak üzere alan bir oluşturucuya izin veriyor AddDbContextFactory . Örneğin, Yukarıdaki örneklerde kullanılan bağlam her iki Oluşturucu içerir:
public class SomeDbContext : DbContext
{
public SomeDbContext()
{
}
public SomeDbContext(DbContextOptions<SomeDbContext> options)
: base(options)
{
}
public DbSet<Blog> Blogs { get; set; }
}
DbContext havuzlaması, bağımlılık ekleme olmadan kullanılabilir
GitHub sorunu: #24137.
PooledDbContextFactoryTürü, uygulamanızın bağımlılık ekleme kapsayıcısına sahip olması gerekmeden, DbContext örnekleri için tek başına bir havuz olarak kullanılabilmesi için genel kullanıma açıldı. Havuz, DbContextOptions bağlam örnekleri oluşturmak için kullanılacak bir örneğiyle oluşturulur:
var options = new DbContextOptionsBuilder<SomeDbContext>()
.EnableSensitiveDataLogging()
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFCoreSample")
.Options;
var factory = new PooledDbContextFactory<SomeDbContext>(options);
Daha sonra fabrika, örnek oluşturmak ve havuzu oluşturmak için kullanılabilir. Örnek:
for (var i = 0; i < 2; i++)
{
using var context1 = factory.CreateDbContext();
Console.WriteLine($"Created DbContext with ID {context1.ContextId}");
using var context2 = factory.CreateDbContext();
Console.WriteLine($"Created DbContext with ID {context2.ContextId}");
}
Örnekler atıldıklarında havuza döndürülür.
Çeşitli geliştirmeler
Son olarak, EF Core yukarıda kapsanmayan alanlarda birçok geliştirme içerir.
Tablo oluştururken [ColumnAttribute. Order] kullanın
GitHub sorunu: #10059.
OrderÖzelliği ColumnAttribute artık geçişler içeren bir tablo oluştururken sütunları sıralamak için kullanılabilir. Örneğin, aşağıdaki modeli göz önünde bulundurun:
public class EntityBase
{
public int Id { get; set; }
public DateTime UpdatedOn { get; set; }
public DateTime CreatedOn { get; set; }
}
public class PersonBase : EntityBase
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Employee : PersonBase
{
public string Department { get; set; }
public decimal AnnualSalary { 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; }
[Required]
public string Postcode { get; set; }
}
Varsayılan olarak, öncelikle birincil anahtar sütunlarını EF Core, varlık türü ve sahip olunan türlerin özelliklerinin yanı sıra temel türlerden son özellikler tarafından takip edin. Örneğin, aşağıdaki tablo SQL Server oluşturulmuştur:
CREATE TABLE [EmployeesWithoutOrdering] (
[Id] int NOT NULL IDENTITY,
[Department] nvarchar(max) NULL,
[AnnualSalary] decimal(18,2) NOT NULL,
[Address_House] nvarchar(max) NULL,
[Address_Street] nvarchar(max) NULL,
[Address_City] nvarchar(max) NULL,
[Address_Postcode] nvarchar(max) NULL,
[UpdatedOn] datetime2 NOT NULL,
[CreatedOn] datetime2 NOT NULL,
[FirstName] nvarchar(max) NULL,
[LastName] nvarchar(max) NULL,
CONSTRAINT [PK_EmployeesWithoutOrdering] PRIMARY KEY ([Id]));
EF Core 6,0 ' de, ColumnAttribute farklı bir sütun sırası belirtmek için kullanılabilir. Örnek:
public class EntityBase
{
[Column(Order = 1)]
public int Id { get; set; }
[Column(Order = 98)]
public DateTime UpdatedOn { get; set; }
[Column(Order = 99)]
public DateTime CreatedOn { get; set; }
}
public class PersonBase : EntityBase
{
[Column(Order = 2)]
public string FirstName { get; set; }
[Column(Order = 3)]
public string LastName { get; set; }
}
public class Employee : PersonBase
{
[Column(Order = 20)]
public string Department { get; set; }
[Column(Order = 21)]
public decimal AnnualSalary { get; set; }
public Address Address { get; set; }
}
[Owned]
public class Address
{
[Column("House", Order = 10)]
public string House { get; set; }
[Column("Street", Order = 11)]
public string Street { get; set; }
[Column("City", Order = 12)]
public string City { get; set; }
[Required]
[Column("Postcode", Order = 13)]
public string Postcode { get; set; }
}
SQL Server, oluşturulan tablo artık şu şekilde olur:
CREATE TABLE [EmployeesWithOrdering] (
[Id] int NOT NULL IDENTITY,
[FirstName] nvarchar(max) NULL,
[LastName] nvarchar(max) NULL,
[House] nvarchar(max) NULL,
[Street] nvarchar(max) NULL,
[City] nvarchar(max) NULL,
[Postcode] nvarchar(max) NULL,
[Department] nvarchar(max) NULL,
[AnnualSalary] decimal(18,2) NOT NULL,
[UpdatedOn] datetime2 NOT NULL,
[CreatedOn] datetime2 NOT NULL,
CONSTRAINT [PK_EmployeesWithOrdering] PRIMARY KEY ([Id]));
Bu, FistName ve LastName sütunlarının bir temel türünde tanımlansa bile en üste taşınmasını sağlar. Sütun sırası değerlerinde boşluk olduğunu ve birden çok türetilmiş tür tarafından kullanıldığında bile her zaman son görüntülenen sütunları her zaman yerleştirmek için kullanılmasına izin vermeyi unutmayın.
Bu örnek ayrıca, ColumnAttribute hem sütun adını hem de sırayı belirtmek için aynı şekilde nasıl kullanılabileceğini gösterir.
Sütun sıralaması ' de API kullanılarak da yapılandırılabilir ModelBuilderOnModelCreating . Örnek:
modelBuilder.Entity<UsingModelBuilder.Employee>(
entityBuilder =>
{
entityBuilder.Property(e => e.Id).HasColumnOrder(1);
entityBuilder.Property(e => e.FirstName).HasColumnOrder(2);
entityBuilder.Property(e => e.LastName).HasColumnOrder(3);
entityBuilder.OwnsOne(
e => e.Address,
ownedBuilder =>
{
ownedBuilder.Property(e => e.House).HasColumnName("House").HasColumnOrder(4);
ownedBuilder.Property(e => e.Street).HasColumnName("Street").HasColumnOrder(5);
ownedBuilder.Property(e => e.City).HasColumnName("City").HasColumnOrder(6);
ownedBuilder.Property(e => e.Postcode).HasColumnName("Postcode").HasColumnOrder(7).IsRequired();
});
entityBuilder.Property(e => e.Department).HasColumnOrder(8);
entityBuilder.Property(e => e.AnnualSalary).HasColumnOrder(9);
entityBuilder.Property(e => e.UpdatedOn).HasColumnOrder(10);
entityBuilder.Property(e => e.CreatedOn).HasColumnOrder(11);
});
Model Oluşturucu ile sıralama, HasColumnOrder ile belirtilen herhangi bir sıraya göre önceliklidir ColumnAttribute . Bu HasColumnOrder , farklı özelliklerde öznitelikler aynı sipariş numarasını belirtmediği zaman, çakışmaları çözme dahil olmak üzere özniteliklerle yapılan sıralamayı geçersiz kılmak için kullanılabilir.
Önemli
Genel durumda, çoğu veritabanının yalnızca tablo oluşturulduğunda sütunları sıralamayı desteklediğini unutmayın. Bu, sütun sırası özniteliğinin varolan bir tablodaki sütunları yeniden sıralamak için kullanılamayacağı anlamına gelir. Bu, geçişlerin tüm tabloyu yeni sütun siparişleriyle yeniden derlebileceği, bu işlem için bir önemli özel durumu SQLite ' dir.
EF Core en az API
GitHub sorunu: #25192.
.NET Core 6,0, .NET uygulamalarında geleneksel olarak gereken çok sayıda ortak kodu kaldırarak Basitleştirilmiş "minimal API 'Ler" özelliği olan güncelleştirilmiş şablonlar içerir.
EF Core 6,0, DbContext türünü kaydeden ve veritabanı sağlayıcısı için yapılandırmayı tek bir satıra sağlayan yeni bir genişletme yöntemi içerir. Örnek:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSqlite<MyDbContext>("Data Source=mydatabase.db");
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSqlServer<MyDbContext>(@"Server=(localdb)\mssqllocaldb;Database=MyDatabase");
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddCosmos<MyDbContext>(
"https://localhost:8081",
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==");
Bunlar tam olarak eşdeğerdir:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MyDbContext>(
options => options.UseSqlite("Data Source=mydatabase.db"));
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MyDbContext>(
options => options.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=MyDatabase"));
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<MyDbContext>(
options => options.UseCosmos(
"https://localhost:8081",
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="));
Not
EF Core minimal API 'Ler yalnızca bir DbContext ve sağlayıcının çok temel kaydını ve yapılandırmasını destekler. AddDbContextEF Core ' AddDbContextPoolAddDbContextFactory de kullanılabilen tüm kayıt ve yapılandırma türlerine erişmek için,, vb. kullanın.
Minimum API 'Ler hakkında daha fazla bilgi edinmek için bu kaynaklara göz atın:
- .NET 6 ' da Maria Naggaga tarafından En düşük API 'leri sunma
- Scott Hanselman 'un bloguna .net 6 MINIMAL API Todo örnek oynatma
- David Fowler tarafından bir bakışta GİST en az API 'leri
- GitHub için En düşük BIR API oynatma
SaveChangesAsync içinde eşitleme bağlamını koru
GitHub sorunu: #23971.
Zaman uyumsuz kod olan her yerde, görevi ayarlamak için 5,0 sürümündeki EF Core kodunu değiştirdik . Bu, EF Core kullanımı için genellikle daha iyi bir seçenektir. Ancak, zaman uyumsuz veritabanı işlemi tamamlandıktan sonra EF Core oluşturulan değerleri izlenen varlıklara ayarlayacağından Savechangesasync özel bir durumdur. Bu değişiklikler daha sonra, örneğin U.I. iş parçacığında çalıştırmak zorunda olabilecek bildirimleri tetikleyebilir. Bu nedenle, bu değişikliği yalnızca Savechangesasync yöntemi için EF Core 6,0 ' de geri döndürüyoruz.
Bellek içi veritabanı: gerekli özelliklerin doğrulanması null değil
GitHub Sorun: #10613. Bu özellik, tarafından @fagnercarvalho. Çok teşekkürler!
Bellek EF Core veritabanı, gerekli olarak işaretlenen bir özellik için null değer kaydetme girişimi yapılırsa bir özel durum oluşturur. Örneğin, gerekli User özelliğine sahip bir tür Username düşünün:
public class User
{
public int Id { get; set; }
[Required]
public string Username { get; set; }
}
Bir varlığı null değerle kaydetme girişimi Username aşağıdaki özel durumla sonuçlandır:
Microsoft.EntityFrameworkCore.DbUpdateException: '{Id: 1}' anahtar değerine sahip 'User' varlık türünün örneği için gerekli '{'Username'}' özellikleri eksik.
Gerekirse bu doğrulama devre dışı bırakılabilir. Örnek:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.LogTo(Console.WriteLine, new[] { InMemoryEventId.ChangesSaved })
.UseInMemoryDatabase("UserContextWithNullCheckingDisabled", b => b.EnableNullChecks(false));
}
Tanılama ve kesme için komut kaynağı bilgileri
GitHub Sorun: #23719. Bu özellik, tarafından @Giorgi. Çok teşekkürler!
Tanılama kaynaklarına ve kesmecilere sağlanan, artık komutu oluşturmak için EF'nin hangi kısmının sorumlu CommandEventData olduğunu belirten bir enum değeri içerir. Bu, tanılama veya kesme noktası içinde filtre olarak kullanılabilir. Örneğin, yalnızca tarafından gelen komutlara uygulanan bir kesme noktası istiyor SaveChanges olabiliriz:
public class CommandSourceInterceptor : DbCommandInterceptor
{
public override InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
if (eventData.CommandSource == CommandSource.SaveChanges)
{
Console.WriteLine($"Saving changes for {eventData.Context!.GetType().Name}:");
Console.WriteLine();
Console.WriteLine(command.CommandText);
}
return result;
}
}
Bu, yalnızca geçişler SaveChanges ve sorgular oluşturan bir uygulamada kullanılan olaylar için kesme noktası filtreler. Örnek:
Saving changes for CustomersContext:
SET NOCOUNT ON;
INSERT INTO [Customers] ([Name])
VALUES (@p0);
SELECT [Id]
FROM [Customers]
WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();
Daha iyi geçici değerleri işleme
GitHub Sorun: #24245.
EF Core varlık türü örneklerde geçici değerleri açığa çıkarmaz. Örneğin, depo tarafından Blog oluşturulan anahtara sahip bir varlık türünü göz önünde bulundurabilirsiniz:
public class Blog
{
public int Id { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
Anahtar Id özelliği, bağlam tarafından iz hemen iz sürece Blog geçici bir değer verir. Örneğin, çağrılırken: DbContext.Add
var blog = new Blog();
context.Add(blog);
Geçici değer, bağlam değişikliği izleyiciden elde edilir, ancak varlık örneğine ayarlanmaz. Örneğin, bu kod:
Console.WriteLine($"Blog.Id value on entity instance = {blog.Id}");
Console.WriteLine($"Blog.Id value tracked by EF = {context.Entry(blog).Property(e => e.Id).CurrentValue}");
Aşağıdaki çıkışı oluşturur:
Blog.Id value on entity instance = 0
Blog.Id value tracked by EF = -2147482647
Geçici değerin yanlışlıkla geçici olarak kabul edilebilir uygulama koduna sızmasını önlemektedir. Ancak bazen geçici değerlerle doğrudan ilgilenebilirsiniz. Örneğin, bir uygulama, yabancı anahtarları kullanarak ilişkileri oluşturmak için kullanılamadan önce varlık grafı için kendi geçici değerlerini oluşturmak istiyor olabilir. Bu, değerleri geçici olarak açıkça işaretleyerek yapılabilir. Örnek:
var blog = new Blog { Id = -1 };
var post1 = new Post { Id = -1, BlogId = -1 };
var post2 = new Post { Id = -2, BlogId = -1 };
context.Add(blog).Property(e => e.Id).IsTemporary = true;
context.Add(post1).Property(e => e.Id).IsTemporary = true;
context.Add(post2).Property(e => e.Id).IsTemporary = true;
Console.WriteLine($"Blog has explicit temporary ID = {blog.Id}");
Console.WriteLine($"Post 1 has explicit temporary ID = {post1.Id} and FK to Blog = {post1.BlogId}");
Console.WriteLine($"Post 2 has explicit temporary ID = {post2.Id} and FK to Blog = {post2.BlogId}");
6 EF Core 6.0'da değer, artık geçici olarak işaretlenmiş olsa bile varlık örneğinde kalır. Örneğin, yukarıdaki kod aşağıdaki çıkışı oluşturur:
Blog has explicit temporary ID = -1
Post 1 has explicit temporary ID = -1 and FK to Blog = -1
Post 2 has explicit temporary ID = -2 and FK to Blog = -1
Benzer şekilde, varlık örnekleri tarafından EF Core geçici değerler açıkça varlık örneklerine ayarlandırı ve geçici değerler olarak işaretlenir. Bu, geçici anahtar değerlerini kullanarak yeni varlıklar arasındaki ilişkileri açıkça ayarlamak için kullanılabilir. Örnek:
var post1 = new Post();
var post2 = new Post();
var blogIdEntry = context.Entry(blog).Property(e => e.Id);
blog.Id = blogIdEntry.CurrentValue;
blogIdEntry.IsTemporary = true;
var post1IdEntry = context.Add(post1).Property(e => e.Id);
post1.Id = post1IdEntry.CurrentValue;
post1IdEntry.IsTemporary = true;
post1.BlogId = blog.Id;
var post2IdEntry = context.Add(post2).Property(e => e.Id);
post2.Id = post2IdEntry.CurrentValue;
post2IdEntry.IsTemporary = true;
post2.BlogId = blog.Id;
Console.WriteLine($"Blog has generated temporary ID = {blog.Id}");
Console.WriteLine($"Post 1 has generated temporary ID = {post1.Id} and FK to Blog = {post1.BlogId}");
Console.WriteLine($"Post 2 has generated temporary ID = {post2.Id} and FK to Blog = {post2.BlogId}");
Sonuçta:
Blog has generated temporary ID = -2147482647
Post 1 has generated temporary ID = -2147482647 and FK to Blog = -2147482647
Post 2 has generated temporary ID = -2147482646 and FK to Blog = -2147482647
EF Core C# boş değere değiştirilebilir başvuru türleri için açıklama ek açıklama
GitHub Sorun: #19007.
Kod EF Core artık C# boş değere değiştirilebilir başvuru türlerini (NRT) kullanıyor. Bu, kendi kodunuzdan EF Core 6.0'ı kullanırken null kullanım için doğru derleyici göstergelerini alacaktır.
Microsoft.Data.Sqlite 6.0
İpucu
Aşağıdaki örnek kodu GitHub'dan indirerek aşağıda gösterilen tüm örnekleri çalıştırabilir ve hata GitHub.
Bağlantı Havuzu
GitHub Sorun: #13837.
Veritabanı bağlantılarını mümkün olduğunca az süre açık tutmak yaygın bir uygulamadır. Bu, bağlantı kaynağıyla ilgili bir iletişimin önlenmesine yardımcı olur. Bu nedenle gibi kitaplıklar EF Core işlemi gerçekleştirmeden önce bağlantıyı hemen açıp hemen sonra yeniden kapatır. Örneğin, şu kodu EF Core düşünün:
Console.WriteLine("Starting query...");
Console.WriteLine();
var users = context.Users.ToList();
Console.WriteLine();
Console.WriteLine("Query finished.");
Console.WriteLine();
foreach (var user in users)
{
if (user.Username.Contains("microsoft"))
{
user.Username = "msft:" + user.Username;
Console.WriteLine("Starting SaveChanges...");
Console.WriteLine();
context.SaveChanges();
Console.WriteLine();
Console.WriteLine("SaveChanges finished.");
}
}
Bu koddan gelen ve bağlantıların günlüğe kaydetmesi açık olan çıktı şöyledir:
Starting query...
dbug: 8/27/2021 09:26:57.810 RelationalEventId.ConnectionOpened[20001] (Microsoft.EntityFrameworkCore.Database.Connection)
Opened connection to database 'main' on server 'C:\dotnet\efdocs\samples\core\Miscellaneous\NewInEFCore6\bin\Debug\net6.0\test.db'.
dbug: 8/27/2021 09:26:57.813 RelationalEventId.ConnectionClosed[20003] (Microsoft.EntityFrameworkCore.Database.Connection)
Closed connection to database 'main' on server 'test.db'.
Query finished.
Starting SaveChanges...
dbug: 8/27/2021 09:26:57.813 RelationalEventId.ConnectionOpened[20001] (Microsoft.EntityFrameworkCore.Database.Connection)
Opened connection to database 'main' on server 'C:\dotnet\efdocs\samples\core\Miscellaneous\NewInEFCore6\bin\Debug\net6.0\test.db'.
dbug: 8/27/2021 09:26:57.814 RelationalEventId.ConnectionClosed[20003] (Microsoft.EntityFrameworkCore.Database.Connection)
Closed connection to database 'main' on server 'test.db'.
SaveChanges finished.
Her işlem için bağlantının hızlı bir şekilde açılmıştır ve kapatılmıştır.
Ancak çoğu veritabanı sistemi için veritabanına fiziksel bağlantı açmak pahalı bir işlemdir. Bu nedenle ADO.NET sağlayıcılar bir fiziksel bağlantı havuzu oluşturabilir ve gerektiğinde bunları DbConnection örneklere kiralar.
Veritabanı erişimi genellikle yalnızca bir dosyaya erişirken SQLite biraz farklıdır. Bu, SQLite veritabanına bağlantı açmanın genellikle çok hızlı olduğu anlamına gelir. Ancak, her zaman böyle değildir. Örneğin, şifrelenmiş bir veritabanına bağlantı açmak çok yavaş olabilir. Bu nedenle, SQLite bağlantıları artık Microsoft.Data.Sqlite 6.0 kullanılırken havuza alındı.
DateOnly ve TimeOnly Desteği
GitHub Sorun: #24506.
Microsoft.Data.Sqlite 6.0, DateOnlyTimeOnly .NET 6'dan yeni ve türlerini destekler. Bunlar SQLite sağlayıcısıyla EF Core 6.0'da da kullanılabilir. Her zaman SQLite'da olduğu gibi, yerel tür sistemi bu türlerden gelen değerlerin desteklenen dört türden biri olarak depolanmış olması anlamına gelir. Microsoft.Data.Sqlite bunları olarak TEXT depolar. Örneğin, şu türleri kullanan bir varlık:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public DateOnly Birthday { get; set; }
public TimeOnly TokensRenewed { get; set; }
}
Haritalar SQLite veritabanında aşağıdaki tabloya bakın:
CREATE TABLE "Users" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Users" PRIMARY KEY AUTOINCREMENT,
"Username" TEXT NULL,
"Birthday" TEXT NOT NULL,
"TokensRenewed" TEXT NOT NULL);
Değerler daha sonra normal şekilde kaydedilebilir, sorguilebilir ve güncelleştirilebilir. Örneğin, linq EF Core şu şekildedir:
var users = context.Users.Where(u => u.Birthday < new DateOnly(1900, 1, 1)).ToList();
SQLite üzerinde aşağıdakine çevrilir:
SELECT "u"."Id", "u"."Birthday", "u"."TokensRenewed", "u"."Username"
FROM "Users" AS "u"
WHERE "u"."Birthday" < '1900-01-01'
Ve yalnızca 1900 CE'den önceki doğum günlerini döndürür:
Found 'ajcvickers'
Found 'wendy'
Kaydetme Noktaları API'si
GitHub Sorun: #20228.
Sağlayıcılarda kaydetme noktaları için ortak bir API'yi standart ADO.NET. Microsoft.Data.Sqlite artık aşağıdakiler dahil olmak üzere bu API'yi destekler:
- İşlemde bir kaydetme noktası oluşturmak için Save(String)
- Önceki bir kaydetme noktası için geri alma (Dize)
- Bir kaydetme noktası serbest bırakmak için Release(String)
Bir kaydetme noktası kullanmak, bir işlem parçasının tüm işlemi geri almadan geri alınarak geri alınarak tamamına olanak sağlar. Örneğin, aşağıdaki kod:
- İşlem oluşturur
- Veritabanına bir güncelleştirme gönderir
- Bir kaydetme noktası oluşturur
- Veritabanına başka bir güncelleştirme gönderir
- Daha önce oluşturulan kaydetme noktası geri döner
- İşlemleri işler
using var connection = new SqliteConnection("Command Timeout=60;DataSource=test.db");
connection.Open();
using var transaction = connection.BeginTransaction();
using (var command = connection.CreateCommand())
{
command.CommandText = @"UPDATE Users SET Username = 'ajcvickers' WHERE Id = 1";
command.ExecuteNonQuery();
}
transaction.Save("MySavepoint");
using (var command = connection.CreateCommand())
{
command.CommandText = @"UPDATE Users SET Username = 'wfvickers' WHERE Id = 2";
command.ExecuteNonQuery();
}
transaction.Rollback("MySavepoint");
transaction.Commit();
Bu, ilk güncelleştirmenin veritabanına işlenmesini sağlarken, ikinci güncelleştirme işlemi işlemeden önce kaydetme noktası geri alınarak işlanmaz.
Bağlantı dizesinde komut zaman aşımı
GitHub Sorun: #22505. Bu özellik, tarafından @nmichels. Çok teşekkürler!
ADO.NET sağlayıcılar iki ayrı zaman aşımını destekler:
- Veritabanına bağlantı yaparken en uzun bekleme sürelerini belirleyen bağlantı zaman aşımı.
- Bir komutun yürütülmesini tamamlamak için en uzun bekleme süresini belirleyen komut zaman aşımı.
Komut zaman aşımı DbCommand.CommandTimeout kullanılarak koddan ayarlandırabilirsiniz. Birçok sağlayıcı artık bu komut zaman aşımını bağlantı dizesinde de ortaya çıkarmaktadır. Microsoft.Data.Sqlite, bağlantı dizesi anahtar sözcüğüyle bu Command Timeout eğilimi takip ediyor. Örneğin, "Command Timeout=60;DataSource=test.db" bağlantı tarafından oluşturulan komutlar için varsayılan zaman aşımı olarak 60 saniye kullanır.
İpucu
Sqlite, Default Timeout için bir eş anlamlı olarak Command Timeout davranır ve tercih edilirse bunun yerine kullanılabilir.