Gelişmiş tablo eşleme

EF Core, varlık türlerini veritabanındaki tablolara eşleme konusunda çok fazla esneklik sunar. EF tarafından oluşturulmamış bir veritabanı kullanmanız gerektiğinde bu daha da kullanışlı hale gelir.

Aşağıdaki teknikler tablolar açısından açıklanmıştır, ancak görünümlere eşlenirken de aynı sonuç elde edilebilir.

Tablo bölme

EF Core, iki veya daha fazla varlığı tek bir satıra eşlemeye olanak tanır. Buna tablo bölme veya tablo paylaşımı denir.

Yapılandırma

Varlık türlerini bölen tabloyu kullanmak için, birincil anahtarların aynı tabloya eşlenmesi ve aynı tablodaki bir varlık türünün birincil anahtarı ile diğerinin arasında en az bir ilişkinin yapılandırılması gerekir.

Tablo bölmeye yönelik yaygın bir senaryo, daha yüksek performans veya kapsülleme için tablodaki sütunların yalnızca bir alt kümesini kullanmaktır.

Bu örnekte Order , öğesinin DetailedOrderbir alt kümesini temsil eder.

public class Order
{
    public int Id { get; set; }
    public OrderStatus? Status { get; set; }
    public DetailedOrder DetailedOrder { get; set; }
}
public class DetailedOrder
{
    public int Id { get; set; }
    public OrderStatus? Status { get; set; }
    public string BillingAddress { get; set; }
    public string ShippingAddress { get; set; }
    public byte[] Version { get; set; }
}

ile aynı sütuna Order.Statuseşlemek DetailedOrder.Status için çağırdığımız Property(o => o.Status).HasColumnName("Status") gerekli yapılandırmaya ek olarak.

modelBuilder.Entity<DetailedOrder>(
    dob =>
    {
        dob.ToTable("Orders");
        dob.Property(o => o.Status).HasColumnName("Status");
    });

modelBuilder.Entity<Order>(
    ob =>
    {
        ob.ToTable("Orders");
        ob.Property(o => o.Status).HasColumnName("Status");
        ob.HasOne(o => o.DetailedOrder).WithOne()
            .HasForeignKey<DetailedOrder>(o => o.Id);
        ob.Navigation(o => o.DetailedOrder).IsRequired();
    });

Bahşiş

Daha fazla bağlam için örnek projenin tamamına bakın.

Kullanım

Tablo bölme kullanarak varlıkları kaydetme ve sorgulama işlemi diğer varlıklarla aynı şekilde yapılır:

using (var context = new TableSplittingContext())
{
    context.Database.EnsureDeleted();
    context.Database.EnsureCreated();

    context.Add(
        new Order
        {
            Status = OrderStatus.Pending,
            DetailedOrder = new DetailedOrder
            {
                Status = OrderStatus.Pending,
                ShippingAddress = "221 B Baker St, London",
                BillingAddress = "11 Wall Street, New York"
            }
        });

    context.SaveChanges();
}

using (var context = new TableSplittingContext())
{
    var pendingCount = context.Orders.Count(o => o.Status == OrderStatus.Pending);
    Console.WriteLine($"Current number of pending orders: {pendingCount}");
}

using (var context = new TableSplittingContext())
{
    var order = context.DetailedOrders.First(o => o.Status == OrderStatus.Pending);
    Console.WriteLine($"First pending order will ship to: {order.ShippingAddress}");
}

İsteğe bağlı bağımlı varlık

Bağımlı varlık NULL tarafından kullanılan tüm sütunlar veritabanındaysa sorgulandığında bunun örneği oluşturulmaz. Bu, asıl öğedeki ilişki özelliğinin null olacağı isteğe bağlı bir bağımlı varlığı modellemeye olanak tanır. Ayrıca, bağımlının tüm özellikleri isteğe bağlıysa ve olarak ayarlanırsa nullda bu durum gerçekleşebilir ve bu da beklenemez.

Ancak, ek denetim sorgu performansını etkileyebilir. Ayrıca, bağımlı varlık türünün kendi bağımlıları varsa, bir örneğin oluşturulup oluşturulmayacağını belirlemek önemsiz hale gelir. Bu sorunlardan kaçınmak için bağımlı varlık türü gerekli olarak işaretlenebilir, daha fazla bilgi için bkz . Bire bir bağımlılar gerekli.

Eşzamanlılık belirteçleri

Tabloyu paylaşan varlık türlerinden herhangi birinin eşzamanlılık belirteci varsa, diğer tüm varlık türlerine de dahil edilmelidir. Aynı tabloya eşlenen varlıklardan yalnızca biri güncelleştirildiğinde eski eşzamanlılık belirteci değerini önlemek için bu gereklidir.

Eşzamanlılık belirtecinin tüketen koda açık olmasını önlemek için gölge özellik olarak bir tane oluşturmak mümkündür:

modelBuilder.Entity<Order>()
    .Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");

modelBuilder.Entity<DetailedOrder>()
    .Property(o => o.Version).IsRowVersion().HasColumnName("Version");

Devralma

Bu bölüme devam etmeden önce devralmayla ilgili ayrılmış sayfanın okunmasını öneririz.

Tablo bölme kullanan bağımlı türlerin devralma hiyerarşisi olabilir, ancak bazı sınırlamalar vardır:

  • Türetilmiş türler aynı tabloyla eşlenemeyeceğinden bağımlı varlık türü TPC eşlemesini kullanamaz.
  • Bağımlı varlık türü TPT eşlemesini kullanabilir , ancak yalnızca kök varlık türü tablo bölmeyi kullanabilir.
  • Asıl varlık türü TPC kullanıyorsa, yalnızca alt öğeleri olmayan varlık türleri tablo bölmeyi kullanabilir. Aksi takdirde, bağımlı sütunların türetilmiş türlere karşılık gelen tablolarda çoğaltılması ve tüm etkileşimleri karmaşık hale getirmesi gerekir.

Varlık bölme

EF Core, bir varlığı iki veya daha fazla tablodaki satırlarla eşlemeye olanak tanır. Buna varlık bölme adı verilir.

Yapılandırma

Örneğin, müşteri verilerini barındıran üç tablo içeren bir veritabanı düşünün:

  • Customers Müşteri bilgileri tablosu
  • PhoneNumbers Müşterinin telefon numarası için bir tablo
  • Addresses Müşterinin adresi için bir tablo

SQL Server'da bu tabloların tanımları şunlardır:

CREATE TABLE [Customers] (
    [Id] int NOT NULL IDENTITY,
    [Name] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Customers] PRIMARY KEY ([Id])
);
    
CREATE TABLE [PhoneNumbers] (
    [CustomerId] int NOT NULL,
    [PhoneNumber] nvarchar(max) NULL,
    CONSTRAINT [PK_PhoneNumbers] PRIMARY KEY ([CustomerId]),
    CONSTRAINT [FK_PhoneNumbers_Customers_CustomerId] FOREIGN KEY ([CustomerId]) REFERENCES [Customers] ([Id]) ON DELETE CASCADE
);

CREATE TABLE [Addresses] (
    [CustomerId] int NOT NULL,
    [Street] nvarchar(max) NOT NULL,
    [City] nvarchar(max) NOT NULL,
    [PostCode] nvarchar(max) NULL,
    [Country] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Addresses] PRIMARY KEY ([CustomerId]),
    CONSTRAINT [FK_Addresses_Customers_CustomerId] FOREIGN KEY ([CustomerId]) REFERENCES [Customers] ([Id]) ON DELETE CASCADE
);

Bu tabloların her biri genellikle türler arasındaki ilişkilerle kendi varlık türlerine eşlenir. Ancak, üç tablonun hepsi her zaman birlikte kullanılıyorsa, bunların tümünü tek bir varlık türüne eşlemek daha kullanışlı olabilir. Örnek:

public class Customer
{
    public Customer(string name, string street, string city, string? postCode, string country)
    {
        Name = name;
        Street = street;
        City = city;
        PostCode = postCode;
        Country = country;
    }

    public int Id { get; set; }
    public string Name { get; set; }
    public string? PhoneNumber { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string? PostCode { get; set; }
    public string Country { get; set; }
}

Bu, EF7'de varlık türündeki her bölme için çağrılarak SplitToTable güncelleştirilir. Örneğin, aşağıdaki kod varlık türünü Customersyukarıda gösterilen , PhoneNumbersve Addresses tablolarına bölerCustomer:

modelBuilder.Entity<Customer>(
    entityBuilder =>
    {
        entityBuilder
            .ToTable("Customers")
            .SplitToTable(
                "PhoneNumbers",
                tableBuilder =>
                {
                    tableBuilder.Property(customer => customer.Id).HasColumnName("CustomerId");
                    tableBuilder.Property(customer => customer.PhoneNumber);
                })
            .SplitToTable(
                "Addresses",
                tableBuilder =>
                {
                    tableBuilder.Property(customer => customer.Id).HasColumnName("CustomerId");
                    tableBuilder.Property(customer => customer.Street);
                    tableBuilder.Property(customer => customer.City);
                    tableBuilder.Property(customer => customer.PostCode);
                    tableBuilder.Property(customer => customer.Country);
                });
    });

Ayrıca, gerekirse tabloların her biri için farklı sütun adlarının belirtilebildiğine de dikkat edin. Ana tablonun sütun adını yapılandırmak için bkz. Tabloya özgü model yapılandırması.

Bağlama yabancı anahtarını yapılandırma

Eşlenen tabloları bağlayan FK, bildirildiği özelliklerin aynısını hedefler. Normalde, yedekli olacağından veritabanında oluşturulmaz. Ancak varlık türünün birden fazla tabloyla eşlenmesi için bir özel durum vardır. Modellerini değiştirmek için fluent API'si ilişki yapılandırmasını kullanabilirsiniz:

modelBuilder.Entity<Customer>()
    .HasOne<Customer>()
    .WithOne()
    .HasForeignKey<Customer>(a => a.Id)
    .OnDelete(DeleteBehavior.Restrict);

Sınırlamalar

  • Varlık bölme hiyerarşilerindeki varlık türleri için kullanılamaz.
  • Ana tablodaki herhangi bir satır için, bölünmüş tabloların her birinde bir satır olmalıdır (parçalar isteğe bağlı değildir).

Tabloya özgü model yapılandırması

Bazı eşleme desenleri, aynı CLR özelliğinin birden çok farklı tablonun her birindeki bir sütuna eşlenmesine neden olur. EF7, bu sütunların farklı adlara sahip olmasını sağlar. Örneğin, basit bir devralma hiyerarşisini göz önünde bulundurun:

public abstract class Animal
{
    public int Id { get; set; }
    public string Breed { get; set; } = null!;
}

public class Cat : Animal
{
    public string? EducationalLevel { get; set; }
}

public class Dog : Animal
{
    public string? FavoriteToy { get; set; }
}

TPT devralma eşleme stratejisiyle bu türler üç tabloya eşlenir. Ancak, her tablodaki birincil anahtar sütunu farklı bir ada sahip olabilir. Örnek:

CREATE TABLE [Animals] (
    [Id] int NOT NULL IDENTITY,
    [Breed] nvarchar(max) NOT NULL,
    CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);

CREATE TABLE [Cats] (
    [CatId] int NOT NULL,
    [EducationalLevel] nvarchar(max) NULL,
    CONSTRAINT [PK_Cats] PRIMARY KEY ([CatId]),
    CONSTRAINT [FK_Cats_Animals_CatId] FOREIGN KEY ([CatId]) REFERENCES [Animals] ([Id]) ON DELETE CASCADE
);

CREATE TABLE [Dogs] (
    [DogId] int NOT NULL,
    [FavoriteToy] nvarchar(max) NULL,
    CONSTRAINT [PK_Dogs] PRIMARY KEY ([DogId]),
    CONSTRAINT [FK_Dogs_Animals_DogId] FOREIGN KEY ([DogId]) REFERENCES [Animals] ([Id]) ON DELETE CASCADE
);

EF7, bu eşlemenin iç içe tablo oluşturucusu kullanılarak yapılandırılmasına izin verir:

modelBuilder.Entity<Animal>().ToTable("Animals");

modelBuilder.Entity<Cat>()
    .ToTable(
        "Cats",
        tableBuilder => tableBuilder.Property(cat => cat.Id).HasColumnName("CatId"));

modelBuilder.Entity<Dog>()
    .ToTable(
        "Dogs",
        tableBuilder => tableBuilder.Property(dog => dog.Id).HasColumnName("DogId"));

TPC devralma eşlemesi ile Breed özelliği, farklı tablolardaki farklı sütun adlarına da eşlenebilir. Örneğin, aşağıdaki TPC tablolarını göz önünde bulundurun:

CREATE TABLE [Cats] (
    [CatId] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
    [CatBreed] nvarchar(max) NOT NULL,
    [EducationalLevel] nvarchar(max) NULL,
    CONSTRAINT [PK_Cats] PRIMARY KEY ([CatId])
);

CREATE TABLE [Dogs] (
    [DogId] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
    [DogBreed] nvarchar(max) NOT NULL,
    [FavoriteToy] nvarchar(max) NULL,
    CONSTRAINT [PK_Dogs] PRIMARY KEY ([DogId])
);

EF7 şu tablo eşlemesini destekler:

modelBuilder.Entity<Animal>().UseTpcMappingStrategy();

modelBuilder.Entity<Cat>()
    .ToTable(
        "Cats",
        builder =>
        {
            builder.Property(cat => cat.Id).HasColumnName("CatId");
            builder.Property(cat => cat.Breed).HasColumnName("CatBreed");
        });

modelBuilder.Entity<Dog>()
    .ToTable(
        "Dogs",
        builder =>
        {
            builder.Property(dog => dog.Id).HasColumnName("DogId");
            builder.Property(dog => dog.Breed).HasColumnName("DogBreed");
        });