2

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

gt.guybrush
  • 1,320
  • 3
  • 19
  • 48

2 Answers2

0

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.

thepirat000
  • 12,362
  • 4
  • 46
  • 72
  • navigation property objects exists even without parent, so are not owned entities. but iam not changing navigation object properties, iam changing parent one, i.e changing ElementoWorkflow.StatoIniziale from State1 object to State2 object – gt.guybrush Aug 24 '23 at 08:59
0

to make it works i hade to add explicit navigation property id:

 public int IdStatoOrigine { get;  set; }

and map it removing shadow properties

builder.Property(b => b.IdStatoOrigine)
        .HasColumnName("ID_STATO_ORIGINE_007");
    builder.HasOne(c => c.StatoOrigine)
        .WithMany()
        .HasForeignKey(c => c.IdStatoOrigine)
        .HasPrincipalKey(g => g.Id)
        .IsRequired();  

honestly after years using NHibernate and Evenrs, Entity framework is a great step back in Domain modelling

gt.guybrush
  • 1,320
  • 3
  • 19
  • 48