簡體   English   中英

如何將代碼優先與 EntityFramework-Plus 的審計功能結合使用?

[英]How to use code-first with EntityFramework-Plus' audit feature?

我正在嘗試設置EntityFramework Plus 的 Audit Auto-Save 功能,但看起來我遇到了一些非常愚蠢的問題。 我正在遵循“通過覆蓋 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 List<AuditEntryProperty> Properties { get; set; } public List<AuditEntryProperty> Properties { get; set; }public AuditEntry Parent { get; set; } public AuditEntry Parent { 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 所以我只是回滾到之前的 state,其中框架具有“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, nullable: true),
                EntityTypeName = table.Column<string>(maxLength: 255, nullable: true),
                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", "1, 1"),
                AuditEntryID = table.Column<int>(nullable: false),
                PropertyName = table.Column<string>(maxLength: 255, nullable: true),
                RelationName = table.Column<string>(maxLength: 255, nullable: true),
                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);
    }

由於這些屬性不是虛擬的,因此仍然無法執行遷移。

我們在EF Plus 問題跟蹤器上創建了一個問題

您會在這里找到一個可以嘗試的項目,我建議您繼續在我們的問題跟蹤器上進行討論,因為 Stack Overflow 不是此類問題的平台。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM