EI-Image-Viewer-Api/IRaCIS.Core.Infra.EFCore/Interceptor/AuditInterceptor.cs

161 lines
5.2 KiB
C#

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infra.EFCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
public class AuditingInterceptor : ISaveChangesInterceptor
{
private readonly string _connectionString;
private SaveChangesAudit _audit;
public AuditingInterceptor(string connectionString)
{
_connectionString = connectionString;
}
#region SavingChanges
public async ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData,
InterceptionResult<int> result,
CancellationToken cancellationToken = default)
{
_audit = CreateAudit(eventData.Context);
await Task.CompletedTask;
return result;
}
public InterceptionResult<int> SavingChanges(
DbContextEventData eventData,
InterceptionResult<int> result)
{
_audit = CreateAudit(eventData.Context);
return result;
}
#endregion
#region SavedChanges
public int SavedChanges(SaveChangesCompletedEventData eventData, int result)
{
if (_audit.Entities.Count > 0)
{
var auditContext = eventData.Context as IRaCISDBContext;
_audit.Succeeded = true;
_audit.EndTime = DateTime.Now;
auditContext.SaveChangesAudits.Add(_audit);
auditContext.SaveChanges();
}
return result;
}
public async ValueTask<int> SavedChangesAsync(
SaveChangesCompletedEventData eventData,
int result,
CancellationToken cancellationToken = default)
{
if (_audit.Entities.Count > 0)
{
var auditContext = eventData.Context as IRaCISDBContext;
_audit.Succeeded = true;
auditContext.SaveChangesAudits.Add(_audit);
_audit.EndTime = DateTime.Now;
await auditContext.SaveChangesAsync();
}
return result;
}
#endregion
#region SaveChangesFailed
public void SaveChangesFailed(DbContextErrorEventData eventData)
{
using (var auditContext = new AuditContext(_connectionString))
{
auditContext.Attach(_audit);
_audit.Succeeded = false;
_audit.EndTime = DateTime.Now;
_audit.ErrorMessage = eventData.Exception.Message;
auditContext.SaveChanges();
}
}
public async Task SaveChangesFailedAsync(
DbContextErrorEventData eventData,
CancellationToken cancellationToken = default)
{
using (var auditContext = new AuditContext(_connectionString))
{
auditContext.Attach(_audit);
_audit.Succeeded = false;
_audit.EndTime = DateTime.Now;
_audit.ErrorMessage = eventData.Exception.InnerException?.Message;
await auditContext.SaveChangesAsync(cancellationToken);
}
}
#endregion
#region CreateAudit
private static bool NeedAudit(EntityEntry entityEntry)
{
var type = entityEntry.Entity.GetType();
return type != typeof(EntityAudit) && type != typeof(SaveChangesAudit) && type != typeof(DicomSeries) && type != typeof(DicomInstance) && type != typeof(TrialAudit);
}
private static SaveChangesAudit CreateAudit(DbContext context)
{
context.ChangeTracker.DetectChanges();
var audit = new SaveChangesAudit { StartTime = DateTime.Now };
foreach (var entry in context.ChangeTracker.Entries().Where(t => NeedAudit(t)))
{
var auditMessage = entry.State switch
{
EntityState.Deleted => CreateDeletedMessage(entry),
EntityState.Modified => CreateModifiedMessage(entry),
EntityState.Added => CreateAddedMessage(entry),
_ => null
};
if (auditMessage != null)
{
var alterIdStr = entry.CurrentValues["Id"].ToString();
audit.Entities.Add(new EntityAudit { State = entry.State, AuditMessage = auditMessage, AlterId = Guid.Parse(alterIdStr) });
}
}
return audit;
string CreateAddedMessage(EntityEntry entry)
=>
entry.Properties.Aggregate(
$"Inserting {entry.Metadata.DisplayName()} with ",
(auditString, property) => auditString + $"{property.Metadata.Name}: '{property.CurrentValue}' ");
string CreateModifiedMessage(EntityEntry entry)
=> entry.Properties.Where(property => property.IsModified || property.Metadata.IsPrimaryKey()).Aggregate(
$"Updating {entry.Metadata.DisplayName()} with ",
(auditString, property) => auditString + $"{property.Metadata.Name}: '{property.CurrentValue}' ");
string CreateDeletedMessage(EntityEntry entry)
=> entry.Properties.Where(property => property.Metadata.IsPrimaryKey()).Aggregate(
$"Deleting {entry.Metadata.DisplayName()} with ",
(auditString, property) => auditString + $"{property.Metadata.Name}: '{property.CurrentValue}' ");
}
#endregion
}