Search code examples
c#entity-framework-coreaudit.net

Audit.NET Entity framework data adapter, object properties not logged


i setup Audit.net for my model and all works fine but nested properties are ignored and got 0 in corresponding log table fields

model

public class ElementoWorkflow : BaseObject<ElementoWorkflow, int>
{
    public override int Id { get;  set; }
    public required Stato StatoOrigine { get;  set; }
    public required bool Finale { get; set; } = false;
}

public class Stato : ServiceRegistryObject
{
    public override RegistryType Gruppo {
        get => RegistryType.Stato;
        set => throw new InvalidOperationException("Non è possibile modificare il Gruppo di appartenenza. Utilizzare la classe generica ServiceRegistryObject.");
    }
}

public class ServiceRegistryObject : ServiceBaseObject<ServiceRegistryObject, int>
{
    public override int Id { get; set; }
    public virtual string? Codice { get; set; }
    public virtual RegistryType Gruppo { get; set; }
}

public abstract class ServiceBaseObject<TEntity, TKey> : BaseObject<TEntity, TKey>
where TEntity : class
{
    public abstract bool Annullato { get; set; }

    public abstract TKey Ordine { get; set; }
}

public void Configure(EntityTypeBuilder<ElementoWorkflow> builder)
    {
        builder
            .ToTable($"NAT_DASH_ELEMENTO_WORKFLOW_007")
            .HasKey(c => c.Id)
            .HasName("NAK_DASH_007");

        builder.Property(b => b.Id)
            .HasColumnName("ID_007");
        builder.Property<int>("IdStatoOrigine")
            .HasColumnName("ID_STATO_ORIGINE_007")
            .IsRequired();
        builder.HasOne(c => c.StatoOrigine)
            .WithMany()
            .HasForeignKey("IdStatoOrigine")
            .HasPrincipalKey(g => g.Id)
            .IsRequired();      
        builder.Property(b => b.Finale)
            .HasColumnName("FINALE_007")
            .IsRequired();
    }

below my config

public DatabaseContext(DbContextOptions<DatabaseContext> options, ILoggerFactory loggerFactory, IHttpContextAccessor httpContextAccessor)
 : base(options)
{
    _loggerFactory = loggerFactory;
    //this.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
    this.ChangeTracker.LazyLoadingEnabled = false;

    Audit.EntityFramework.Configuration.Setup()
        .ForContext<DatabaseContext>(config => config
            .IncludeEntityObjects()     
            .ReloadDatabaseValues(true)
            .AuditEventType("DatabaseContext"))
        
        .UseOptOut()
            .IgnoreAny(t => t.Name.EndsWith("History"));


    Audit.Core.Configuration.Setup()

        .UseEntityFramework(ef => ef
        .AuditTypeExplicitMapper(m => m
        .Map<ElementoWorkflow, ElementoWorkflowAudit>()
        .AuditEntityAction<IAudit>((evt, entry, auditEntity) =>
        {
            auditEntity.AuditData = DateTime.UtcNow;
            auditEntity.AuditUser = httpContextAccessor.HttpContext?.User.Claims.FirstOrDefault(c => c.Type.Contains(TipoClaim.matricola.ToString()))?.Value;
            auditEntity.AuditAction = entry.Action;
        })
    )
);

and audit objects

internal class ElementoWorkflowAudit : IAudit
{
    public int AuditId { get; set; }
    public string AuditAction { get; set; }
    public string? AuditUser { get; set; }
    public DateTime AuditData { get; set; }
    public int Id { get; set; }
    public Stato StatoOrigine { get; set; }
    public bool Finale { get; set; } = false;
}

public void Configure(EntityTypeBuilder<ElementoWorkflowAudit> builder)
{
    builder
         .ToTable("NAT_DASH_AUD_ELEMENTO_WORKFLOW_007")
        .HasKey(e => e.AuditId)
        .HasName("NAK_DASH_AUD_ELEMENTO_WORKFLOW_007");
    builder.Property(b => b.AuditId).HasColumnName("AuditId")
        .HasDefaultValueSql($"SYUNA{Costanti.CodiceApplicazione}.SEQ_{Costanti.CodiceApplicazione}__AUD.NEXTVAL");
    builder.Property(b => b.AuditData);
    builder.Property(b => b.AuditAction);
    builder.Property(b => b.AuditUser);

    builder.Property(b => b.Id)
        .HasColumnName("ID_007");

    builder.Property<int>("IdStatoOrigine")
        .HasColumnName("ID_STATO_ORIGINE_007");

    builder.HasOne(c => c.StatoOrigine)
        .WithMany()
        .HasForeignKey("IdStatoOrigine")
        .HasPrincipalKey(g => g.Id);

    builder.Property(b => b.Finale)
        .HasColumnName("FINALE_007");
}

and oracle db table

CREATE TABLE NAT_DASH_AUD_ELEMENTO_WORKFLOW_007 
(
  "AuditId" NUMBER(9, 0) DEFAULT "SYUNADASH"."SEQ_DASH__AUD"."NEXTVAL" NOT NULL 
, "AuditAction" VARCHAR2(250 BYTE) 
, "AuditUser" VARCHAR2(250 BYTE) 
, "AuditData" DATE 
, ID_007 NUMBER(12, 0) 
, ID_STATO_ORIGINE_007 NUMBER(9, 0) 
, FINALE_007 NUMBER(1, 0) 
, CONSTRAINT NAK_DASH_AUD_ELEMENTO_WORKFLOW_007 PRIMARY KEY 
  (
    "AuditId" 
  )
)

where ID_STATO_ORIGINE_007 is always null


Solution

  • The navigation properties are not included as changes for the parent entity unless they are owned entities.

    So, EF will detect and include the change in the foreign key properties, and when the child entity is modified, it will include the change for that entity type.

    You could share a working sample that reproduces the issue and open an issue here for better support.