I would suggest that database inheritance/TPT/TPH is not necessary for this situation. What you need is just an IAuditable interface:
public interface IAuditable
{
string CreatedBy { get; set; }
DateTimeOffset CreatedTime { get; set; }
string ModifiedBy { get; set; }
DateTimeOffset? ModifiedTime { get; set; }
}
Then you have two options:
If you are sure all of your entities will be IAuditable, you can simply change the T4 template so that all auto-generated entities will implement IAuditable.
If only some of your entities will be IAuditable, you can add partial classes to those auditable entities like:
public partial class Foo
{
// Auto-generated properties by EF
public int Id { get; set; }
...
}
Then in another file:
public partial class Foo : IAuditable
{
// No implementation because auditable fields already exists
}
You can then derive an abstract class from DbContext to automatically update these IAuditable properties:
public abstract class AuditableDbContext : DbContext
{
public override int SaveChanges()
{
UpdateAuditableProperties();
return base.SaveChanges();
}
public override async Task<int> SaveChangesAsync()
{
UpdateAuditableProperties();
return await base.SaveChangesAsync();
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
{
UpdateAuditableProperties();
return await base.SaveChangesAsync(cancellationToken);
}
protected virtual void UpdateAuditableProperties()
{
var now = DateTimeOffset.Now;
var userName = GetUserName();
var changedAuditableEntities = from entry in ChangeTracker.Entries<IAuditable>()
let state = entry.State
where
state.HasFlag(EntityState.Added) ||
state.HasFlag(EntityState.Modified)
select entry;
foreach (var auditable in changedAuditableEntities)
{
var entity = auditable.Entry;
switch (auditable.State)
{
case EntityState.Added:
entity.CreatedDate = now;
entity.CreatedBy = userName;
break;
case EntityState.Modified:
entity.ModifiedDate = now;
entity.ModifiedBy = userName;
break;
}
}
}
protected abstract string GetUserName();
}
Your DbContext can derive from AuditableDbContext and then implement GetUserName() to supply the username who creates/modifies the entities.