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:
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 OnModelCreating
kullanı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 OwnsOne
hem 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ı HasKey
yapı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:
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 ShippingAddress
değ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:
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 DetailedOrder
ayrı 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:Order
OrderDetails
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
ModelBuilder
sahip 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.