Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
EF bir .NET türü hiyerarşisini bir veritabanına eşleyebilir. Bu, .NET varlıklarınızı temel ve türetilmiş türleri kullanarak her zamanki gibi koda yazmanızı ve EF'nin uygun veritabanı şemasını sorunsuz bir şekilde oluşturmasını, sorgular oluşturmasını vb. sağlar. Tür hiyerarşisinin nasıl eşlendiğine ilişkin gerçek ayrıntılar sağlayıcıya bağımlıdır; bu sayfada ilişkisel veritabanı bağlamında devralma desteği açıklanmaktadır.
Varlık türü hiyerarşi eşlemesi
Kural gereği EF, temel veya türetilmiş türleri otomatik olarak taramaz; Bu, hiyerarşinizdeki bir CLR türünün eşlenmesi istiyorsanız modelinizde bu türü açıkça belirtmeniz gerektiği anlamına gelir. Örneğin, bir hiyerarşinin yalnızca temel türünü belirtmek EF Core'un tüm alt türlerini örtük olarak içermesine neden olmaz.
Aşağıdaki örnek, Blog
ve onun alt sınıfı RssBlog
için bir DbSet kullanıma sunar.
Blog
başka bir alt sınıfı varsa modele dahil edilmeyecektir.
internal class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<RssBlog> RssBlogs { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
public class RssBlog : Blog
{
public string RssUrl { get; set; }
}
Uyarı
Veritabanı sütunları, TPH eşlemesi kullanılırken gerektiğinde otomatik olarak null yapılabilir hale gelir. Örneğin, normal RssUrl
örnekleri bu özelliğe sahip olmadığından Blog
sütunu boş değer alabilir.
Hiyerarşideki bir veya daha fazla varlık için DbSet
göstermek istemiyorsanız, bunların modele dahil olduğundan emin olmak için Fluent API'sini de kullanabilirsiniz.
Tavsiye
kurallarına bağlı değilseniz, temel türü açıkça HasBaseType
kullanarak belirtebilirsiniz. Bir varlık türünü hiyerarşiden kaldırmak için .HasBaseType((Type)null)
de kullanabilirsiniz.
Hiyerarşi başına tablo ve ayrıştırıcı yapılandırması
Varsayılan olarak, EF hiyerarşi başına tablo (TPH) desenini kullanarak devralmayı eşler. TPH, hiyerarşideki tüm türlerin verilerini depolamak için tek bir tablo kullanır ve her satırın hangi türü temsil ettiği belirlemek için ayrıştırıcı sütunu kullanılır.
Yukarıdaki model aşağıdaki veritabanı şemasına eşlenmiştir (her satırda hangi tür Discriminator
depolandığını tanımlayan örtük olarak oluşturulan Blog
sütununa dikkat edin).
kullanarak Blog varlık hiyerarşisini sorgulama sonuçlarının ekran görüntüsü
Ayırıcı sütunun adını ve türünü ve hiyerarşideki her türü tanımlamak için kullanılan değerleri yapılandırabilirsiniz:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasDiscriminator<string>("blog_type")
.HasValue<Blog>("blog_base")
.HasValue<RssBlog>("blog_rss");
}
Yukarıdaki örneklerde EF, ayrımcıyı örtük olarak hiyerarşinin temel varlığında gölge özelliği olarak eklemiştir. Bu özellik diğer özellikler gibi yapılandırılabilir:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property("blog_type")
.HasMaxLength(200);
}
Son olarak, ayrımcı varlığınızdaki normal bir .NET özelliğine de eşlenebilir:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasDiscriminator(b => b.BlogType);
modelBuilder.Entity<Blog>()
.Property(e => e.BlogType)
.HasMaxLength(200)
.HasColumnName("blog_type");
modelBuilder.Entity<RssBlog>();
}
TPH desenini kullanan türetilmiş varlıkları sorgularken, EF Core sorgudaki ayırıcı sütuna bir koşul ekler. Bu filtre, sonuçta bulunmayan temel türler veya eşdüzey türler için ek satır almamamızı sağlar. Bu filtre koşulu, temel varlık türü için atlanır, çünkü temel varlığı sorgulamak hiyerarşideki tüm varlıkların sonuçlarını alır. Bir sorgudan sonuçlar elde ederken modeldeki herhangi bir varlık türüne eşlenmeyen bir ayırıcı değerle karşılaşırsak, sonuçların nasıl gerçekleştirilmesi gerektiğini bilmediğimiz için bir özel durum oluştururuz. Bu hata yalnızca veritabanınızda EF modelinde eşlenmeyen ayırıcı değerleri içeren satırlar varsa oluşur. Bu tür verileriniz varsa, hiyerarşideki herhangi bir türü sorgulamak için her zaman filtre koşulu eklememiz gerektiğini belirtmek için EF Core modelindeki ayırıcı eşlemeyi tamamlanmamış olarak işaretleyebilirsiniz. Ayrıştırıcı yapılandırmasındaki IsComplete(false)
çağrısı eşlemenin tamamlanmamış olduğunu işaretler.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.HasDiscriminator()
.IsComplete(false);
}
Paylaşılan sütunlar
Varsayılan olarak, hiyerarşideki iki eşdüzey varlık türü aynı ada sahip bir özelliğe sahip olduğunda, bunlar iki ayrı sütuna eşlenir. Ancak, türleri aynıysa, aynı veritabanı sütununa eşlenebilir:
public class MyContext : DbContext
{
public DbSet<BlogBase> Blogs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.HasColumnName("Url");
modelBuilder.Entity<RssBlog>()
.Property(b => b.Url)
.HasColumnName("Url");
}
}
public abstract class BlogBase
{
public int BlogId { get; set; }
}
public class Blog : BlogBase
{
public string Url { get; set; }
}
public class RssBlog : BlogBase
{
public string Url { get; set; }
}
Uyarı
SQL Server gibi ilişkisel veritabanı sağlayıcıları, atama kullanırken paylaşılan sütunları sorgularken otomatik olarak ayrıştırıcı koşulunu kullanmaz. Sorgu Url = (blog as RssBlog).Url
eşdüzey Url
satırları için Blog
değerini de döndürür. Sorguyu RssBlog
varlıklarla kısıtlamak için ayırıcıya el ile Url = blog is RssBlog ? (blog as RssBlog).Url : null
gibi bir filtre eklemeniz gerekir.
Tür başına tablo yapılandırması
TPT eşleme deseninde, tüm türler tek tek tablolara eşlenir. Yalnızca bir temel türe veya türetilmiş türe ait özellikler, bu türle eşlenen bir tabloda depolanır. Türetilmiş türlerle eşlenen tablolar, türetilmiş tabloyu temel tabloyla birleştiren bir yabancı anahtar da depolar.
modelBuilder.Entity<Blog>().ToTable("Blogs");
modelBuilder.Entity<RssBlog>().ToTable("RssBlogs");
Tavsiye
Her varlık türünde ToTable
çağırmak yerine, her kök varlık türünde modelBuilder.Entity<Blog>().UseTptMappingStrategy()
çağırabilirsiniz ve tablo adları EF tarafından oluşturulur.
Tavsiye
Her tablodaki birincil anahtar sütunlar için farklı sütun adları yapılandırmak için bkz. Tabloya özgü özellik yapılandırması.
EF yukarıdaki model için aşağıdaki veritabanı şemasını oluşturur.
CREATE TABLE [Blogs] (
[BlogId] int NOT NULL IDENTITY,
[Url] nvarchar(max) NULL,
CONSTRAINT [PK_Blogs] PRIMARY KEY ([BlogId])
);
CREATE TABLE [RssBlogs] (
[BlogId] int NOT NULL,
[RssUrl] nvarchar(max) NULL,
CONSTRAINT [PK_RssBlogs] PRIMARY KEY ([BlogId]),
CONSTRAINT [FK_RssBlogs_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([BlogId]) ON DELETE NO ACTION
);
Uyarı
Birincil anahtar kısıtlaması yeniden adlandırılırsa, yeni ad hiyerarşiye eşlenen tüm tablolara uygulanır, gelecekteki EF sürümleri kısıtlamanın yalnızca sorun 19970 düzeltildiğinde belirli bir tablo için yeniden adlandırılmasına izin verir.
Toplu yapılandırma kullanıyorsanız, GetColumnName(IProperty, StoreObjectIdentifier)çağırarak belirli bir tablonun sütun adını alabilirsiniz.
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var tableIdentifier = StoreObjectIdentifier.Create(entityType, StoreObjectType.Table);
Console.WriteLine($"{entityType.DisplayName()}\t\t{tableIdentifier}");
Console.WriteLine(" Property\tColumn");
foreach (var property in entityType.GetProperties())
{
var columnName = property.GetColumnName(tableIdentifier.Value);
Console.WriteLine($" {property.Name,-10}\t{columnName}");
}
Console.WriteLine();
}
Uyarı
Çoğu durumda TPT, TPH ile karşılaştırıldığında daha alt performans gösterir. daha fazla bilgi için performans belgelerine bakın.
Dikkat
Türetilmiş bir türün sütunları farklı tablolara eşlenir, bu nedenle hem devralınan hem de bildirilen özellikleri kullanan bileşik FK kısıtlamaları ve dizinleri veritabanında oluşturulamaz.
Somut tip başına tablo yapılandırması
TPC eşleme düzeninde, tüm türler tek tek tablolara eşlenir. Her tablo, ilgili varlık türündeki tüm özellikler için sütunlar içerir. Bu, TPT stratejisiyle ilgili bazı yaygın performans sorunlarını giderir.
Tavsiye
EF Ekibi, .NET Veri Topluluğu Standupbölümünde TPC eşlemesi hakkında demonstrasyon yaparak kapsamlı bir şekilde tartıştı. Tüm Topluluk Standup bölümlerinde olduğu gibi artık YouTube'de TPC bölümünü izleyebilirsiniz.
modelBuilder.Entity<Blog>().UseTpcMappingStrategy()
.ToTable("Blogs");
modelBuilder.Entity<RssBlog>()
.ToTable("RssBlogs");
Tavsiye
Her varlık türünde ToTable
çağırmak yerine, yalnızca her kök varlık türünde modelBuilder.Entity<Blog>().UseTpcMappingStrategy()
çağırmak tablo adlarını kurala göre oluşturur.
Tavsiye
Her tablodaki birincil anahtar sütunlar için farklı sütun adları yapılandırmak için bkz. Tabloya özgü özellik yapılandırması.
EF yukarıdaki model için aşağıdaki veritabanı şemasını oluşturur.
CREATE TABLE [Blogs] (
[BlogId] int NOT NULL DEFAULT (NEXT VALUE FOR [BlogSequence]),
[Url] nvarchar(max) NULL,
CONSTRAINT [PK_Blogs] PRIMARY KEY ([BlogId])
);
CREATE TABLE [RssBlogs] (
[BlogId] int NOT NULL DEFAULT (NEXT VALUE FOR [BlogSequence]),
[Url] nvarchar(max) NULL,
[RssUrl] nvarchar(max) NULL,
CONSTRAINT [PK_RssBlogs] PRIMARY KEY ([BlogId])
);
TPC veritabanı şeması
TPC stratejisi TPT stratejisine benzer, ancak hiyerarşideki her somut türü için farklı bir tablo oluşturulur, ancak tablolar soyut türleri için oluşturulmaz; bu nedenle "beton başına tablo türü" adıdır. TPT'de olduğu gibi, tablonun kendisi kaydedilen nesnenin türünü gösterir. Ayrıca, TPT eşlemesinin aksine, her tablo somut türde ve onun temel türlerinde bulunan her özelliğin sütunlarını içerir. TPC veritabanı şemaları normal dışıdır.
Örneğin, şu hiyerarşiyi haritalamayı göz önünde bulundurun:
public abstract class Animal
{
protected Animal(string name)
{
Name = name;
}
public int Id { get; set; }
public string Name { get; set; }
public abstract string Species { get; }
public Food? Food { get; set; }
}
public abstract class Pet : Animal
{
protected Pet(string name)
: base(name)
{
}
public string? Vet { get; set; }
public ICollection<Human> Humans { get; } = new List<Human>();
}
public class FarmAnimal : Animal
{
public FarmAnimal(string name, string species)
: base(name)
{
Species = species;
}
public override string Species { get; }
[Precision(18, 2)]
public decimal Value { get; set; }
public override string ToString()
=> $"Farm animal '{Name}' ({Species}/{Id}) worth {Value:C} eats {Food?.ToString() ?? "<Unknown>"}";
}
public class Cat : Pet
{
public Cat(string name, string educationLevel)
: base(name)
{
EducationLevel = educationLevel;
}
public string EducationLevel { get; set; }
public override string Species => "Felis catus";
public override string ToString()
=> $"Cat '{Name}' ({Species}/{Id}) with education '{EducationLevel}' eats {Food?.ToString() ?? "<Unknown>"}";
}
public class Dog : Pet
{
public Dog(string name, string favoriteToy)
: base(name)
{
FavoriteToy = favoriteToy;
}
public string FavoriteToy { get; set; }
public override string Species => "Canis familiaris";
public override string ToString()
=> $"Dog '{Name}' ({Species}/{Id}) with favorite toy '{FavoriteToy}' eats {Food?.ToString() ?? "<Unknown>"}";
}
public class Human : Animal
{
public Human(string name)
: base(name)
{
}
public override string Species => "Homo sapiens";
public Animal? FavoriteAnimal { get; set; }
public ICollection<Pet> Pets { get; } = new List<Pet>();
public override string ToString()
=> $"Human '{Name}' ({Species}/{Id}) with favorite animal '{FavoriteAnimal?.Name ?? "<Unknown>"}'" +
$" eats {Food?.ToString() ?? "<Unknown>"}";
}
SQL Server kullanılırken, bu hiyerarşi için oluşturulan tablolar şunlardır:
CREATE TABLE [Cats] (
[Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
[Name] nvarchar(max) NOT NULL,
[FoodId] uniqueidentifier NULL,
[Vet] nvarchar(max) NULL,
[EducationLevel] nvarchar(max) NOT NULL,
CONSTRAINT [PK_Cats] PRIMARY KEY ([Id]));
CREATE TABLE [Dogs] (
[Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
[Name] nvarchar(max) NOT NULL,
[FoodId] uniqueidentifier NULL,
[Vet] nvarchar(max) NULL,
[FavoriteToy] nvarchar(max) NOT NULL,
CONSTRAINT [PK_Dogs] PRIMARY KEY ([Id]));
CREATE TABLE [FarmAnimals] (
[Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
[Name] nvarchar(max) NOT NULL,
[FoodId] uniqueidentifier NULL,
[Value] decimal(18,2) NOT NULL,
[Species] nvarchar(max) NOT NULL,
CONSTRAINT [PK_FarmAnimals] PRIMARY KEY ([Id]));
CREATE TABLE [Humans] (
[Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence]),
[Name] nvarchar(max) NOT NULL,
[FoodId] uniqueidentifier NULL,
[FavoriteAnimalId] int NULL,
CONSTRAINT [PK_Humans] PRIMARY KEY ([Id]));
Şunlara dikkat edin:
Bunlar nesne modelinde
Animal
olduğundan,Pet
veyaabstract
türleri için tablo yoktur. C# öğesinin soyut türlerin örneklerine izin vermediğini ve bu nedenle soyut tür örneğinin veritabanına kaydedileceği bir durum olmadığını unutmayın.Temel türlerdeki özelliklerin eşlemesi her beton türü için yinelenir. Örneğin, her tablonun bir
Name
sütunu vardır ve hem Kediler hem de Köpekler'in birVet
sütunu vardır.Bazı verilerin bu veritabanına kaydedilmesi aşağıdaki sonuçlara neden olur:
Cats tablosu
Kimlik | İsim | FoodId | Veteriner | Eğitim Seviyesi |
---|---|---|---|---|
1 | Alice | 99ca3e98-b26d-4a0c-d4ae-08da7aca624f | Pengelly | MBA (İşletme Yüksek Lisansı) |
2 | Mac | 99ca3e98-b26d-4a0c-d4ae-08da7aca624f | Pengelly | Okul öncesi |
8 | Baxter | 5dc5019e-6f72-454b-d4b0-08da7aca624f | Bothell Evcil Hayvan Hastanesi | Bsc |
Köpekler masası
Kimlik | İsim | FoodId | Veteriner | FavoriteToy |
---|---|---|---|---|
3 | Tost | 011aaf6f-d588-4fad-d4ac-08da7aca624f | Pengelly | Bay Sincap |
FarmAnimals tablosu
Kimlik | İsim | FoodId | Değer | Tür |
---|---|---|---|---|
4 | Clyde | 1d495075-f527-4498-d4af-08da7aca624f | 100,00 | Equus africanus asinus (Afrika yaban eşeği) |
İnsanlar tablosu
Kimlik | İsim | FoodId | FavoriHayvanId |
---|---|---|---|
5 | Wendy | 5418fd81-7660-432f-d4b1-08da7aca624f | 2 |
6 | Arthur | 59b495d4-0414-46bf-d4ad-08da7aca624f | 1 |
9 | Katie | sıfır | 8 |
TPT eşlemesinin aksine, tek bir nesnenin tüm bilgilerinin tek bir tabloda yer aldığına dikkat edin. Ayrıca, TPH eşlemesinin aksine, model tarafından hiçbir zaman kullanılmayan herhangi bir tabloda sütun ve satır birleşimi yoktur. Aşağıda bu özelliklerin sorgular ve depolama için nasıl önemli olabileceğini göreceğiz.
Anahtar oluşturma
Seçilen devralma eşleme stratejisi, birincil anahtar değerlerinin nasıl oluşturulup yönetildiğinin sonuçlarına neden olur. TPH'deki anahtarlar kolaydır çünkü her varlık örneği tek bir tabloda tek bir satırla temsil edilir. Herhangi bir tür anahtar değeri oluşturma kullanılabilir ve ek kısıtlama gerekmez.
TPT stratejisi için, tabloda her zaman hiyerarşinin temel türüne eşlenmiş bir satır vardır. Bu satırda herhangi bir tür anahtar oluşturma kullanılabilir ve diğer tabloların anahtarları yabancı anahtar kısıtlamaları kullanılarak bu tabloya bağlanır.
TPC için işler biraz daha karmaşık hale geliyor. İlk olarak, EF Core'un, varlıkların farklı türleri olsa bile bir hiyerarşideki tüm varlıkların benzersiz bir anahtar değerine sahip olmasını gerektirdiğini anlamak önemlidir. Örneğin, örnek modelimizi kullanarak bir Köpek, Kedi ile aynı kimlik anahtarı değerine sahip olamaz. İkincisi, TPT'nin aksine, anahtar değerlerin yaşadığı ve oluşturulabileceği tek bir yer olarak davranabilen ortak bir tablo yoktur. Bu, basit bir Identity
sütunu kullanılamayacağı anlamına gelir.
Sıraları destekleyen veritabanları için anahtar değerleri, her tablo için varsayılan kısıtlamada başvurulan tek bir sıra kullanılarak oluşturulabilir. Bu, yukarıda gösterilen TPC tablolarında kullanılan ve her tablonun aşağıdakilere sahip olduğu stratejidir:
[Id] int NOT NULL DEFAULT (NEXT VALUE FOR [AnimalSequence])
AnimalSequence
, EF Core tarafından oluşturulan bir veritabanı dizisidir. Bu strateji, SQL Server için EF Core veritabanı sağlayıcısı kullanılırken varsayılan olarak TPC hiyerarşileri için kullanılır. Sıraları destekleyen diğer veritabanları için veritabanı sağlayıcıları da benzer bir varsayılan değere sahip olmalıdır. Hi-Lo desenleri gibi dizileri kullanan diğer anahtar oluşturma stratejileri de TPC ile kullanılabilir.
Standart Kimlik sütunları TPC ile çalışmasa da, her tablo uygun bir tohumla yapılandırılırsa ve her tablo için oluşturulan değerler hiçbir zaman çakışmayacak şekilde artırılırsa Kimlik sütunlarını kullanmak mümkündür. Örneğin:
modelBuilder.Entity<Cat>().ToTable("Cats", tb => tb.Property(e => e.Id).UseIdentityColumn(1, 4));
modelBuilder.Entity<Dog>().ToTable("Dogs", tb => tb.Property(e => e.Id).UseIdentityColumn(2, 4));
modelBuilder.Entity<FarmAnimal>().ToTable("FarmAnimals", tb => tb.Property(e => e.Id).UseIdentityColumn(3, 4));
modelBuilder.Entity<Human>().ToTable("Humans", tb => tb.Property(e => e.Id).UseIdentityColumn(4, 4));
Önemli
Bu stratejinin kullanılması, hiyerarşideki toplam tür sayısının önceden bilinmesini gerektirdiğinden türetilmiş türleri daha sonra eklemeyi zorlaştırır.
SQLite dizileri veya Kimlik çekirdek/artışını desteklemez ve bu nedenle TPC stratejisiyle SQLite kullanılırken tamsayı anahtar değeri oluşturma desteklenmez. Ancak, istemci tarafı oluşturma veya genel olarak benzersiz anahtarlar (GUID'ler gibi) SQLite dahil olmak üzere tüm veritabanlarında desteklenir.
Yabancı anahtar kısıtlamaları
TPC eşleme stratejisi, normal olmayan bir SQL şeması oluşturur. Bu, bazı veritabanı temizleyicilerinin buna karşı olmasının bir nedenidir. Örneğin, FavoriteAnimalId
yabancı anahtar sütununu göz önünde bulundurun. Bu sütundaki değer, bir hayvanın birincil anahtar değeriyle eşleşmelidir. Bu, TPH veya TPT kullanılırken veritabanında basit bir FK kısıtlaması ile zorunlu kılınabilir. Örneğin:
CONSTRAINT [FK_Animals_Animals_FavoriteAnimalId] FOREIGN KEY ([FavoriteAnimalId]) REFERENCES [Animals] ([Id])
TPC kullanırken, belirli bir hayvanın birincil anahtarı, o hayvanın somut türüne karşılık gelen tabloda depolanır. Örneğin, bir kedinin birincil anahtarı Cats.Id
sütununda depolanırken, bir köpeğin birincil anahtarı Dogs.Id
sütununda depolanır vb. Bu, bu ilişki için bir FK kısıtlaması oluşturulamayacağı anlamına gelir.
Uygulamada, uygulama geçersiz veri eklemeye çalışmadığı sürece bu bir sorun değildir. Örneğin, tüm veriler EF Core tarafından eklenirse ve varlıkları ilişkilendirmek için gezintileri kullanırsa, FK sütununun her zaman geçerli PK değerleri içermesi garanti edilir.
Özet ve rehberlik
Özetle, TPH genellikle çoğu uygulama için uygundur ve çok çeşitli senaryolar için iyi bir varsayılandır, bu nedenle ihtiyacınız yoksa TPC'nin karmaşıklığını eklemeyin. Özel olarak, kodunuz çoğunlukla temel türe göre sorgu yazma gibi birçok türdeki varlıkları sorgulayacaksa, TPC üzerinden TPH'ye doğru eğilin.
Bununla birlikte, TPC aynı zamanda kodunuzun çoğunlukla tek bir yaprak türündeki varlıkları sorguladığında ve karşılaştırmalarınız TPH ile karşılaştırıldığında bir iyileştirme gösterdiğinde kullanmak için iyi bir eşleme stratejisidir.
Yalnızca dış etmenlerle kısıtlandığınızda TPT'yi kullanın.