我在SQL Server数据库上使用Entity Framework核心的ASP.NET核心2.0。
我必须跟踪和审核用户对数据所做的所有事情。我的目标是建立一个自动机制来编写所有正在发生的事情。
例如,如果我有动物表,我想要一个并行表“Audit_animals”,您可以在其中找到有关数据的所有信息,操作类型(添加,删除,编辑)以及制作此内容的用户。 / p>
我这次刚刚在Django + MySQL中做过,但现在环境不同了。我找到this并且看起来很有趣,但我想知道是否有更好的方法,哪种方法是在EF Core中做到这一点。
更新
我正在尝试this并且发生了一些事情,但我遇到了一些问题。
我补充说:
services.AddMvc().AddJsonOptions(options => {
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
public Mydb_Context(DbContextOptions<isMultiPayOnLine_Context> options) : base(options)
{
Audit.EntityFramework.Configuration.Setup()
.ForContext<Mydb_Context>(config => config
.IncludeEntityObjects()
.AuditEventType("Mydb_Context:Mydb"))
.UseOptOut()
}
public MyRepository(Mydb_Context context)
{
_context = context;
_context.AddAuditCustomField("UserName", "pippo");
}
我还创建了一个用于插入审核的表(只有一个用于测试此工具),但我唯一得到的就是您在图像中看到的内容。带有我创建的数据的json文件列表....为什么??
答案 0 :(得分:4)
如果要将审计表(Audit_Animals)映射到与审计的Animals表相同的EF上下文,则可以使用同一...
库中包含的 EntityFramework数据提供程序 。
查看文档here:
实体框架数据提供程序
如果您打算将审核日志存储在中 与审计实体相同的数据库,您可以使用
Audit.EntityFramework
。如果您计划存储审核,请使用此选项 具有相似结构的表中每个实体类型的跟踪。
还有另一个可以类似方式审核EF上下文的库,请看一下:zzzprojects/EntityFramework-Plus。
不能推荐一个,因为它们提供不同的功能(我是audit.net库的所有者)。
答案 1 :(得分:2)
事件输出
要配置输出持久性机制,请参阅Configuration和Data Providers部分。
然后,在Configuration的文档中:
如果您未指定数据提供者,将使用默认
FileDataProvider
将 事件作为.json文件 写入当前工作目录。 (强调我的)
长短,请按照文档配置您要使用的数据提供程序。
答案 2 :(得分:0)
如果使用SQL Server 2016
<或Azure SQL
,则可以查看时间表(系统版本的时间表)。
摘自文档:
数据库功能,为提供以下功能提供内置支持 有关在任何时间点存储在表中的数据的信息 不仅限于当前时间正确的数据。 时间性是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 DELETE
或UPDATE
进行升级。这是因为该版本不支持具有级联操作的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/
测试Create
,Update
和Delete
将显示完整的历史记录。
[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();
}
测试了几次,但是日志看起来像这样:
此解决方案具有IMAO的巨大优势,因为它不是特定于对象关系映射器(ORM)的,并且如果您编写纯SQL,甚至可以获得历史记录。
默认情况下,“历史记录”表也是只读的,因此减少了审计跟踪的机会。收到错误:Cannot update rows in a temporal history table ''
如果您需要访问数据,则可以使用首选的ORM来获取数据或通过SQL进行审核。