Появилась задача реализовать в базе данных версионность данных. Была выбрана парадигма создания таблиц версий, то есть на каждую таблицу Entity создается таблица EntityHistory, в которой есть все поля из Entity + DateModification (дата изменения записи), EntityId (ссылка на запись сущности в таблице Entity), UserId (ссылка на пользователя, внесшего изменение), Operation (тип изменения записи - create, update, delete, etc.). Из такой схемы напрашивается применение TPC-подхода: создаю модель Entity, наследую от нее EntityHistory, в DataContext.OnModelCreating() указываю соответствующий маппинг. Но при этом в генерируемой миграции создаются команды на удаление ForeignKey-ев на таблицу Entity из других таблиц, также не создается ForeignKey в таблице EntityHistory на Entity. Что я делаю не так и как это все победить?
Примеры:
EntityHistory.cs:
public class EntityHistory : Entity, IHistory
{
public DateTimeOffset DateModification { get; set; }
[Required]
public int IdEntity { get; set; }
[ForeignKey("IdEntity")]
public virtual Entity Entity { get; set; }
public int? IdOperation { get; set; }
[Required]
public string IdUser { get; set; }
[ForeignKey("IdUser")]
public AspNetUser User { get; set; }
public EntityHistory(string idUser, int idEntity, int idOperation)
{
this.IdUser = idUser;
this.IdEntity = idEntity;
this.IdOperation = idOperation;
this.DateModification = DateTimeOffset.Now;
}
private EntityHistory()
{
}
}
Entity.cs:
public class Entity : AttributeAnnotatedValidator, Identified<int>
{
[Key]
public int Id { get; set; }
[Required(ErrorMessage = "Необходимо указать номер договора!")]
public string Number { get; set; }
...
[ForeignKey("Contractor")]
[Required]
public int ContractorId { get; set; }
public Organization Contractor { get; set; }
[ForeignKey("Customer")]
public int? CustomerId { get; set; }
public Organization Customer { get; set; }
public List<EntityHistory> EntityHistory;
}
DefaultConnection.cs :
public class DefaultConnection : DefaultStorageContext
{
public DbSet<AspNetUser> AspNetUsers { get; set; }
public DbSet<Entity> Entity{ get; set; }
public DbSet<Contractor> Contractors { get; set; }
public DbSet<AgreementReason> AgreementReasons { get; set; }
public DbSet<StateOfAgreement> StateOfAgreements { get; set; }
public DbSet<AgreementStateFlow> AgreementStateFlow { get; set; }
public DbSet<EntityHistory> EntityHistory { get; set; }
public DbSet<Organization> Organizations { get; set; }
...
public DefaultConnection()
{
Database.SetInitializer<DefaultConnection>(new DropCreateDatabaseIfModelChanges<DefaultConnection>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<OneToOneConstraintIntroductionConvention>();
modelBuilder.Entity<EntityHistory>().Map(
m => {
m.MapInheritedProperties();
m.ToTable("EntityHistory");
});
base.OnModelCreating(modelBuilder);
}
}
неправильно сгенеренная миграция:
public partial class Create_EntityHistory : DbMigration
{
public override void Up()
{
DropForeignKey("dbo.AgreementManagedObjects", "AgreementId", "dbo.Entity");
DropForeignKey("dbo.AgreementStateFlows", "AgreementId", "dbo.Entity");
DropForeignKey("dbo.ExportAgreements", "Agreement_Id", "dbo.Entity");
DropIndex("dbo.AgreementManagedObjects", new[] { "EntityId" });
DropIndex("dbo.AgreementStateFlows", new[] { "EntityId" });
DropIndex("dbo.ExportAgreements", new[] { "Entity_Id" });
DropPrimaryKey("dbo.Entity");
CreateTable(
"dbo.EntityHistory",
c => new
{
Id = c.Int(nullable: false),
Number = c.String(nullable: false),
...
DateModification = c.DateTimeOffset(nullable: false, precision: 7),
IdEntity = c.Int(nullable: false),
IdOperation = c.Int(),
IdUser = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Organizations", t => t.ContractorId)
.ForeignKey("dbo.Organizations", t => t.CustomerId)
.ForeignKey("dbo.AspNetUsers", t => t.IdUser)
.Index(t => t.ContractorId)
.Index(t => t.CustomerId)
.Index(t => t.IdUser);
AlterColumn("dbo.Entity", "Id", c => c.Int(nullable: false));
AddPrimaryKey("dbo.Entity", "Id");
}
}
}