Sahip Olunan Varlık Türleri

EF Core, yalnızca diğer varlık türlerinin gezinti özelliklerinde görünebilen varlık türlerini modellemenizi sağlar. Bunlara sahip olunan varlık türleri denir. Sahip olunan varlık türünü içeren varlık sahibidir.

Sahip olunan varlıklar temelde sahibin bir parçasıdır ve bunlar olmadan var olamaz, kavramsal olarak toplamalara benzerler. Bu, sahip olunan varlığın, sahiple ilişkinin bağımlı tarafında tanım gereği olduğu anlamına gelir.

Türleri sahip olunan olarak yapılandırma

Çoğu sağlayıcıda varlık türleri hiçbir zaman kurala ait olarak yapılandırılmaz. Türü sahip olduğu gibi yapılandırmak için içindeki yöntemini OnModelCreating açıkça kullanmanız OwnsOne veya türüne OwnedAttribute açıklama eklemeniz gerekir. Azure Cosmos DB sağlayıcısı bunun bir özel durumudur. Azure Cosmos DB bir belge veritabanı olduğundan, sağlayıcı tüm ilgili varlık türlerini varsayılan olarak sahip olduğu şekilde yapılandırır.

Bu örnekte, StreetAddress kimlik özelliği olmayan bir türdür. Belirli bir siparişin sevkiyat adresini belirtmek için Sipariş türünün özelliği olarak kullanılır.

Başka bir varlık türünden başvurulduğunda sahip olunan varlık olarak işlemek için kullanabiliriz OwnedAttribute :

[Owned]
public class StreetAddress
{
    public string Street { get; set; }
    public string City { get; set; }
}
public class Order
{
    public int Id { get; set; }
    public StreetAddress ShippingAddress { get; set; }
}

özelliğinin OwnsOne varlık türünün Sahip Olunan Varlık Order olduğunu ShippingAddress belirtmek ve gerekirse ek modeller yapılandırmak için içindeki OnModelCreating yöntemini kullanmak da mümkündür.

modelBuilder.Entity<Order>().OwnsOne(p => p.ShippingAddress);

ShippingAddress özelliği türünde özelse Order yönteminin dize sürümünü OwnsOne kullanabilirsiniz:

modelBuilder.Entity<Order>().OwnsOne(typeof(StreetAddress), "ShippingAddress");

Yukarıdaki model aşağıdaki veritabanı şemasına eşlenmiştir:

Sahip olunan başvuru içeren varlık için veritabanı modelinin ekran görüntüsü

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

İpucu

Sahip olunan varlık türü gerekli olarak işaretlenebilir. Daha fazla bilgi için bkz . Gerekli bire bir bağımlılar .

Örtük anahtarlar

Başvuru gezintisi aracılığıyla OwnsOne yapılandırılan veya bulunan sahip türler her zaman sahiple bire bir ilişkiye sahiptir, bu nedenle yabancı anahtar değerleri benzersiz olduğundan kendi anahtar değerlerine ihtiyaç duyulmaz. Önceki örnekte, StreetAddress türünün bir anahtar özelliği tanımlaması gerekmez.

EF Core'un bu nesneleri nasıl izlediğini anlamak için, birincil anahtarın sahip olunan tür için gölge özelliği olarak oluşturulduğunu bilmek yararlıdır. Sahip olunan türün bir örneğinin anahtarının değeri, sahip örneğin anahtarının değeriyle aynı olacaktır.

Sahip olunan tür koleksiyonları

Sahip olunan türlerin koleksiyonunu yapılandırmak için içinde OnModelCreatingkullanınOwnsMany.

Sahip olunan türlerin birincil anahtara ihtiyacı vardır. .NET türünde iyi aday özellikleri yoksa EF Core bir tane oluşturmayı deneyebilir. Ancak, sahip olunan türler bir koleksiyon aracılığıyla tanımlandığında, sahip olan örneğin OwnsOnehem sahibine hem de birincil anahtarına yabancı anahtar olarak davranacak bir gölge özellik oluşturmak yeterli değildir. Her sahip için birden çok sahip türü örneği olabilir ve bu nedenle sahibin anahtarı her sahip örneği için benzersiz bir kimlik sağlamak için yeterli değildir.

Bunun en basit iki çözümü şunlardır:

  • Sahibine işaret eden yabancı anahtardan bağımsız olarak yeni bir özellik üzerinde vekil birincil anahtar tanımlama. Kapsanan değerlerin tüm sahipler arasında benzersiz olması gerekir (örneğin, Üst {1} öğeDe Alt {1}öğe varsa, Üst {2} öğe alt {1}öğeye sahip olamaz), dolayısıyla değerin doğal bir anlamı yoktur. Yabancı anahtar birincil anahtarın bir parçası olmadığından değerleri değiştirilebilir, bu nedenle bir alt öğeyi bir üst öğeden diğerine taşıyabilirsiniz, ancak bu genellikle toplu semantiklere karşı olur.
  • Bileşik anahtar olarak yabancı anahtarı ve ek bir özelliği kullanma. Ek özellik değerinin artık yalnızca belirli bir üst öğe için benzersiz olması gerekir (bu nedenle, Ebeveyn {1} alt {1,1} öğeye sahipse, Üst öğe {2} yine de Alt {2,1}öğeye sahip olabilir). Birincil anahtarın yabancı anahtarın bir parçası haline getirerek sahip ve sahip olunan varlık arasındaki ilişki sabit hale gelir ve toplam semantiği daha iyi yansıtır. EF Core varsayılan olarak bunu yapar.

Bu örnekte sınıfını Distributor kullanacağız.

public class Distributor
{
    public int Id { get; set; }
    public ICollection<StreetAddress> ShippingCenters { get; set; }
}

Varsayılan olarak, gezinti özelliği "DistributorId" ("DistributorId", "Id") aracılığıyla ShippingCenters başvurulan sahip olunan tür için kullanılan birincil anahtar FK ve "Id" benzersiz int bir değerdir.

Farklı bir birincil anahtar çağrısı HasKeyyapılandırmak için.

modelBuilder.Entity<Distributor>().OwnsMany(
    p => p.ShippingCenters, a =>
    {
        a.WithOwner().HasForeignKey("OwnerId");
        a.Property<int>("Id");
        a.HasKey("Id");
    });

Yukarıdaki model aşağıdaki veritabanı şemasına eşlenmiştir:

Sahip olunan koleksiyonu içeren varlık için veritabanı modelinin ekran görüntüsü

Tablo bölme ile sahip olunan türleri eşleme

İlişkisel veritabanları kullanılırken, varsayılan olarak sahip olunan başvuru türleri sahiple aynı tabloya eşlenir. Bunun için tablonun ikiye bölünmesi gerekir: bazı sütunlar sahibin verilerini depolamak için, bazı sütunlar ise sahip olunan varlığın verilerini depolamak için kullanılır. Bu, tablo bölme olarak bilinen yaygın bir özelliktir.

Varsayılan olarak EF Core, sahip olunan varlık türünün özellikleri için veritabanı sütunlarını desen Navigation_OwnedEntityProperty izler. Bu nedenle StreetAddress , özellikler 'Orders' tablosunda 'ShippingAddress_Street' ve 'ShippingAddress_City' adlarıyla görünür.

Bu sütunları yeniden adlandırmak için yöntemini kullanabilirsiniz HasColumnName .

modelBuilder.Entity<Order>().OwnsOne(
    o => o.ShippingAddress,
    sa =>
    {
        sa.Property(p => p.Street).HasColumnName("ShipsToStreet");
        sa.Property(p => p.City).HasColumnName("ShipsToCity");
    });

Not

Ignore gibi normal varlık türü yapılandırma yöntemlerinin çoğu aynı şekilde çağrılabilir.

Aynı .NET türünü birden çok sahip olunan tür arasında paylaşma

Sahip olunan varlık türü başka bir sahip olunan varlık türüyle aynı .NET türünde olabilir, bu nedenle .NET türü sahip olunan bir türü tanımlamak için yeterli olmayabilir.

Bu gibi durumlarda sahipten sahip olan varlığa işaret eden özellik, sahip olunan varlık türünün tanımlama gezintisi olur. EF Core açısından bakıldığında, tanımlama gezintisi .NET türüyle birlikte türün kimliğinin bir parçasıdır.

Örneğin, aşağıdaki sınıfta ShippingAddress ve BillingAddress ikisi de aynı .NET türündedir. StreetAddress

public class OrderDetails
{
    public DetailedOrder Order { get; set; }
    public StreetAddress BillingAddress { get; set; }
    public StreetAddress ShippingAddress { get; set; }
}

EF Core'un bu nesnelerin izlenen örneklerini nasıl ayırt ettiğini anlamak için, tanımlayıcı gezintinin sahibin anahtarının ve sahip olunan türün .NET türünün değeriyle birlikte örneğin anahtarının bir parçası haline geldiğini düşünmek yararlı olabilir.

İç içe sahip olunan türler

Bu örnekteOrderDetails, her iki StreetAddress tür de olan ve ShippingAddressdeğerlerine sahipBillingAddress. Ardından OrderDetails türüne DetailedOrder aittir.

public class DetailedOrder
{
    public int Id { get; set; }
    public OrderDetails OrderDetails { get; set; }
    public OrderStatus Status { get; set; }
}
public enum OrderStatus
{
    Pending,
    Shipped
}

Sahip olunan bir türe yapılan her gezinti, tamamen bağımsız yapılandırmaya sahip ayrı bir varlık türü tanımlar.

İç içe sahip olunan türlere ek olarak, sahip olunan bir tür, sahip olunan varlık bağımlı tarafta olduğu sürece sahip veya farklı bir varlık olabilecek normal bir varlığa başvurabilir. Bu özellik sahip olunan varlık türlerini EF6'daki karmaşık türlerden farklı olarak ayarlar.

public class OrderDetails
{
    public DetailedOrder Order { get; set; }
    public StreetAddress BillingAddress { get; set; }
    public StreetAddress ShippingAddress { get; set; }
}

Sahip olunan türleri yapılandırma

Bu modeli yapılandırmak için yöntemi akıcı bir çağrıda zincirleme OwnsOne yapmak mümkündür:

modelBuilder.Entity<DetailedOrder>().OwnsOne(
    p => p.OrderDetails, od =>
    {
        od.WithOwner(d => d.Order);
        od.Navigation(d => d.Order).UsePropertyAccessMode(PropertyAccessMode.Property);
        od.OwnsOne(c => c.BillingAddress);
        od.OwnsOne(c => c.ShippingAddress);
    });

Sahibine WithOwner işaret eden gezinti özelliğini tanımlamak için kullanılan çağrıya dikkat edin. Sahiplik ilişkisinin WithOwner() parçası olmayan sahip varlık türüne bir gezinti tanımlamak için bağımsız değişken olmadan çağrılmalıdır.

Hem hem de OrderDetails StreetAddressüzerinde kullanarak OwnedAttribute bu sonucu elde etmek de mümkündür.

Buna ek olarak, aramaya dikkat edin Navigation . Sahip olunan türlere yönelik gezinti özellikleri, sahip olunmayan gezinti özellikleri için olarak daha fazla yapılandırılabilir.

Yukarıdaki model aşağıdaki veritabanı şemasına eşlenmiştir:

İç içe sahip olunan başvurular içeren varlık için veritabanı modelinin ekran görüntüsü

Sahip olunan türleri ayrı tablolarda depolama

Ayrıca EF6 karmaşık türlerinden farklı olarak, sahip olunan türler sahibinden ayrı bir tabloda depolanabilir. Sahip olunan bir türü sahiple aynı tabloya eşleyen kuralı geçersiz kılmak için, çağırmanız ToTable ve farklı bir tablo adı sağlamanız yeterlidir. Aşağıdaki örnek, ve iki adresini uygulamasından DetailedOrderayrı bir tabloyla eşleyecektirOrderDetails:

modelBuilder.Entity<DetailedOrder>().OwnsOne(p => p.OrderDetails, od => { od.ToTable("OrderDetails"); });

Bunu yapmak için öğesini TableAttribute kullanmak da mümkündür, ancak sahip olunan türde birden çok gezinti olduğunda bunun başarısız olacağını unutmayın çünkü bu durumda birden çok varlık türü aynı tabloyla eşlenir.

Sahip olunan türleri sorgulama

Sahibi sorgularken sahip olunan türler varsayılan olarak eklenir. Sahip olunan türler ayrı bir tabloda depolanmış olsa bile yöntemini kullanmak Include gerekli değildir. Daha önce açıklanan modele bağlı olarak, aşağıdaki sorgu ve veritabanından sahip olan StreetAddresses ikisini alır:OrderOrderDetails

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

Sınırlamalar

Bu sınırlamalardan bazıları sahip olunan varlık türlerinin çalışma şekli için temeldir, ancak bazıları da gelecek sürümlerde kaldırabilmemiz gereken kısıtlamalardır:

Tasarıma göre kısıtlamalar

  • Sahip olunan bir tür için oluşturamazsınız DbSet<T> .
  • üzerinde ModelBuildersahip olunan bir türle çağrı Entity<T>() yapamazsınız.
  • Sahip olunan varlık türlerinin örnekleri birden çok sahip tarafından paylaşılamaz (bu, sahip olunan varlık türleri kullanılarak uygulanamayan değer nesneleri için iyi bilinen bir senaryodur).

Geçerli eksiklikler

  • Sahip olunan varlık türlerinin devralma hiyerarşileri olamaz

Önceki sürümlerdeki eksiklikler

  • EF Core 2.x'te sahip olunan varlık türlerine yönelik başvuru gezintileri, sahipten ayrı bir tabloya açıkça eşlenmedikleri sürece null olamaz.
  • EF Core 3.x'te sahiple aynı tabloya eşlenen sahip varlık türlerinin sütunları her zaman null atanabilir olarak işaretlenir.