Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
EF Core ger stor flexibilitet när det gäller att mappa entitetstyper till tabeller i en databas. Detta blir ännu mer användbart när du behöver använda en databas som inte har skapats av EF.
Nedanstående tekniker beskrivs i termer av tabeller, men samma resultat kan uppnås när du mappar till vyer också.
Tabelluppdelning
MED EF Core kan du mappa två eller flera entiteter till en enda rad. Detta kallas tabelldelning eller tabelldelning.
Konfiguration
Om du vill använda en tabell som delar entitetstyperna måste mappas till samma tabell måste de primära nycklarna mappas till samma kolumner och minst en relation konfigureras mellan den primära nyckeln av en entitetstyp och en annan i samma tabell.
Ett vanligt scenario för tabelldelning är att bara använda en delmängd av kolumnerna i tabellen för bättre prestanda eller inkapsling.
I det här exemplet Order
representerar en delmängd av DetailedOrder
.
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; }
}
Förutom den konfiguration som krävs anropar Property(o => o.Status).HasColumnName("Status")
vi för att mappa DetailedOrder.Status
till samma kolumn som 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();
});
Tips/Råd
Mer kontext finns i fullständiga exempelprojektet.
Användning
Du sparar och frågar entiteter med tabelldelning på samma sätt som andra entiteter:
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}");
}
Valfri beroende entitet
Om alla kolumner som används av en beroende entitet finns NULL
i databasen skapas ingen instans för den när du frågar. Detta gör det möjligt att modellera en valfri beroende entitet, där relationsegenskapen på huvudentiteten skulle ha ett null-värde. Observera att detta också skulle inträffa om alla beroende egenskaper är valfria och inställda på null
, vilket kanske inte är förväntat.
Den ytterligare kontrollen kan dock påverka frågeprestanda. Om den beroende entitetstypen dessutom har egna beroenden blir det dessutom icke-trivialt att avgöra om en instans ska skapas. Om du vill undvika dessa problem kan den beroende entitetstypen markeras efter behov. Mer information finns i Obligatoriska en-till-en-beroenden .
Samtidighetstoken
Om någon av de entitetstyper som delar en tabell har en samtidighetstoken måste den också ingå i alla andra entitetstyper. Detta är nödvändigt för att undvika ett inaktuellt samtidighetstokenvärde när endast en av entiteterna som mappas till samma tabell uppdateras.
För att undvika att exponera samtidighetstoken för den förbrukande koden är det möjligt att skapa en som en skuggegenskap:
modelBuilder.Entity<Order>()
.Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
modelBuilder.Entity<DetailedOrder>()
.Property(o => o.Version).IsRowVersion().HasColumnName("Version");
Arv
Vi rekommenderar att du läser den dedikerade sidan om arv innan du fortsätter med det här avsnittet.
Beroende typer som använder tabelldelning kan ha en arvshierarki, men det finns vissa begränsningar:
- Den beroende entitetstypen kan inte använda TPC-mappning eftersom de härledda typerna inte skulle kunna mappas till samma tabell.
- Den beroende entitetstypen kan använda TPT-mappning, men endast rotentitetstypen kan använda tabelldelning.
- Om huvudentitetstypen använder TPC kan endast de entitetstyper som inte har några underordnade använda tabelldelning. Annars skulle de beroende kolumnerna behöva dupliceras på de tabeller som motsvarar de härledda typerna, vilket komplicerar alla interaktioner.
Entitetsdelning
MED EF Core kan du mappa en entitet till rader i två eller flera tabeller. Detta kallas entitetsdelning.
Konfiguration
Tänk dig till exempel en databas med tre tabeller som innehåller kunddata:
- En
Customers
tabell för kundinformation - En
PhoneNumbers
tabell för kundens telefonnummer - En
Addresses
tabell för kundens adress
Här är definitioner för dessa tabeller i SQL Server:
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
);
Var och en av dessa tabeller mappas vanligtvis till sin egen entitetstyp, med relationer mellan typerna. Men om alla tre tabellerna alltid används tillsammans kan det vara enklare att mappa dem alla till en enda entitetstyp. Till exempel:
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; }
}
Detta uppnås i EF7 genom att anropa SplitToTable
för varje delning i entitetstypen. Följande kod uppdelar till exempel entitetstypen Customer
i tabellerna Customers
, PhoneNumbers
, och Addresses
som visas ovan.
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);
});
});
Observera också att vid behov kan olika kolumnnamn anges för var och en av tabellerna. Information om hur du konfigurerar kolumnnamnet för huvudtabellen finns i Tabellspecifik fasetteringskonfiguration.
Konfigurera den länkande sekundärnyckeln
FK:n som länkar de mappade tabellerna riktar sig till samma egenskaper som den deklareras för. Normalt skulle den inte skapas i databasen, eftersom den skulle vara redundant. Men det finns ett undantag för när entitetstypen mappas till mer än en tabell. Om du vill ändra dess fasetter kan du använda relationskonfigurationen Fluent API:
modelBuilder.Entity<Customer>()
.HasOne<Customer>()
.WithOne()
.HasForeignKey<Customer>(a => a.Id)
.OnDelete(DeleteBehavior.Restrict);
Begränsningar
- Entitetsdelning kan inte användas för entitetstyper i hierarkier.
- För alla rader i huvudtabellen måste det finnas en rad i var och en av de delade tabellerna (fragmenten är inte valfria).
Tabellspecifik facetkonfiguration
Vissa mappningsmönster resulterar i att samma CLR-egenskap mappas till en kolumn i var och en av flera olika tabeller. MED EF7 kan dessa kolumner ha olika namn. Tänk dig till exempel en enkel arvshierarki:
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; }
}
Med strategin för TPT-arvsmappning mappas dessa typer till tre tabeller. Den primära nyckelkolumnen i varje tabell kan dock ha ett annat namn. Till exempel:
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
);
MED EF7 kan den här mappningen konfigureras med hjälp av en kapslad tabellbyggare:
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"));
Med TPC-arvsmappningen kan egenskapen Breed
också mappas till olika kolumnnamn i olika tabeller. Tänk till exempel på följande TPC-tabeller:
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 stöder den här tabellmappningen:
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");
});