使用Entity Framework Core进行审计跟踪

时间:2018-02-14 14:28:31

标签: sql-server asp.net-core entity-framework-core audit audit.net

我在SQL Server数据库上使用Entity Framework核心的ASP.NET核心2.0。

我必须跟踪和审核用户对数据所做的所有事情。我的目标是建立一个自动机制来编写所有正在发生的事情。

例如,如果我有动物表,我想要一个并行表“Audit_animals”,您可以在其中找到有关数据的所有信息,操作类型(添加,删除,编辑)以及制作此内容的用户。 / p>

我这次刚刚在Django + MySQL中做过,但现在环境不同了。我找到this并且看起来很有趣,但我想知道是否有更好的方法,哪种方法是在EF Core中做到这一点。

更新

我正在尝试this并且发生了一些事情,但我遇到了一些问题。

我补充说:

  1. services.AddMvc().AddJsonOptions(options => {
    
                options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            }); 
    
  2. public Mydb_Context(DbContextOptions<isMultiPayOnLine_Context> options) : base(options)
    {
        Audit.EntityFramework.Configuration.Setup()
            .ForContext<Mydb_Context>(config => config
                .IncludeEntityObjects()
                .AuditEventType("Mydb_Context:Mydb"))
            .UseOptOut()
    }
    
  3. public MyRepository(Mydb_Context context)
    {
        _context = context;
        _context.AddAuditCustomField("UserName", "pippo");
    
    }
    
  4. 我还创建了一个用于插入审核的表(只有一个用于测试此工具),但我唯一得到的就是您在图像中看到的内容。带有我创建的数据的json文件列表....为什么??

    enter image description here

3 个答案:

答案 0 :(得分:4)

如果要将审计表(Audit_Animals)映射到与审计的Animals表相同的EF上下文,则可以使用同一...库中包含的 EntityFramework数据提供程序

查看文档here

  

实体框架数据提供程序

     

如果您打算将审核日志存储在中   与审计实体相同的数据库,您可以使用   Audit.EntityFramework。如果您计划存储审核,请使用此选项   具有相似结构的表中每个实体类型的跟踪。

还有另一个可以类似方式审核EF上下文的库,请看一下:zzzprojects/EntityFramework-Plus

不能推荐一个,因为它们提供不同的功能(我是audit.net库的所有者)。

答案 1 :(得分:2)

阅读documentation

  

事件输出

     

要配置输出持久性机制,请参阅ConfigurationData Providers部分。

然后,在Configuration的文档中:

  

如果您未指定数据提供者,将使用默认FileDataProvider 事件作为.json文件 写入当前工作目录。 (强调我的)

长短,请按照文档配置您要使用的数据提供程序。

答案 2 :(得分:0)

如果使用SQL Server 2016 <或Azure SQL,则可以查看时间表(系统版本的时间表)。

https://docs.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables?view=sql-server-ver15

摘自文档:

数据库功能,为提供以下功能提供内置支持 有关在任何时间点存储在表中的数据的信息 不仅限于当前时间正确的数据。 时间性是ANSI SQL 2011中引入的数据库功能。

当前存在一个开放的问题来支持此操作:

https://github.com/dotnet/efcore/issues/4693

今天有第三方选项可用,但是由于它们不是来自Microsoft的,因此当然存在将来版本将不支持它们的风险。

https://github.com/Adam-Langley/efcore-temporal-query

https://github.com/findulov/EntityFrameworkCore.TemporalTables

我这样解决了:

如果使用随附的Visual Studio 2019 LocalDB(Microsoft SQL Server 2016 (13.1.4001.0 LocalDB),则需要使用cascading DELETEUPDATE进行升级。这是因为该版本不支持具有级联操作的Temporal tables

完整的升级指南:

https://stackoverflow.com/a/64210519/3850405

首先添加一个新的空迁移。我更喜欢使用Package Manager Console(PMC):

Add-Migration "Temporal tables"

应如下所示:

public partial class Temporaltables : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {

    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {

    }
}

然后像这样编辑迁移:

public partial class Temporaltables : Migration
{
    List<string> tablesToUpdate = new List<string>
        {
           "Images",
           "Languages",
           "Questions",
           "Texts",
           "Medias",
        };

    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql($"CREATE SCHEMA History");
        foreach (var table in tablesToUpdate)
        {
            string alterStatement = $@"ALTER TABLE [{table}] ADD SysStartTime datetime2(0) GENERATED ALWAYS AS ROW START HIDDEN
     CONSTRAINT DF_{table}_SysStart DEFAULT GETDATE(), SysEndTime datetime2(0) GENERATED ALWAYS AS ROW END HIDDEN
     CONSTRAINT DF_{table}_SysEnd DEFAULT CONVERT(datetime2 (0), '9999-12-31 23:59:59'),
     PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)";
            migrationBuilder.Sql(alterStatement);
            alterStatement = $@"ALTER TABLE [{table}] SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = History.[{table}]));";
            migrationBuilder.Sql(alterStatement);
        }
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        foreach (var table in tablesToUpdate)
        {
            string alterStatement = $@"ALTER TABLE [{table}] SET (SYSTEM_VERSIONING = OFF);";
            migrationBuilder.Sql(alterStatement);
            alterStatement = $@"ALTER TABLE [{table}] DROP PERIOD FOR SYSTEM_TIME";
            migrationBuilder.Sql(alterStatement);
            alterStatement = $@"ALTER TABLE [{table}] DROP DF_{table}_SysStart, DF_{table}_SysEnd";
            migrationBuilder.Sql(alterStatement);
            alterStatement = $@"ALTER TABLE [{table}] DROP COLUMN SysStartTime, COLUMN SysEndTime";
            migrationBuilder.Sql(alterStatement);
            alterStatement = $@"DROP TABLE History.[{table}]";
            migrationBuilder.Sql(alterStatement);
        }
        migrationBuilder.Sql($"DROP SCHEMA History");
    }
}

tablesToUpdate应该包含您需要历史记录的每个表。

然后运行Update-Database命令。

原始来源,使用转义表和方括号等进行了一些修改:

https://intellitect.com/updating-sql-database-use-temporal-tables-entity-framework-migration/

测试CreateUpdateDelete将显示完整的历史记录。

[HttpGet]
public async Task<ActionResult<string>> Test()
{
    var identifier1 = "OATestar123";

    var identifier2 = "OATestar12345";

    var newQuestion = new Question()
    {
        Identifier = identifier1
    };
    _dbContext.Questions.Add(newQuestion);
    await _dbContext.SaveChangesAsync();

    var question = await _dbContext.Questions.FirstOrDefaultAsync(x => x.Identifier == identifier1);
    question.Identifier = identifier2;
    await _dbContext.SaveChangesAsync();

    question = await _dbContext.Questions.FirstOrDefaultAsync(x => x.Identifier == identifier2);
    _dbContext.Entry(question).State = EntityState.Deleted;
    await _dbContext.SaveChangesAsync();

    return Ok();
}

测试了几次,但是日志看起来像这样:

enter image description here

此解决方案具有IMAO的巨大优势,因为它不是特定于对象关系映射器(ORM)的,并且如果您编写纯SQL,甚至可以获得历史记录。

enter image description here

默认情况下,“历史记录”表也是只读的,因此减少了审计跟踪的机会。收到错误:Cannot update rows in a temporal history table ''

enter image description here

如果您需要访问数据,则可以使用首选的ORM来获取数据或通过SQL进行审核。