Jegyzet
Az oldalhoz való hozzáférés engedélyezést igényel. Próbálhatod be jelentkezni vagy könyvtárat váltani.
Az oldalhoz való hozzáférés engedélyezést igényel. Megpróbálhatod a könyvtár váltását.
Az EF Core nagy rugalmasságot nyújt az entitástípusok adatbázistáblákhoz való leképezése során. Ez még hasznosabbá válik, ha olyan adatbázist kell használnia, amelyet nem az EF hozott létre.
Az alábbi technikákat táblázatokban ismertetjük, de ugyanez az eredmény érhető el a nézetekre való kivetítéskor is.
Táblázat felosztása
Az EF Core lehetővé teszi két vagy több entitás egyetlen sorba való leképezését. Ezt táblamegosztásnak vagy táblamegosztásnak nevezzük.
Konfiguráció
A tábla felosztásához az entitástípusokat ugyanarra a táblára kell leképezni, az elsődleges kulcsokat ugyanahhoz az oszlophoz kell hozzárendelni, és legalább egy kapcsolatot kell konfigurálni az egyik entitástípus elsődleges kulcsa és egy másik, ugyanabban a táblában lévő másik között.
A táblák felosztásának gyakori forgatókönyve, hogy a nagyobb teljesítmény vagy beágyazás érdekében a táblázat oszlopainak csak egy részhalmazát használja.
Ebben a példában a Order egy részhalmaza a DetailedOrder-nek.
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; }
}
A szükséges konfiguráción kívül meghívjuk Property(o => o.Status).HasColumnName("Status")-t, hogy leképezze DetailedOrder.Status-et ugyanarra az oszlopra, mint Order.Status.
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();
});
Jótanács
További információért tekintse meg a teljes mintaprojekt.
Használat
Az entitások táblafelosztással történő mentése és lekérdezése ugyanúgy történik, mint a többi entitás:
using (var context = new TableSplittingContext())
{
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();
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"
}
});
await context.SaveChangesAsync();
}
using (var context = new TableSplittingContext())
{
var pendingCount = await context.Orders.CountAsync(o => o.Status == OrderStatus.Pending);
Console.WriteLine($"Current number of pending orders: {pendingCount}");
}
using (var context = new TableSplittingContext())
{
var order = await context.DetailedOrders.FirstAsync(o => o.Status == OrderStatus.Pending);
Console.WriteLine($"First pending order will ship to: {order.ShippingAddress}");
}
Választható függő entitás
Ha az összes oszlop, amelyet egy függő entitás használ, NULL-ot tartalmaz az adatbázisban, akkor nem jön létre példány a lekérdezés során. Ez lehetővé teszi egy olyan választható függő entitás modellezését, ahol a fő entitáson lévő kapcsolati tulajdonság null értékű lenne. Vegye figyelembe, hogy ez akkor is előfordul, ha a függő összes tulajdonsága nem kötelező, és mind null értékre van állítva, ami talán nem várható.
A további ellenőrzés azonban befolyásolhatja a lekérdezés teljesítményét. Ezenkívül ha a függő entitástípus saját függőkkel rendelkezik, akkor annak meghatározása, hogy létre kell-e hozni egy példányt, nem triviális lesz. A problémák elkerülése érdekében a függő entitástípus kötelezőként megjelölhető, további információt a Kötelező egy-az-egyhez függők című témakörben talál.
Egyidejűségi tokenek
Ha a táblát megosztó entitástípusok bármelyikének van konkurenciaérvényesítő azonosítója, akkor az minden más entitástípusban is szerepelnie kell. Erre azért van szükség, hogy elkerülhető legyen az elavult egyidejűségi tokenérték, ha csak az ugyanazon táblához hozzárendelt entitások egyike frissül.
Annak érdekében, hogy az egyidejűséghez tartozó jogkivonat ne kerüljön bele a használó kódjába, lehetséges, hogy árnyéktulajdonságként létrehozhatunk egyet.
modelBuilder.Entity<Order>()
.Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
modelBuilder.Entity<DetailedOrder>()
.Property(o => o.Version).IsRowVersion().HasColumnName("Version");
Örökség
Javasoljuk, hogy a szakasz folytatása előtt olvassa el a dedikált oldalt az öröklésről .
A táblafelosztást használó függő típusok öröklési hierarchiával rendelkezhetnek, de vannak korlátozások:
- A függő entitástípus nem használhat TPC-megfeleltetést, mivel a származtatott típusok nem lennének képesek ugyanahhoz a táblához leképezni.
- A függő entitástípus használhat TPT-leképezést, de csak a legfelső szintű entitástípus használhat táblafelosztást.
- Ha az egyszerű entitástípus TPC-t használ, akkor csak azok az entitástípusok használhatják a táblák felosztását, amelyek nem rendelkeznek leszármazottaikkal. Ellenkező esetben a függő oszlopokat duplikálni kell a származtatott típusoknak megfelelő táblákon, ami bonyolítja az összes interakciót.
Entitás felosztása
Az EF Core lehetővé teszi, hogy egy entitást két vagy több tábla soraihoz rendeljen. Ezt entitásfelosztásnak nevezzük.
Konfiguráció
Vegyük például egy olyan adatbázist, amely három táblával rendelkezik, amelyek ügyféladatokat tárolnak:
- Egy
Customerstábla az ügyféladatokhoz - A
PhoneNumberstáblázat, amely az ügyfél telefonszámát tartalmazza. - Egy
Addressestáblázat az ügyfél címével kapcsolatban
Az alábbi definíciók az SQL Server alábbi tábláihoz tartoznak:
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
);
Ezek a táblák általában a saját entitástípusukhoz vannak leképezve, a típusok közötti kapcsolatokkal. Ha azonban mindhárom táblát mindig együtt használják, akkor kényelmesebb lehet őket egyetlen entitástípusra leképezni. Például:
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; }
}
Az EF7-ben ez úgy érhető el, hogy a SplitToTable függvényt meghívják az entitástípus minden felosztásához. Az alábbi kód például felosztja az Customer entitástípust a Customersfent látható , PhoneNumbersés Addresses táblákra:
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);
});
});
Figyelje meg azt is, hogy szükség esetén az egyes táblákhoz különböző oszlopnevek is megadhatóak. A főtábla oszlopnevének konfigurálásához lásd a táblázatspecifikus aspektuskonfigurációt.
A csatolási idegen kulcs konfigurálása
A megfeleltetett táblákat összekapcsoló FK ugyanazokat a tulajdonságokat célozza meg, amelyeken deklarálva van. Általában nem jön létre az adatbázisban, mivel redundáns lenne. Kivételt jelent azonban, ha az entitástípus egynél több táblára van leképezve. Az aspektusok módosításához használhatja a Fluent API kapcsolatkonfigurációját:
modelBuilder.Entity<Customer>()
.HasOne<Customer>()
.WithOne()
.HasForeignKey<Customer>(a => a.Id)
.OnDelete(DeleteBehavior.Restrict);
Korlátozások
- Az entitásfelosztás nem használható a hierarchiák entitástípusaihoz.
- A főtábla bármely sorához sornak kell lennie az egyes felosztott táblákban (a töredékek nem kötelezőek).
Táblázatspecifikus aspektuskonfiguráció
Egyes leképezési minták azt eredményezik, hogy a rendszer ugyanazt a CLR-tulajdonságot rendeli hozzá egy oszlophoz több különböző tábla mindegyikében. Az EF7 lehetővé teszi, hogy ezek az oszlopok különböző névvel rendelkezzenek. Vegyük például egy egyszerű öröklési hierarchiát:
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; }
}
A TPT öröklés-leképezési stratégiával ezek a típusok három táblára lesznek leképezve. Előfordulhat azonban, hogy az egyes táblák elsődleges kulcsoszlopának más a neve. Például:
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
);
Az EF7 lehetővé teszi a megfeleltetés konfigurálását beágyazott táblaszerkesztő használatával:
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"));
A TPC-öröklés megfeleltetésével a Breed tulajdonság különböző táblák különböző oszlopneveihez is megfeleltethető. Vegyük például a következő TPC-táblákat:
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])
);
Az EF7 támogatja ezt a táblázatleképezést:
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");
});