161 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			5.3 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.UtcNow.AddHours(8);
 | 
						|
            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.UtcNow.AddHours(8);
 | 
						|
 | 
						|
            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.UtcNow.AddHours(8);
 | 
						|
            _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.UtcNow.AddHours(8);
 | 
						|
            _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.UtcNow.AddHours(8) };
 | 
						|
 | 
						|
        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
 | 
						|
 | 
						|
} |