如何将代码优先与EntityFramework-Plus的审核功能结合使用?

我正在尝试设置EntityFramework Plus' Audit Auto-Save feature,但似乎我陷入了一个非常愚蠢的问题。我正在遵循“通过覆盖SaveChanges和SaveChangesAsync自动保存”路径,但是我尝试使用代码优先,因为我要使用的项目已经运行了一段时间了。话虽如此,我的DbContext看起来像这样:

public class CadastralDbContext : DbContext
{
    public CadastralDbContext(DbContextOptions<CadastralDbContext> options) : base(options) { }

    static CadastralDbContext()
    {
        AuditManager.DefaultConfiguration.AutoSavePreaction = (context,audit) =>
           (context as CadastralDbContext).AuditEntries.AddRange(audit.Entries);
    }

    public DbSet<AuditEntry> AuditEntries { get; set; }

    public DbSet<AuditEntryProperty> AuditEntryProperties { get; set; }

    //Ommited my DbSets

    protected override void OnmodelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(CadastralDbContext).Assembly);
        /*** Ignore these for now ***/
        //modelBuilder.Entity<AuditEntry>().Ignore(x => x.Properties);
        //modelBuilder.Entity<AuditEntryProperty>().Ignore(x => x.Parent);
    }

    public override int SaveChanges()
    {
        var audit = new Audit();
        audit.PreSaveChanges(this);
        var rowAffecteds = base.SaveChanges();
        audit.PostSaveChanges();

        if (audit.Configuration.AutoSavePreaction != null)
        {
            audit.Configuration.AutoSavePreaction(this,audit);
            base.SaveChanges();
        }

        return rowAffecteds;
    }

    public async Task<int> SaveChangesAsync()
    {
        return await SaveChangesAsync(CancellationToken.None);
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
    {
        var audit = new Audit();
        audit.PreSaveChanges(this);
        var rowAffecteds = await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        audit.PostSaveChanges();

        if (audit.Configuration.AutoSavePreaction != null)
        {
            audit.Configuration.AutoSavePreaction(this,audit);
            await base.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        }

        return rowAffecteds;
    }
}

}

基本上,本教程所说的内容添加了DbSet<AuditEntry>DbSet<AuditEntryProperty>,它们是框架本身的类。检查这些的元数据,我们有:

//
// Summary:
//     An audit entry.
public class AuditEntry
{
    //
    // Summary:
    //     Gets or sets the object state entry.
    [NotMapped]
    public object Entity;
    //
    // Summary:
    //     Gets or sets the object state entry.
    [NotMapped]
    public EntityEntry Entry;
    //
    // Summary:
    //     Gets or sets the parent.
    public Audit Parent;

    public AuditEntry();

    //
    // Summary:
    //     Gets or sets the identifier of the audit entry.
    [Column(Order = 0)]
    public int AuditEntryID { get; set; }
    //
    // Summary:
    //     Gets or sets who created this object.
    [Column(Order = 5)]
    [MaxLength(255)]
    public string CreatedBy { get; set; }
    //
    // Summary:
    //     Gets or sets the the date of the changes.
    [Column(Order = 6)]
    public DateTime CreatedDate { get; set; }
    //
    // Summary:
    //     Gets or sets the name of the entity set.
    [Column(Order = 1)]
    [MaxLength(255)]
    public string entitysetName { get; set; }
    //
    // Summary:
    //     Gets or sets the name of the entity type.
    [Column(Order = 2)]
    [MaxLength(255)]
    public string EntityTypeName { get; set; }
    //
    // Summary:
    //     Gets or sets the properties.
    public List<AuditEntryProperty> Properties { get; set; }
    //
    // Summary:
    //     Gets or sets the entry state.
    [Column(Order = 3)]
    public AuditEntryState State { get; set; }
    //
    // Summary:
    //     Gets or sets the name of the entry state.
    [Column(Order = 4)]
    [MaxLength(255)]
    public string StateName { get; set; }
}

//
// Summary:
//     An audit entry property.
public class AuditEntryProperty
{
    //
    // Summary:
    //     Gets or sets the new value audited.
    [NotMapped]
    public PropertyEntry PropertyEntry;
    public object NewValue;
    public object OldValue;

    public AuditEntryProperty();

    //
    // Summary:
    //     Gets or sets the name of the property internally.
    [NotMapped]
    public string InternalPropertyName { get; set; }
    //
    // Summary:
    //     Gets or sets a value indicating whether OldValue and NewValue is set.
    [NotMapped]
    public bool IsValueSet { get; set; }
    //
    // Summary:
    //     Gets or sets the name of the relation audited.
    [Column(Order = 2)]
    [MaxLength(255)]
    public string RelationName { get; set; }
    //
    // Summary:
    //     Gets or sets the name of the property audited.
    [Column(Order = 3)]
    [MaxLength(255)]
    public string PropertyName { get; set; }
    //
    // Summary:
    //     Gets or sets the parent.
    public AuditEntry Parent { get; set; }
    //
    // Summary:
    //     Gets or sets the identifier of the audit entry property.
    [Column(Order = 0)]
    public int AuditEntryPropertyID { get; set; }
    //
    // Summary:
    //     Gets or sets the new value audited formatted.
    [Column("NewValue",Order = 5)]
    public string NewValueFormatted { get; set; }
    //
    // Summary:
    //     Gets or sets the identifier of the audit entry.
    [Column(Order = 1)]
    public int AuditEntryID { get; set; }
    //
    // Summary:
    //     Gets or sets the old value audited formatted.
    [Column("OldValue",Order = 4)]
    public string OldValueFormatted { get; set; }
}

除了两个属性:public List<AuditEntryProperty> Properties { get; set; }public AuditEntry Parent { get; set; }之外,它看起来已经足够好了。由于未将其标记为virtual,因此添加迁移将失败。我尝试了一种变通方法,只是看是否可以获取它来生成表,而我确实成功了(前面提到的那些行):

    protected override void OnmodelCreating(ModelBuilder modelBuilder)
    {
        //...
        modelBuilder.Entity<AuditEntry>().Ignore(x => x.Properties);
        modelBuilder.Entity<AuditEntryProperty>().Ignore(x => x.Parent);
    }

这似乎禁用了两个表都具有的PrimaryKey-ForeignKey关系,这些关系是在框架本身内部设置的,因为没有迹象表明我应该手动进行操作。我什至试图运行该脚本只是为了看看会发生什么,结果是灾难性的:

CREATE INDEX [IX_AuditEntryID] ON [dbo].[AuditEntryProperties]([AuditEntryID])

GO

ALTER TABLE [dbo].[AuditEntryProperties] 
ADD CONSTRAINT [FK_dbo.AuditEntryProperties_dbo.AuditEntries_AuditEntryID] 
FOREIGN KEY ([AuditEntryID])
REFERENCES [dbo].[AuditEntries] ([AuditEntryID])
ON DELETE CASCADE

GO

这使我在插入时遇到以下SQL错误:String or binary data would be truncated。因此,我只是回滚到以前的状态,在该状态下框架具有“ 50%的输出”,因为每当用户请求插入,更新或删除操作时,它都会将记录保存到AuditEntry表(其中包含表等数据)没有任何东西会保留在AuditEntryProperties中(新值,旧值,列),除了这些属性被忽略是所有这些的原因之外,我无法想到其他任何东西。

我以为我可以覆盖AuditEntry和AuditEntryProperties,但这听起来像是一个很大的愚蠢的解决方法。我不是数据库专家,我在这里想念什么?

编辑:忘记添加迁移代码:

        migrationBuilder.Createtable(
            name: "AuditEntries",columns: table => new
            {
                AuditEntryID = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:Identity","1,1"),CreatedBy = table.Column<string>(maxLength: 255,nullable: true),CreatedDate = table.Column<DateTime>(nullable: false),entitysetName = table.Column<string>(maxLength: 255,EntityTypeName = table.Column<string>(maxLength: 255,State = table.Column<int>(nullable: false),StateName = table.Column<string>(maxLength: 255,nullable: true)
            },constraints: table =>
            {
                table.PrimaryKey("PK_AuditEntries",x => x.AuditEntryID);
            });

        migrationBuilder.Createtable(
            name: "AuditEntryProperties",columns: table => new
            {
                AuditEntryPropertyID = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:Identity",AuditEntryID = table.Column<int>(nullable: false),PropertyName = table.Column<string>(maxLength: 255,RelationName = table.Column<string>(maxLength: 255,NewValue = table.Column<string>(nullable: true),OldValue = table.Column<string>(nullable: true)
            },constraints: table =>
            {
                table.PrimaryKey("PK_AuditEntryProperties",x => x.AuditEntryPropertyID);
            });

编辑2 试图通过Fluent API添加FK:

    protected override void OnmodelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(CadastralDbContext).Assembly);
        modelBuilder.Entity<AuditEntryProperty>().HasOne<AuditEntry>(prop => prop.Parent).WithMany(a => a.Properties).HasForeignKey(prop => prop.AuditEntryID);
    }

由于这些属性不是虚拟的,因此仍然无法执行迁移。

huomiao1122 回答:如何将代码优先与EntityFramework-Plus的审核功能结合使用?

我们在EF Plus Issues Tracker

上创建了一个问题

您会在这里找到一个可以尝试的项目,我建议您继续在我们的问题跟踪器上进行讨论,因为Stack Overflow并不是解决此类问题的平台。

本文链接:https://www.f2er.com/3159505.html

大家都在问