You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Support for temporal tables for TPT (Table per Type) and other entity-splitting scenarios #26457
We are developing an application that currently use the concept of Table-Per-Type to minimize the duplication of fields instead of using a discriminator that is required with Table-Per-Hierarchy. More tables are planned in the future that will depend on the same parent table.
With the latest version of EF Core, we saw an opportunity to use the new temporal table support to easily keep track of operations in our tables. After adding t => t.IsTemporal() build action to our tables configuration and try to add a new migration, we received the following error:
Temporal tables are only supported for entities using Table-Per-Hierarchy inheritance mapping.
This message is returned through an InvalidOperationException in SqlServerModelValidator.ValidateTemporalTables when EF detect a derived table mapping in one of the tables for which we're trying to configure the temporal build action.
We were not able to find much information related to this restriction of temporal for TPT as per those are quite new features supported in EF Core 5 for TPT and EF Core 6 for temporal tables.
Is support planned in the future for both mechanisms to work on the same set of tables?
Thanks!
Version information
EF Core version: (6.0.0-rc.2.21480.5)
Database provider: (Microsoft.EntityFrameworkCore.SqlServer)
Target framework: (.NET 6.0.0-rc.2.21480.5)
IDE: (e.g. Visual Studio 2022 17.0.0 Preview 6)
@pboucher1960 We discussed this, and we're going to consider it for EF Core 7.0. For it to work correctly, all the tables must be updated inside the same transaction. We do this by default when using SaveChanges in EF Core, but it's possible that a set of TPT tables are updated in a way where the time periods would be inconsistent. We suspect (hope) that will only be a corner case and therefore should not block supporting temporal tables.
@ajcvickers Thank you for your answer. Do we have any time frame for EF Core 7.0 to be usable in test cases? In the meantime, we will evaluate a more conventional table structure to meet our requirements.
I have the same problem as @pboucher1960 and would like to add an additional suggestion. When activating a temporal table for an entity, it makes sense to make all tables in the hiërarchie temporal. However, personally I'd prefer an approach where only the base entity has a temporal table whereas the derived entities don't have one. Therefore, the temporal information is only available when querying the base entity.
For it to work correctly, all the tables must be updated inside the same transaction. We do this by default when using SaveChanges in EF Core, but it's possible that a set of TPT tables are updated in a way where the time periods would be inconsistent. We suspect (hope) that will only be a corner case and therefore should not block supporting temporal tables.
I don't think that's a real corner case actually. What happens if you only update data in a derived entity? That means the table containing the base entity's data isn't touched at all. Not even mentioning that there are cases (as in our case) where an external process is responsible for filling the actual tables.
changed the title [-]Is support for temporal tables for TPT (Table per Types) planned in the future and if so, when?[/-][+]Support for temporal tables for TPT (Table per Type)[/+]on Jan 11, 2022
changed the title [-]Support for temporal tables for TPT (Table per Type)[/-][+]Support for temporal tables for TPT (Table per Type) and other table-splitting scenarios[/+]on Jun 15, 2022
changed the title [-]Support for temporal tables for TPT (Table per Type) and other table-splitting scenarios[/-][+]Support for temporal tables for TPT (Table per Type) and other entity-splitting scenarios[/+]on Jun 15, 2022
It's a pretty major drawback to be unable to use system versioning with TPT hierarchies. Table per hierarchy is not appropriate for complex inheritance that is typical in real world apps, and it isn't good to be forced to use TPH just to enable the use of system versioning (which has been around for some time and is useful). This sort of thing I would hope is not based on votes that may or may not accurately reflect the end user collective desire for a feature. This is fairly fundamental functionality and should work for completeness, it sounds rather like it is possibly being avoided due to complexity given that it was due in 7?
Just asking: Can we see this working on TPC/TPT in the next time (EF Core 9 f.e.)?
I'm pretty sure I already know the answer, but i dont want to hear read it.
@skoehlerrto It's not going to happen for EF9. This issue has 29 votes (👍) at the time of writing. That means that there are 108 issues on the backlog with more votes. The best thing you can do is vote for this issue, but implementation is unlikely to happen anytime soon.
Just ran into this today, does anyone have a good workaround for TPC?
It feels incorrect to change my model back to TPH since I only want one of the types at a time
what the hell haha. this is kind of weird. it seems quite fundamental to have this. more 👍 please haha :) Anyone have a nice alternative?
Just ran into this today, does anyone have a good workaround for TPC? It feels incorrect to change my model back to TPH since I only want one of the types at a time
I've had success not referencing the abstract type at all when configuring EF Core. The downside to this is you have to copy/paste identical configuration code into every type. My caveat is that my abstract classes are pretty simple, but I do have derived types that EF Core sets up with temporal tables in the DB.
what the hell haha. this is kind of weird. it seems quite fundamental to have this. more 👍 please haha :) Anyone have a nice alternative?
Just ran into this today, does anyone have a good workaround for TPC? It feels incorrect to change my model back to TPH since I only want one of the types at a time
I've had success not referencing the abstract type at all when configuring EF Core. The downside to this is you have to copy/paste identical configuration code into every type. My caveat is that my abstract classes are pretty simple, but I do have derived types that EF Core sets up with temporal tables in the DB.
Yeah, I had to do this as well. Luckily, there wasn't a lot of copy-pasting required in my case.
Can't we just activate temporal tables manually without EF knowing about it? EF doesn't need to know about the temporal tables since SQL-server handles all the heavy lifting anyway. That would make EF happy when using TPC and TPT?
Yeah, I had to do this as well. Luckily, there wasn't a lot of copy-pasting required in my case.
You should never copy/paste.....
/// <summary>/// Provides a base configuration for entities inheriting from <see cref="EntityRowBase"/>./// </summary>publicstaticclassEntityConfigurationExtensions{/// <summary>/// Configures the base properties for the entity type./// </summary>/// <param name="builder">The entity type builder.</param>/// <param name="isTemporal">Flag to indicate if table should be temporal or not.</param>/// <param name="excludeFromMigrations">Flag to include or exclude from migrations.</param>/// <returns>An <see cref="EntityTypeBuilder{T}" /> for further configuration.</returns>publicstaticEntityTypeBuilder<T>ConfigureBase<T>(thisEntityTypeBuilder<T>builder,boolisTemporal=true,boolexcludeFromMigrations=false)whereT:EntityRowBase{// First map to temporal table and configure the primary keybuilder.ToTable(typeof(T).Name, e =>{e.IsTemporal(isTemporal);e.ExcludeFromMigrations(excludeFromMigrations);}).HasKey(e =>e.Id);// Make sure the id is not generated by the databasebuilder.Property(e =>e.Id).ValueGeneratedNever();// Configure the row version for concurrencybuilder.Property(e =>e.RowVersion).IsRowVersion();// Return the builder for further configurationreturnbuilder;}/// <summary>/// Configures the base properties for the bridge entity./// </summary>/// <param name="builder">The entity type builder.</param>/// <param name="key">The expression to define the primary key</param>/// <param name="isTemporal">Flag to indicate if table should be temporal or not.</param>/// <param name="excludeFromMigrations">Flag to include or exclude from migrations.</param>/// <returns>An <see cref="EntityTypeBuilder{T}" /> for further configuration.</returns>publicstaticvoidConfigureBridge<T>(thisEntityTypeBuilder<T>builder,Expression<Func<T,object?>>key,boolisTemporal=true,boolexcludeFromMigrations=false)whereT:class=>builder.ToTable(typeof(T).Name, e =>{e.IsTemporal(isTemporal);e.ExcludeFromMigrations(excludeFromMigrations);}).HasKey(key);}
The issue was opened 4 years ago. Why do I have to be affected again by issues nobody cares to fix?
4 long years to get this solved, and we got nothing. Well, almost nothing. We got copilot everywhere...
Coming back to this one with a use case the workaround I posted above cannot solve, and which I don't think this issue's framing currently contemplates: non-temporal fragments of a temporal entity — asymmetric temporality via entity splitting.
The scenario. We're making the ASP.NET Core Identity users table temporal. The driver is erasure/audit compliance built on crypto-shredding (NIST SP 800-88 cryptographic erase applied at the field level): PII columns are ciphertext under per-subject keys, temporal history is the audit record, and destroying a subject's key renders every copy — current row, history rows, backups — unreadable in one operation. Temporal history isn't incidental here; it is the compliance artifact.
The problem. Identity keeps AccessFailedCount and LockoutEnd on the user entity. With the table temporal, every wrong-password attempt mints a history row — the audit log drowns in rate-limiter churn. The natural EF answer is entity splitting:
…with the explicit intent that users is temporal and UserLockout is not. The asymmetry is the entire point of the split.
What happens today. Temporality is an entity-type annotation, so it chains onto every fragment — #30366 (closed as a duplicate of this issue) shows all fragments stamped SqlServer:IsTemporal sharing a single TemporalHistoryTableName, then an NRE in migration generation. Checking current main, SqlServerModelValidator.ValidateTemporalTable still has no entity-splitting guard, and no SplitTableBuilder.IsTemporal(...) surface exists — so this is neither supported nor cleanly blocked. It's the same per-store-object metadata gap I ran into from the other direction in efcore/EFCore.NamingConventions#396 (key/FK constraint names have no per-store-object override either).
The ask. When this gets picked up, please make per-fragment temporal control part of the design — specifically the ability for a fragment table of a temporal entity to be non-temporal. Supporting splitting by making all fragments temporal would leave this use case exactly where it is today.
Happy to put together a minimal repro against the .NET 11 previews if that helps.
cc @roji — this is the SQL Server half of a story we're building cross-provider (trigger-based history on the PG side); would value your read on whether per-store-object temporal facets are feasible as part of this issue's scope.
Activity
ajcvickers commentedon Oct 27, 2021
@pboucher1960 We discussed this, and we're going to consider it for EF Core 7.0. For it to work correctly, all the tables must be updated inside the same transaction. We do this by default when using SaveChanges in EF Core, but it's possible that a set of TPT tables are updated in a way where the time periods would be inconsistent. We suspect (hope) that will only be a corner case and therefore should not block supporting temporal tables.
pboucher1960 commentedon Oct 27, 2021
@ajcvickers Thank you for your answer. Do we have any time frame for EF Core 7.0 to be usable in test cases? In the meantime, we will evaluate a more conventional table structure to meet our requirements.
roji commentedon Oct 27, 2021
@pboucher1960 EF Core 7.0 is planned to release in November of 2022 (a year after 6.0).
LeonarddeR commentedon Dec 2, 2021
I have the same problem as @pboucher1960 and would like to add an additional suggestion. When activating a temporal table for an entity, it makes sense to make all tables in the hiërarchie temporal. However, personally I'd prefer an approach where only the base entity has a temporal table whereas the derived entities don't have one. Therefore, the temporal information is only available when querying the base entity.
I don't think that's a real corner case actually. What happens if you only update data in a derived entity? That means the table containing the base entity's data isn't touched at all. Not even mentioning that there are cases (as in our case) where an external process is responsible for filling the actual tables.
[-]Is support for temporal tables for TPT (Table per Types) planned in the future and if so, when?[/-][+]Support for temporal tables for TPT (Table per Type)[/+][-]Support for temporal tables for TPT (Table per Type)[/-][+]Support for temporal tables for TPT (Table per Type) and other table-splitting scenarios[/+][-]Support for temporal tables for TPT (Table per Type) and other table-splitting scenarios[/-][+]Support for temporal tables for TPT (Table per Type) and other entity-splitting scenarios[/+]4 remaining items
KillianDav commentedon Jun 22, 2023
It's a pretty major drawback to be unable to use system versioning with TPT hierarchies. Table per hierarchy is not appropriate for complex inheritance that is typical in real world apps, and it isn't good to be forced to use TPH just to enable the use of system versioning (which has been around for some time and is useful). This sort of thing I would hope is not based on votes that may or may not accurately reflect the end user collective desire for a feature. This is fairly fundamental functionality and should work for completeness, it sounds rather like it is possibly being avoided due to complexity given that it was due in 7?
jarednapier commentedon Dec 14, 2023
Is there going to be a bug fix with TPC mapping and temporal tables?
skoehlerrto commentedon May 15, 2024
Just asking: Can we see this working on TPC/TPT in the next time (EF Core 9 f.e.)?
I'm pretty sure I already know the answer, but i dont want to
hearread it.ajcvickers commentedon May 15, 2024
@skoehlerrto It's not going to happen for EF9. This issue has 29 votes (👍) at the time of writing. That means that there are 108 issues on the backlog with more votes. The best thing you can do is vote for this issue, but implementation is unlikely to happen anytime soon.
salgaard commentedon Sep 11, 2024
what the hell haha. this is kind of weird. it seems quite fundamental to have this. more 👍 please haha :)
Anyone have a nice alternative?
SpaceOgre commentedon Sep 26, 2024
Just ran into this today, does anyone have a good workaround for TPC?
It feels incorrect to change my model back to TPH since I only want one of the types at a time
jarednapier commentedon Sep 26, 2024
I've had success not referencing the abstract type at all when configuring EF Core. The downside to this is you have to copy/paste identical configuration code into every type. My caveat is that my abstract classes are pretty simple, but I do have derived types that EF Core sets up with temporal tables in the DB.
Venom1991 commentedon Dec 28, 2024
Yeah, I had to do this as well. Luckily, there wasn't a lot of copy-pasting required in my case.
Fronix commentedon Oct 1, 2025
Can't we just activate temporal tables manually without EF knowing about it? EF doesn't need to know about the temporal tables since SQL-server handles all the heavy lifting anyway. That would make EF happy when using TPC and TPT?
buvinghausen commentedon Oct 10, 2025
You should never copy/paste.....
mdzieg commentedon Nov 18, 2025
The issue was opened 4 years ago. Why do I have to be affected again by issues nobody cares to fix?
4 long years to get this solved, and we got nothing. Well, almost nothing. We got copilot everywhere...
buvinghausen commentedon Aug 5, 2026
Coming back to this one with a use case the workaround I posted above cannot solve, and which I don't think this issue's framing currently contemplates: non-temporal fragments of a temporal entity — asymmetric temporality via entity splitting.
The scenario. We're making the ASP.NET Core Identity
userstable temporal. The driver is erasure/audit compliance built on crypto-shredding (NIST SP 800-88 cryptographic erase applied at the field level): PII columns are ciphertext under per-subject keys, temporal history is the audit record, and destroying a subject's key renders every copy — current row, history rows, backups — unreadable in one operation. Temporal history isn't incidental here; it is the compliance artifact.The problem. Identity keeps
AccessFailedCountandLockoutEndon the user entity. With the table temporal, every wrong-password attempt mints a history row — the audit log drowns in rate-limiter churn. The natural EF answer is entity splitting:…with the explicit intent that
usersis temporal andUserLockoutis not. The asymmetry is the entire point of the split.What happens today. Temporality is an entity-type annotation, so it chains onto every fragment — #30366 (closed as a duplicate of this issue) shows all fragments stamped
SqlServer:IsTemporalsharing a singleTemporalHistoryTableName, then an NRE in migration generation. Checking currentmain,SqlServerModelValidator.ValidateTemporalTablestill has no entity-splitting guard, and noSplitTableBuilder.IsTemporal(...)surface exists — so this is neither supported nor cleanly blocked. It's the same per-store-object metadata gap I ran into from the other direction in efcore/EFCore.NamingConventions#396 (key/FK constraint names have no per-store-object override either).The ask. When this gets picked up, please make per-fragment temporal control part of the design — specifically the ability for a fragment table of a temporal entity to be non-temporal. Supporting splitting by making all fragments temporal would leave this use case exactly where it is today.
Happy to put together a minimal repro against the .NET 11 previews if that helps.
cc @roji — this is the SQL Server half of a story we're building cross-provider (trigger-based history on the PG side); would value your read on whether per-store-object temporal facets are feasible as part of this issue's scope.